[New Feature] Apply graph strategy

This commit is contained in:
Qolzam
2018-01-12 13:40:59 +07:00
parent 7231c59f92
commit e38dbc9fd3
169 changed files with 2931 additions and 2580 deletions

View File

@@ -1,9 +1,19 @@
{ {
"hosting": { "hosting": {
"public": "public", "public": "public",
"rewrites": [{ "rewrites": [
"source": "**", {
"destination": "/index.html" "source": "/bundle-v0.4.js",
}] "destination": "/bundle-v0.4.js"
},
{
"source": "/favicon.ico",
"destination": "/favicon.ico"
},
{
"source": "**",
"destination": "/index.html"
}
]
} }
} }

View File

@@ -17,7 +17,7 @@
"@types/react-infinite-scroller": "^1.0.4", "@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.2",
"babel-runtime": "^6.26.0", "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",
@@ -28,7 +28,8 @@
"faker": "^4.1.0", "faker": "^4.1.0",
"file-loader": "^0.11.1", "file-loader": "^0.11.1",
"firebase": "^4.6.2", "firebase": "^4.6.2",
"inversify": "^4.3.0", "install": "^0.10.2",
"inversify": "^4.6.0",
"keycode": "^2.1.9", "keycode": "^2.1.9",
"lodash": "^4.17.4", "lodash": "^4.17.4",
"material-ui": "^0.19.4", "material-ui": "^0.19.4",
@@ -36,6 +37,7 @@
"morgan": "^1.8.1", "morgan": "^1.8.1",
"node-env-file": "^0.1.8", "node-env-file": "^0.1.8",
"node-sass": "^4.5.2", "node-sass": "^4.5.2",
"npm": "^5.6.0",
"prop-types": "^15.6.0", "prop-types": "^15.6.0",
"react": "^16.0.0", "react": "^16.0.0",
"react-addons-test-utils": "^15.6.2", "react-addons-test-utils": "^15.6.2",
@@ -43,7 +45,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-infinite-scroller": "^1.1.2",
"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",

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -122,7 +122,7 @@
</div> </div>
</div> </div>
<script src="/bundle-v0.3.js"></script> <script src="/bundle-v0.5.js"></script>
</body> </body>
</html> </html>

View File

@@ -15,13 +15,16 @@ import { AuthorizeActionType } from 'constants/authorizeActionType'
// - Import services // - Import services
import { IAuthorizeService } from 'core/services/authorize' import { IAuthorizeService } from 'core/services/authorize'
import { IServiceProvider, ServiceProvide } from 'core/factories'
// - Import actions // - Import actions
import * as globalActions from 'actions/globalActions' import * as globalActions from 'actions/globalActions'
import { provider } from '../socialEngine'
import { SocialProviderTypes } from 'core/socialProviderTypes'
const serviceProvider: IServiceProvider = new ServiceProvide() /**
const authorizeService: IAuthorizeService = serviceProvider.createAuthorizeService() * Get service providers
*/
const authorizeService: IAuthorizeService = provider.get<IAuthorizeService>(SocialProviderTypes.AuthorizeService)
/* _____________ CRUD DB _____________ */ /* _____________ CRUD DB _____________ */

View File

@@ -1,6 +1,6 @@
// - Import domain // - Import domain
import { User } from 'core/domain/users' import { User, Profile } from 'core/domain/users'
import { Circle, UserFollower } from 'core/domain/circles' import { Circle, UserTie } from 'core/domain/circles'
import { SocialError } from 'core/domain/common' import { SocialError } from 'core/domain/common'
// - Import utility components // - Import utility components
@@ -15,11 +15,17 @@ import * as postActions from 'actions/postActions'
import * as userActions from 'actions/userActions' import * as userActions from 'actions/userActions'
import * as notifyActions from 'actions/notifyActions' import * as notifyActions from 'actions/notifyActions'
import { IServiceProvider,ServiceProvide } from 'core/factories' import { IServiceProvider, ServiceProvide } from 'core/factories'
import { ICircleService } from 'core/services/circles' import { ICircleService } from 'core/services/circles'
import { SocialProviderTypes } from 'core/socialProviderTypes'
import { provider } from '../socialEngine'
import { IUserTieService } from 'core/services/circles'
const serviceProvider: IServiceProvider = new ServiceProvide() /**
const circleService: ICircleService = serviceProvider.createCircleService() * Get service providers
*/
const circleService: ICircleService = provider.get<ICircleService>(SocialProviderTypes.CircleService)
const userTieService: IUserTieService = provider.get<IUserTieService>(SocialProviderTypes.UserTieService)
/* _____________ CRUD DB _____________ */ /* _____________ CRUD DB _____________ */
@@ -33,10 +39,9 @@ export let dbAddCircle = (circleName: string) => {
let uid: string = getState().authorize.uid let uid: string = getState().authorize.uid
let circle: Circle = { let circle: Circle = {
creationDate: moment().unix(), creationDate: moment().unix(),
name: circleName, name: circleName
users: {}
} }
return circleService.addCircle(uid,circle).then((circleKey: string) => { return circleService.addCircle(uid, circle).then((circleKey: string) => {
circle.id = circleKey circle.id = circleKey
circle.ownerId = uid circle.ownerId = uid
dispatch(addCircle(circle)) dispatch(addCircle(circle))
@@ -47,31 +52,29 @@ export let dbAddCircle = (circleName: string) => {
} }
/** /**
* Add a user in a circle * Update user in circle/circles
* @param {string} cid is circle identifier
* @param {User} userFollowing is the user for following
*/ */
export let dbAddFollowingUser = (cid: string, userFollowing: UserFollower) => { export let dbUpdateUserInCircles = (circleIdList: string[], userFollowing: UserTie) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
const state = getState()
let uid: string = state.authorize.uid
let user: User = { ...state.user.info[uid], userId: uid }
let uid: string = getState().authorize.uid return userTieService.tieUseres(
let user: User = getState().user.info[uid] { userId: user.userId!, fullName: user.fullName, avatar: user.avatar, approved: false },
{ userId: userFollowing.userId!, fullName: userFollowing.fullName, avatar: userFollowing.avatar, approved: false },
let userCircle: User = { circleIdList
creationDate: moment().unix(), )
fullName: userFollowing.fullName,
avatar: userFollowing.avatar || ''
}
let userFollower: UserFollower = {
creationDate: moment().unix(),
fullName: user.fullName,
avatar: user.avatar || '',
approved: false
}
return circleService.addFollowingUser(uid,cid,userCircle,userFollower,userFollowing.userId as string)
.then(() => { .then(() => {
dispatch(addFollowingUser(uid, cid, userFollowing.userId as string, { ...userCircle } as User)) dispatch(addFollowingUser(
new UserTie(
userFollowing.userId!,
moment().unix(),
userFollowing.fullName,
userFollowing.avatar,
false,
circleIdList
)))
dispatch(notifyActions.dbAddNotification( dispatch(notifyActions.dbAddNotification(
{ {
@@ -89,18 +92,16 @@ export let dbAddFollowingUser = (cid: string, userFollowing: UserFollower) => {
} }
/** /**
* Delete a user from a circle * Delete following user
* @param {string} cid is circle identifier
* @param {string} userFollowingId following user identifier
*/ */
export let dbDeleteFollowingUser = (cid: string, userFollowingId: string) => { export let dbDeleteFollowingUser = (userFollowingId: string) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
let uid: string = getState().authorize.uid let uid: string = getState().authorize.uid
return circleService.deleteFollowingUser(uid,cid,userFollowingId) return userTieService.removeUsersTie(uid, userFollowingId)
.then(() => { .then(() => {
dispatch(deleteFollowingUser(uid, cid, userFollowingId)) dispatch(deleteFollowingUser(userFollowingId))
}, (error: SocialError) => { }, (error: SocialError) => {
dispatch(globalActions.showErrorMessage(error.message)) dispatch(globalActions.showErrorMessage(error.message))
}) })
@@ -109,7 +110,6 @@ export let dbDeleteFollowingUser = (cid: string, userFollowingId: string) => {
/** /**
* Update a circle from database * Update a circle from database
* @param {Circle} newCircle
*/ */
export const dbUpdateCircle = (newCircle: Circle) => { export const dbUpdateCircle = (newCircle: Circle) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
@@ -118,14 +118,13 @@ export const dbUpdateCircle = (newCircle: Circle) => {
let uid: string = getState().authorize.uid let uid: string = getState().authorize.uid
// Write the new data simultaneously in the list // Write the new data simultaneously in the list
let circle: Circle = getState().circle.userCircles[uid][newCircle.id!] let circle: Circle = getState().circle.userTies[uid][newCircle.id!]
let updatedCircle: Circle = { let updatedCircle: Circle = {
name: newCircle.name || circle.name, name: newCircle.name || circle.name
users: newCircle.users ? newCircle.users : (circle.users || [])
} }
return circleService.updateCircle(uid,newCircle.id!,circle) return circleService.updateCircle(uid, newCircle.id!, circle)
.then(() => { .then(() => {
dispatch(updateCircle(uid,{ id: newCircle.id, ...updatedCircle })) dispatch(updateCircle({ id: newCircle.id, ...updatedCircle }))
}, (error: SocialError) => { }, (error: SocialError) => {
dispatch(globalActions.showErrorMessage(error.message)) dispatch(globalActions.showErrorMessage(error.message))
}) })
@@ -135,17 +134,16 @@ export const dbUpdateCircle = (newCircle: Circle) => {
/** /**
* Delete a circle from database * Delete a circle from database
* @param {string} id is circle identifier
*/ */
export const dbDeleteCircle = (id: string) => { export const dbDeleteCircle = (circleId: string) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
// Get current user id // Get current user id
let uid: string = getState().authorize.uid let uid: string = getState().authorize.uid
return circleService.deleteCircle(uid,id) return circleService.deleteCircle(uid, circleId)
.then(() => { .then(() => {
dispatch(deleteCircle(uid, id)) dispatch(deleteCircle(circleId))
}, (error: SocialError) => { }, (error: SocialError) => {
dispatch(globalActions.showErrorMessage(error.message)) dispatch(globalActions.showErrorMessage(error.message))
}) })
@@ -154,7 +152,7 @@ export const dbDeleteCircle = (id: string) => {
} }
/** /**
* Get all user circles from data base * Get all circles from data base belong to current user
*/ */
export const dbGetCircles = () => { export const dbGetCircles = () => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
@@ -163,16 +161,7 @@ export const dbGetCircles = () => {
return circleService.getCircles(uid) return circleService.getCircles(uid)
.then((circles: { [circleId: string]: Circle }) => { .then((circles: { [circleId: string]: Circle }) => {
Object.keys(circles).forEach((circleId) => { dispatch(addCircles(circles))
if (circleId !== '-Followers' && circles[circleId].users) {
Object.keys(circles[circleId].users).filter((v, i, a) => a.indexOf(v) === i).forEach((userId) => {
dispatch(postActions.dbGetPostsByUserId(userId))
dispatch(userActions.dbGetUserInfoByUserId(userId, ''))
})
}
})
dispatch(addCircles(uid, circles))
}) })
.catch((error: SocialError) => { .catch((error: SocialError) => {
dispatch(globalActions.showErrorMessage(error.message)) dispatch(globalActions.showErrorMessage(error.message))
@@ -182,6 +171,46 @@ export const dbGetCircles = () => {
} }
} }
/**
* Get all user ties from data base
*/
export const dbGetUserTies = () => {
return (dispatch: any, getState: Function) => {
let uid: string = getState().authorize.uid
if (uid) {
userTieService.getUserTies(uid).then((result) => {
dispatch(userActions.addPeopleInfo(result as any))
dispatch(addUserTies(result))
})
.catch((error: SocialError) => {
dispatch(globalActions.showErrorMessage(error.message))
})
}
}
}
/**
* Get all followers
*/
export const dbGetFollowers = () => {
return (dispatch: any, getState: Function) => {
let uid: string = getState().authorize.uid
if (uid) {
userTieService.getUserTies(uid).then((result) => {
dispatch(userActions.addPeopleInfo(result as any))
dispatch(addUserTies(result))
})
.catch((error: SocialError) => {
dispatch(globalActions.showErrorMessage(error.message))
})
}
}
}
/** /**
* Get all user circles from data base by user id * Get all user circles from data base by user id
* @param uid user identifier * @param uid user identifier
@@ -191,12 +220,12 @@ export const dbGetCirclesByUserId = (uid: string) => {
if (uid) { if (uid) {
return circleService.getCircles(uid) return circleService.getCircles(uid)
.then((circles: { [circleId: string]: Circle }) => { .then((circles: { [circleId: string]: Circle }) => {
dispatch(addCircles(uid, circles)) dispatch(addCircles(circles))
}) })
.catch((error: SocialError) => { .catch((error: SocialError) => {
dispatch(globalActions.showErrorMessage(error.message)) dispatch(globalActions.showErrorMessage(error.message))
}) })
} }
} }
} }
@@ -204,9 +233,7 @@ export const dbGetCirclesByUserId = (uid: string) => {
/* _____________ CRUD State _____________ */ /* _____________ CRUD State _____________ */
/** /**
* Add a normal circle * Add a circle
* @param {string} uid is user identifier
* @param {Circle} circle
*/ */
export const addCircle = (circle: Circle) => { export const addCircle = (circle: Circle) => {
return { return {
@@ -217,37 +244,31 @@ export const addCircle = (circle: Circle) => {
/** /**
* Update a circle * Update a circle
* @param {string} uid is user identifier
* @param {Circle} circle
*/ */
export const updateCircle = (uid: string, circle: Circle) => { export const updateCircle = (circle: Circle) => {
return { return {
type: CircleActionType.UPDATE_CIRCLE, type: CircleActionType.UPDATE_CIRCLE,
payload: { uid, circle } payload: { circle }
} }
} }
/** /**
* Delete a circle * Delete a circle
* @param {string} uid is user identifier
* @param {string} id is circle identifier
*/ */
export const deleteCircle = (uid: string, id: string) => { export const deleteCircle = (circleId: string) => {
return { return {
type: CircleActionType.DELETE_CIRCLE, type: CircleActionType.DELETE_CIRCLE,
payload: { uid, id } payload: { circleId }
} }
} }
/** /**
* Add a list of circle * Add a list of circle
* @param {string} uid
* @param {circleId: string]: Circle} circles
*/ */
export const addCircles = (uid: string, circles: { [circleId: string]: Circle }) => { export const addCircles = (circleList: {[circleId: string]: Circle}) => {
return { return {
type: CircleActionType.ADD_LIST_CIRCLE, type: CircleActionType.ADD_LIST_CIRCLE,
payload: { uid, circles } payload: { circleList }
} }
} }
@@ -262,55 +283,126 @@ export const clearAllCircles = () => {
/** /**
* Open circle settings * Open circle settings
* @param uid user idenifier
* @param id circle identifier
*/ */
export const openCircleSettings = (uid: string, id: string) => { export const openCircleSettings = (circleId: string) => {
return { return {
type: CircleActionType.OPEN_CIRCLE_SETTINGS, type: CircleActionType.OPEN_CIRCLE_SETTINGS,
payload: { uid, id } payload: { circleId }
} }
} }
/** /**
* Close open circle settings * Close open circle settings
* @param uid user identifier
* @param id circle identifier
*/ */
export const closeCircleSettings = (uid: string, id: string) => { export const closeCircleSettings = (circleId: string) => {
return { return {
type: CircleActionType.CLOSE_CIRCLE_SETTINGS, type: CircleActionType.CLOSE_CIRCLE_SETTINGS,
payload: { uid, id } payload: { circleId }
} }
} }
/** /**
* Add following user in a circle * Add following user
* @param {string} uid user identifire who want to follow the following user
* @param {string} cid circle identifier that following user should be added in
* @param {string} followingId following user identifier
* @param {User} userCircle information about following user
*/ */
export const addFollowingUser = (uid: string, cid: string, followingId: string, userCircle: User) => { export const addFollowingUser = (userTie: UserTie) => {
return { return {
type: CircleActionType.ADD_FOLLOWING_USER, type: CircleActionType.ADD_FOLLOWING_USER,
payload: { uid, cid, followingId, userCircle } payload: { userTie }
}
}
/**
* Update the user tie
*/
export const updateUserTie = (userTie: UserTie) => {
return {
type: CircleActionType.UPDATE_USER_TIE,
payload: { userTie }
}
}
/**
* Add user ties
*/
export const addUserTies = (userTies: {[userId: string]: UserTie }) => {
return {
type: CircleActionType.ADD_USER_TIE_LIST,
payload: { userTies }
}
}
/**
* Add users who send tie request for current user
*/
export const addUserTieds = (userTieds: {[userId: string]: UserTie }) => {
return {
type: CircleActionType.ADD_USER_TIED_LIST,
payload: { userTieds }
}
}
/**
* Delete the user from a circle
*/
export const deleteUserFromCircle = (userId: string, circleId: string) => {
return {
type: CircleActionType.DELETE_USER_FROM_CIRCLE,
payload: { userId, circleId }
}
}
/**
* Delete following user
*/
export const deleteFollowingUser = (userId: string) => {
return {
type: CircleActionType.DELETE_FOLLOWING_USER,
payload: { userId }
}
}
/**
* Show the box to select circle
*/
export const showSelectCircleBox = (userId: string) => {
return {
type: CircleActionType.SHOW_SELECT_CIRCLE_BOX,
payload: { userId }
} }
} }
/** /**
* Delete following user from a circle * Hide the box to select circle
* @param {string} uid user identifire who want to follow the following user
* @param {string} cid circle identifier that following user should be added in
* @param {string} followingId following user identifier
*/ */
export const deleteFollowingUser = (uid: string, cid: string, followingId: string) => { export const hideSelectCircleBox = (userId: string) => {
return { return {
type: CircleActionType.DELETE_FOLLOWING_USER, type: CircleActionType.HIDE_SELECT_CIRCLE_BOX,
payload: { uid, cid, followingId } payload: { userId }
} }
} }
/**
* Show loading on following user
*/
export const showFollowingUserLoading = (userId: string) => {
return {
type: CircleActionType.SHOW_FOLLOWING_USER_LOADING,
payload: { userId }
}
}
/**
* Hide loading on following user
*/
export const hideFollowingUserLoading = (userId: string) => {
return {
type: CircleActionType.HIDE_FOLLOWING_USER_LOADING,
payload: { userId }
}
}

View File

@@ -1,5 +1,6 @@
// - Import react components // - Import react components
import moment from 'moment' import moment from 'moment'
import _ from 'lodash'
// - Import domain // - Import domain
import { Comment } from 'core/domain/comments' import { Comment } from 'core/domain/comments'
@@ -14,11 +15,14 @@ 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 * as postActions from 'actions/postActions'
import { IServiceProvider, ServiceProvide } from 'core/factories'
import { ICommentService } from 'core/services/comments' import { ICommentService } from 'core/services/comments'
import { SocialProviderTypes } from 'core/socialProviderTypes'
import { provider } from '../socialEngine'
const serviceProvider: IServiceProvider = new ServiceProvide() /**
const commentService: ICommentService = serviceProvider.createCommentService() * Get service providers
*/
const commentService: ICommentService = provider.get<ICommentService>(SocialProviderTypes.CommentService)
/* _____________ CRUD DB _____________ */ /* _____________ CRUD DB _____________ */
@@ -28,11 +32,12 @@ const commentService: ICommentService = serviceProvider.createCommentService()
* @param {object} newComment user comment * @param {object} newComment user comment
* @param {function} callBack will be fired when server responsed * @param {function} callBack will be fired when server responsed
*/ */
export const dbAddComment = (ownerPostUserId: string | null,newComment: Comment, callBack: Function) => { export const dbAddComment = (ownerPostUserId: string, newComment: Comment, callBack: Function) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
dispatch(globalActions.showTopLoading()) dispatch(globalActions.showTopLoading())
const state = getState()
let uid: string = getState().authorize.uid let uid: string = getState().authorize.uid
let comment: Comment = { let comment: Comment = {
@@ -74,8 +79,10 @@ export const dbAddComment = (ownerPostUserId: string | null,newComment: Comment,
*/ */
export const dbGetComments = (ownerUserId: string, postId: string) => { export const dbGetComments = (ownerUserId: string, postId: string) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
const state = getState()
let uid: string = getState().authorize.uid let uid: string = getState().authorize.uid
if (uid) { if (uid) {
return commentService.getComments(postId, (comments: {[postId: string]: {[commentId: string]: Comment}}) => { return commentService.getComments(postId, (comments: {[postId: string]: {[commentId: string]: Comment}}) => {
/** /**
@@ -89,24 +96,21 @@ export const dbGetComments = (ownerUserId: string, postId: string) => {
return return
} }
if (comments && Object.keys(comments).length > 0) { const desiredComments = comments[postId]
commentsCount = Object.keys(comments).length if (desiredComments && Object.keys(desiredComments).length > 0) {
let sortedObjects = comments as any commentsCount = Object.keys(desiredComments).length
let sortedObjects = desiredComments as any
// Sort posts with creation date // 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)) const commentKeys = Object.keys(sortedObjects)
if (commentKeys.length > 1) {
sortedObjects = _.fromPairs(_.toPairs(sortedObjects)
.sort((a: any, b: any) => parseInt(b[1].creationDate,10) - parseInt(a[1].creationDate,10)).slice(0, 3))
}
post.comments = sortedObjects
post.commentCounter = commentsCount
dispatch(postActions.updatePost(post))
} }
}) })
} }
@@ -119,29 +123,17 @@ export const dbGetComments = (ownerUserId: string, postId: string) => {
* @param {string} postId is the identifier of the post which comment belong to * @param {string} postId is the identifier of the post which comment belong to
* @param {string} text is the text of comment * @param {string} text is the text of comment
*/ */
export const dbUpdateComment = (id: string, postId: string, text: string) => { export const dbUpdateComment = (comment: Comment) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
delete comment.editorStatus
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 commentService.updateComment(comment)
let comment: Comment = getState().comment.postComments[postId][id]
let updatedComment: Comment = {
postId: postId,
score: comment.score,
text: text,
creationDate: comment.creationDate,
userDisplayName: comment.userDisplayName,
userAvatar: comment.userAvatar,
userId: uid
}
return commentService.updateComment(updatedComment)
.then(() => { .then(() => {
dispatch(updateComment( id, postId, text)) dispatch(updateComment( comment))
dispatch(globalActions.hideTopLoading()) dispatch(globalActions.hideTopLoading())
}, (error: SocialError) => { }, (error: SocialError) => {
@@ -168,7 +160,7 @@ export const dbDeleteComment = (id?: string | null, postId?: string) => {
// Get current user id // Get current user id
let uid: string = getState().authorize.uid let uid: string = getState().authorize.uid
return commentService.deleteComment(id!,postId!) return commentService.deleteComment(id!)
.then(() => { .then(() => {
dispatch(deleteComment(id!, postId!)) dispatch(deleteComment(id!, postId!))
dispatch(globalActions.hideTopLoading()) dispatch(globalActions.hideTopLoading())
@@ -202,11 +194,11 @@ export const addComment = (comment: Comment) => {
* @param postId post identefier which comment belong to * @param postId post identefier which comment belong to
* @param text the new text for comment * @param text the new text for comment
*/ */
export const updateComment = ( id: string, postId: string, text: string) => { export const updateComment = ( comment: Comment) => {
return { return {
type: CommentActionType.UPDATE_COMMENT, type: CommentActionType.UPDATE_COMMENT,
payload: {id, postId, text} payload: { comment }
} }
} }

View File

@@ -68,6 +68,23 @@ export const hideMessage = () => {
* @param {string} message * @param {string} message
*/ */
export const showErrorMessage = (message: string) => { export const showErrorMessage = (message: string) => {
const appElement = document.getElementById('app')
const masterElement = document.getElementById('master')
const container = document.createElement('div')
const div = document.createElement('div')
div.innerHTML = message
container.style.position = '100000'
container.style.position = 'fixed'
container.style.backgroundColor = '#32c3e4b8'
container.style.width = '100%'
container.style.height = '100%'
container.style.display = 'flex'
container.style.alignItems = 'center'
container.style.alignItems = 'center'
container.style.flexDirection = 'row'
container.appendChild(div)
appElement!.insertBefore(container, masterElement)
return { return {
type: GlobalActionType.SHOW_ERROR_MESSAGE_GLOBAL, type: GlobalActionType.SHOW_ERROR_MESSAGE_GLOBAL,
payload: message payload: message

View File

@@ -15,13 +15,16 @@ import * as globalActions from 'actions/globalActions'
// - Import app API // - Import app API
import FileAPI from 'api/FileAPI' import FileAPI from 'api/FileAPI'
import { IServiceProvider, ServiceProvide } from 'core/factories'
import { IImageGalleryService } from 'core/services/imageGallery' import { IImageGalleryService } from 'core/services/imageGallery'
import { FileResult } from 'models/files/fileResult' import { FileResult } from 'models/files/fileResult'
import { SocialProviderTypes } from 'core/socialProviderTypes'
import { provider } from '../socialEngine'
const serviceProvider: IServiceProvider = new ServiceProvide() /**
const imageGalleryService: IImageGalleryService = serviceProvider.createImageGalleryService() * Get service providers
const storageService: IStorageService = serviceProvider.createStorageService() */
const imageGalleryService: IImageGalleryService = provider.get<IImageGalleryService>(SocialProviderTypes.ImageGalleryService)
const storageService: IStorageService = provider.get<IStorageService>(SocialProviderTypes.StorageService)
/* _____________ UI Actions _____________ */ /* _____________ UI Actions _____________ */

View File

@@ -12,11 +12,14 @@ import { NotificationActionType } from 'constants/notificationActionType'
import * as globalActions from 'actions/globalActions' import * as globalActions from 'actions/globalActions'
import * as userActions from 'actions/userActions' import * as userActions from 'actions/userActions'
import { IServiceProvider, ServiceProvide } from 'core/factories'
import { INotificationService } from 'core/services/notifications' import { INotificationService } from 'core/services/notifications'
import { SocialProviderTypes } from 'core/socialProviderTypes'
import { provider } from '../socialEngine'
const serviceProvider: IServiceProvider = new ServiceProvide() /**
const notificationService: INotificationService = serviceProvider.createNotificationService() * Get service providers
*/
const notificationService: INotificationService = provider.get<INotificationService>(SocialProviderTypes.NotificationService)
/* _____________ CRUD DB _____________ */ /* _____________ CRUD DB _____________ */

View File

@@ -15,11 +15,14 @@ import { PostActionType } from 'constants/postActionType'
// - Import actions // - Import actions
import * as globalActions from 'actions/globalActions' import * as globalActions from 'actions/globalActions'
import { IServiceProvider, ServiceProvide } from 'core/factories'
import { IPostService } from 'core/services/posts' import { IPostService } from 'core/services/posts'
import { SocialProviderTypes } from 'core/socialProviderTypes'
import { provider } from '../socialEngine'
const serviceProvider: IServiceProvider = new ServiceProvide() /**
const postService: IPostService = serviceProvider.createPostService() * Get service providers
*/
const postService: IPostService = provider.get<IPostService>(SocialProviderTypes.PostService)
/* _____________ CRUD DB _____________ */ /* _____________ CRUD DB _____________ */
@@ -127,7 +130,7 @@ export const dbUpdatePost = (updatedPost: Post, callBack: Function) => {
return postService.updatePost(updatedPost).then(() => { return postService.updatePost(updatedPost).then(() => {
dispatch(updatePost(uid, { ...updatedPost })) dispatch(updatePost(updatedPost))
callBack() callBack()
dispatch(globalActions.hideTopLoading()) dispatch(globalActions.hideTopLoading())
@@ -169,13 +172,88 @@ export const dbDeletePost = (id: string) => {
/** /**
* Get all user posts from data base * Get all user posts from data base
*/ */
export const dbGetPosts = () => { export const dbGetPosts = (page: number = 0, limit: number = 10) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
let uid: string = getState().authorize.uid const state = getState()
if (uid) { const {stream} = state.post
const lastPageRequest = stream.lastPageRequest
const lastPostId = stream.lastPostId
const hasMoreData = stream.hasMoreData
return postService.getPosts(uid).then((posts: { [postId: string]: Post }) => { let uid: string = state.authorize.uid
dispatch(addPosts(uid, posts)) if (uid && lastPageRequest !== page) {
return postService.getPosts(uid, lastPostId, page, limit).then((result) => {
if (!result.posts || !(result.posts.length > 0)) {
return dispatch(notMoreDataStream())
}
// Store last post Id
dispatch(lastPostStream(result.newLastPostId))
let parsedData: { [userId: string]: {[postId: string]: Post} } = {}
result.posts.forEach((post) => {
const postId = Object.keys(post)[0]
const postData = post[postId]
const ownerId = postData.ownerUserId!
parsedData = {
...parsedData,
[ownerId]: {
...parsedData[ownerId],
[postId]: {
...postData
}
}
}
})
dispatch(addPosts(parsedData))
})
.catch((error: SocialError) => {
dispatch(globalActions.showErrorMessage(error.message))
})
}
}
}
/**
* Get all user posts from data base
*/
export const dbGetPostsByUserId = (page: number = 0, limit: number = 10) => {
return (dispatch: any, getState: Function) => {
const state = getState()
const {profile} = state.post
const lastPageRequest = profile.lastPageRequest
const lastPostId = profile.lastPostId
const hasMoreData = profile.hasMoreData
let uid: string = state.authorize.uid
if (uid && lastPageRequest !== page) {
return postService.getPostsByUserId(uid, lastPostId, page, limit).then((result) => {
if (!result.posts || !(result.posts.length > 0)) {
return dispatch(notMoreDataProfile())
}
// Store last post Id
dispatch(lastPostProfile(result.newLastPostId))
let parsedData: { [userId: string]: {[postId: string]: Post} } = {}
result.posts.forEach((post) => {
const postId = Object.keys(post)[0]
const postData = post[postId]
const ownerId = postData.ownerUserId!
parsedData = {
...parsedData,
[ownerId]: {
...parsedData[ownerId],
[postId]: {
...postData
}
}
}
})
dispatch(addPosts(parsedData))
}) })
.catch((error: SocialError) => { .catch((error: SocialError) => {
dispatch(globalActions.showErrorMessage(error.message)) dispatch(globalActions.showErrorMessage(error.message))
@@ -205,22 +283,6 @@ export const dbGetPostById = (uid: string, postId: string) => {
} }
} }
/**
* Get all user posts from data base by user id
* @param uid posts owner identifier
*/
export const dbGetPostsByUserId = (uid: string) => {
return (dispatch: Function, getState: Function) => {
if (uid) {
return postService.getPosts(uid).then((posts: { [postId: string]: Post }) => {
dispatch(addPosts(uid, posts))
})
}
}
}
/* _____________ CRUD State _____________ */ /* _____________ CRUD State _____________ */
/** /**
@@ -240,10 +302,10 @@ export const addPost = (uid: string, post: Post) => {
* @param {string} uid is user identifier * @param {string} uid is user identifier
* @param {Post} post * @param {Post} post
*/ */
export const updatePost = (uid: string, post: Post) => { export const updatePost = (post: Post) => {
return { return {
type: PostActionType.UPDATE_POST, type: PostActionType.UPDATE_POST,
payload: { uid, post } payload: { post }
} }
} }
@@ -264,10 +326,10 @@ export const deletePost = (uid: string, id: string) => {
* @param {string} uid * @param {string} uid
* @param {[object]} posts * @param {[object]} posts
*/ */
export const addPosts = (uid: string, posts: { [postId: string]: Post }) => { export const addPosts = (userPosts: { [userId: string]: {[postId: string]: Post} }) => {
return { return {
type: PostActionType.ADD_LIST_POST, type: PostActionType.ADD_LIST_POST,
payload: { uid, posts } payload: { userPosts }
} }
} }
@@ -282,7 +344,6 @@ export const clearAllData = () => {
/** /**
* Add a post with image * Add a post with image
* @param {object} post
*/ */
export const addImagePost = (uid: string, post: any) => { export const addImagePost = (uid: string, post: any) => {
return { return {
@@ -291,3 +352,88 @@ export const addImagePost = (uid: string, post: any) => {
} }
} }
/**
* Set stream has more data to show
*/
export const hasMoreDataStream = () => {
return {
type: PostActionType.HAS_MORE_DATA_STREAM
}
}
/**
* Set stream has not data any more to show
*/
export const notMoreDataStream = () => {
return {
type: PostActionType.NOT_MORE_DATA_STREAM
}
}
/**
* Set last page request of stream
*/
export const requestPageStream = (page: number) => {
return {
type: PostActionType.REQUEST_PAGE_STREAM,
payload: { page}
}
}
/**
* Set last post identification of stream
*/
export const lastPostStream = (lastPostId: string) => {
return {
type: PostActionType.LAST_POST_STREAM,
payload: { lastPostId}
}
}
/**
* Set profile posts has more data to show
*/
export const hasMoreDataProfile = () => {
return {
type: PostActionType.HAS_MORE_DATA_PROFILE
}
}
/**
* Set profile posts has not data any more to show
*/
export const notMoreDataProfile = () => {
return {
type: PostActionType.NOT_MORE_DATA_PROFILE
}
}
/**
* Set last page request of profile posts
*/
export const requestPageProfile = (page: number) => {
return {
type: PostActionType.REQUEST_PAGE_PROFILE,
payload: { page}
}
}
/**
* Set last post identification of profile posts
*/
export const lastPostProfile = (lastPostId: string) => {
return {
type: PostActionType.LAST_POST_PROFILE,
payload: { lastPostId}
}
}

View File

@@ -0,0 +1,51 @@
import moment from 'moment'
// - Import action types
import { ServerActionType } from 'constants/serverActionType'
// - Import domain
// - Import actions
import * as globalActions from 'actions/globalActions'
import { ServerRequestModel } from 'models/server/serverRequestModel'
import { SocialError } from 'core/domain/common/socialError'
/**
* Add a request
* @param {Request} request
*/
export const sendRequest = (request: ServerRequestModel) => {
return { type: ServerActionType.ADD_REQUEST, payload: request }
}
/**
* delete a request
*/
export const deleteRequest = (requestId: string) => {
return { type: ServerActionType.DELETE_REQUEST, payload: {requestId} }
}
/**
* Update request stattus ti successful
*/
export const okRequest = (requestId: string) => {
return { type: ServerActionType.OK_REQUEST, payload: {requestId} }
}
/**
* Set error request
*/
export const errorRequest = (requestId: string, error: SocialError) => {
return { type: ServerActionType.ERROR_REQUEST, payload: {requestId, error} }
}
/**
* Clear all data
*/
export const clearAllrequests = () => {
return { type: ServerActionType.CLEAR_ALL_DATA_REQUEST }
}

View File

@@ -0,0 +1,5 @@
export enum ServerRequestStatusType {
Sent = 'Sent',
OK = 'OK',
Error = 'Error'
}

View File

@@ -1,4 +1,5 @@
// - Import react components // - Import react components
import { provider } from '../socialEngine'
import _ from 'lodash' import _ from 'lodash'
// - Import domain // - Import domain
import { Profile } from 'core/domain/users' import { Profile } from 'core/domain/users'
@@ -11,11 +12,13 @@ import { UserActionType } from 'constants/userActionType'
import * as globalActions from 'actions/globalActions' import * as globalActions from 'actions/globalActions'
import * as userActions from 'actions/userActions' import * as userActions from 'actions/userActions'
import { IServiceProvider, ServiceProvide } from 'core/factories'
import { IUserService } from 'core/services/users' import { IUserService } from 'core/services/users'
import { SocialProviderTypes } from 'core/socialProviderTypes'
const serviceProvider: IServiceProvider = new ServiceProvide() /**
const userService: IUserService = serviceProvider.createUserService() * Get service providers
*/
const userService: IUserService = provider.get<IUserService>(SocialProviderTypes.UserService)
/* _____________ CRUD DB _____________ */ /* _____________ CRUD DB _____________ */
@@ -93,7 +96,7 @@ export const dbUpdateUserInfo = (newProfile: Profile) => {
let uid: string = getState().authorize.uid let uid: string = getState().authorize.uid
let profile: Profile = getState().user.info[uid] let profile: Profile = getState().user.info[uid]
let updatedProfie: Profile = { let updatedProfile: Profile = {
avatar: newProfile.avatar || profile.avatar || '', avatar: newProfile.avatar || profile.avatar || '',
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 || '',
@@ -101,10 +104,9 @@ export const dbUpdateUserInfo = (newProfile: Profile) => {
tagLine: newProfile.tagLine || profile.tagLine || '', tagLine: newProfile.tagLine || profile.tagLine || '',
creationDate: newProfile.creationDate creationDate: newProfile.creationDate
} }
return userService.updateUserProfile(uid,updatedProfile).then(() => {
return userService.updateUserProfile(uid,updatedProfie).then(() => { dispatch(updateUserInfo(uid, updatedProfile))
dispatch(updateUserInfo(uid, updatedProfie))
dispatch(closeEditProfile()) dispatch(closeEditProfile())
}) })
.catch((error: SocialError) => dispatch(globalActions.showErrorMessage(error.message))) .catch((error: SocialError) => dispatch(globalActions.showErrorMessage(error.message)))
@@ -114,17 +116,40 @@ export const dbUpdateUserInfo = (newProfile: Profile) => {
} }
// - Get people info from database // - Get people info from database
export const dbGetPeopleInfo = (page?: number) => { export const dbGetPeopleInfo = (page: number, limit: number) => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
const {authorize, user} = getState() const state = getState()
let uid: string = authorize.uid const {people} = state.user
if (uid) { const lastPageRequest = people.lastPageRequest
const lastKey = '' const lastUserId = people.lastUserId
return userService.getUsersProfile(uid, lastKey) const hasMoreData = people.hasMoreData
.then((usersProfile: {[userId: string]: Profile}) => {
dispatch(addPeopleInfo(usersProfile)) let uid: string = state.authorize.uid
if (uid && lastPageRequest !== page) {
return userService.getUsersProfile(uid, lastUserId, page, limit).then((result) => {
if (!result.users || !(result.users.length > 0)) {
return dispatch(notMoreDataPeople())
}
// Store last user Id
dispatch(lastUserPeople(result.newLastUserId))
let parsedData: {[userId: string]: Profile} = {}
result.users.forEach((post) => {
const userId = Object.keys(post)[0]
const postData = post[userId]
parsedData = {
...parsedData,
[userId]: {
...postData
}
}
})
dispatch(addPeopleInfo(parsedData))
}) })
.catch((error: SocialError) => dispatch(globalActions.showErrorMessage(error.message))) .catch((error: SocialError) => dispatch(globalActions.showErrorMessage(error.message)))
} }
} }
@@ -167,17 +192,6 @@ export const updateUserInfo = (uid: string, info: Profile) => {
} }
} }
/**
* User info
* @param {Profile} info
*/
export const userInfo = (info: Profile) => {
return {
type: UserActionType.USER_INFO,
info
}
}
export const clearAllData = () => { export const clearAllData = () => {
return { return {
type: UserActionType.CLEAR_ALL_DATA_USER type: UserActionType.CLEAR_ALL_DATA_USER
@@ -202,4 +216,46 @@ export const closeEditProfile = () => {
type: UserActionType.CLOSE_EDIT_PROFILE type: UserActionType.CLOSE_EDIT_PROFILE
} }
} }
/**
* Set profile posts has more data to show
*/
export const hasMoreDataPeople = () => {
return {
type: UserActionType.HAS_MORE_DATA_PEOPLE
}
}
/**
* Set profile posts has not data any more to show
*/
export const notMoreDataPeople = () => {
return {
type: UserActionType.NOT_MORE_DATA_PEOPLE
}
}
/**
* Set last page request of profile posts
*/
export const requestPagePeople = (page: number) => {
return {
type: UserActionType.REQUEST_PAGE_PEOPLE,
payload: { page}
}
}
/**
* Set last user identification of find people page
*/
export const lastUserPeople = (lastUserId: string) => {
return {
type: UserActionType.LAST_USER_PEOPLE,
payload: { lastUserId}
}
}

View File

@@ -11,12 +11,15 @@ 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 * as postActions from 'actions/postActions'
import { IServiceProvider, ServiceProvide } from 'core/factories'
import { IVoteService } from 'core/services/votes' import { IVoteService } from 'core/services/votes'
import { Post } from 'core/domain/posts' import { Post } from 'core/domain/posts'
import { SocialProviderTypes } from 'core/socialProviderTypes'
import { provider } from '../socialEngine'
const serviceProvider: IServiceProvider = new ServiceProvide() /**
const voteService: IVoteService = serviceProvider.createVoteService() * Get service providers
*/
const voteService: IVoteService = provider.get<IVoteService>(SocialProviderTypes.VoteService)
/* _____________ CRUD DB _____________ */ /* _____________ CRUD DB _____________ */
@@ -40,7 +43,8 @@ export const dbAddVote = (postId: string,ownerPostUserId: string) => {
const post: Post = state.post.userPosts[ownerPostUserId][postId] const post: Post = state.post.userPosts[ownerPostUserId][postId]
post.score! += 1 post.score! += 1
dispatch(postActions.updatePost(ownerPostUserId,post)) post.votes = { ...post.votes!, [uid]: true}
dispatch(postActions.updatePost(post))
return voteService.addVote(vote).then((voteKey: string) => { return voteService.addVote(vote).then((voteKey: string) => {
if (uid !== ownerPostUserId) { if (uid !== ownerPostUserId) {
@@ -56,7 +60,8 @@ export const dbAddVote = (postId: string,ownerPostUserId: string) => {
}) })
.catch((error) => { .catch((error) => {
post.score! -= 1 post.score! -= 1
dispatch(postActions.updatePost(ownerPostUserId,post)) post.votes = { ...post.votes!, [uid]: false}
dispatch(postActions.updatePost(post))
dispatch(globalActions.showErrorMessage(error.message)) dispatch(globalActions.showErrorMessage(error.message))
}) })
} }
@@ -99,17 +104,15 @@ export const dbDeleteVote = (postId: string, ownerPostUserId: string) => {
const state = getState() const state = getState()
// Get current user id // Get current user id
let uid: string = state.authorize.uid let uid: string = state.authorize.uid
let votes: {[voteId: string]: Vote} = getState().vote.postVotes[postId]
let id: string = Object.keys(votes).filter((key) => votes[key].userId === uid)[0]
const vote = votes[id]
const post: Post = state.post.userPosts[ownerPostUserId][postId] const post: Post = state.post.userPosts[ownerPostUserId][postId]
post.score! -= 1 post.score! -= 1
dispatch(postActions.updatePost(ownerPostUserId,post)) post.votes = { ...post.votes!, [uid]: false}
return voteService.deleteVote(vote).then(x => x) dispatch(postActions.updatePost(post))
return voteService.deleteVote(uid, postId).then(x => x)
.catch((error: any) => { .catch((error: any) => {
post.score! += 1 post.score! += 1
dispatch(postActions.updatePost(ownerPostUserId,post)) post.votes = { ...post.votes!, [uid]: true}
dispatch(postActions.updatePost(post))
dispatch(globalActions.showErrorMessage(error.message)) dispatch(globalActions.showErrorMessage(error.message))
}) })
} }
@@ -129,8 +132,8 @@ export const addVote = (vote: Vote) => {
* @param {string} id vote identifier * @param {string} id vote identifier
* @param {string} postId post identifier which vote on * @param {string} postId post identifier which vote on
*/ */
export const deleteVote = (id: string, postId: string) => { export const deleteVote = (userId: string, postId: string) => {
return { type: VoteActionType.DELETE_VOTE, payload: {id, postId} } return { type: VoteActionType.DELETE_VOTE, payload: {userId, postId} }
} }

View File

@@ -1,4 +1,4 @@
import { Circle, UserFollower } from 'core/domain/circles' import { Circle, UserTie } from 'core/domain/circles'
/** /**
* Get the circles' id which the specify users is in that circle * Get the circles' id which the specify users is in that circle
@@ -24,7 +24,7 @@ export const getUserBelongCircles = (circles: {[circleId: string]: Circle},follo
* @param {object} circles * @param {object} circles
*/ */
export const getFollowingUsers = (circles: {[circleId: string]: Circle}) => { export const getFollowingUsers = (circles: {[circleId: string]: Circle}) => {
let followingUsers: {[userId: string]: UserFollower} = {} let followingUsers: {[userId: string]: UserTie} = {}
Object.keys(circles).forEach((cid) => { Object.keys(circles).forEach((cid) => {
if (cid.trim() !== '-Followers' && circles[cid].users) { if (cid.trim() !== '-Followers' && circles[cid].users) {
Object.keys(circles[cid].users).forEach((userId) => { Object.keys(circles[cid].users).forEach((userId) => {

32
src/api/CommonAPI.ts Normal file
View File

@@ -0,0 +1,32 @@
/**
* Log the data
* @param title log title
* @param data log data object
*/
const logger = (title: string, data: any, trace?: boolean) => {
const randomColor = getRandomColor()
if (trace) {
console.trace()
}
console.log(`\n\n\n%c ${title} :\n`, `color:${getRandomColor()};font-size:15`)
console.log('%c =========================================', `color:${randomColor}`)
console.log(data)
console.log('%c =========================================', `color:${randomColor}`)
}
/**
* Get random color in hex
*/
const getRandomColor = () => {
let letters = '0123456789ABCDEF'
let color = '#'
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)]
}
return color
}
export default {
logger,
getRandomColor
}

View File

@@ -1,3 +1,5 @@
import { ServerRequestType } from 'constants/serverRequestType'
// - Import actions // - Import actions
const isValidEmail = (email: string) => { const isValidEmail = (email: string) => {
@@ -5,6 +7,10 @@ const isValidEmail = (email: string) => {
return re.test(email) return re.test(email)
} }
const createServerRequestId = (requestType: ServerRequestType, uniqueId: string) => {
return `${requestType}:${uniqueId}`
}
function queryString (name: string, url: string = window.location.href) { function queryString (name: string, url: string = window.location.href) {
name = name.replace(/[[]]/g, '\\$&') name = name.replace(/[[]]/g, '\\$&')
@@ -23,5 +29,6 @@ function queryString (name: string, url: string = window.location.href) {
export default { export default {
isValidEmail, isValidEmail,
queryString queryString,
createServerRequestId
} }

View File

@@ -244,8 +244,8 @@ const mapDispatchToProps = (dispatch: any, ownProps: ICircleComponentProps) => {
return { return {
deleteCircle: (id: string) => dispatch(circleActions.dbDeleteCircle(id)), deleteCircle: (id: string) => dispatch(circleActions.dbDeleteCircle(id)),
updateCircle: (circle: Circle) => dispatch(circleActions.dbUpdateCircle(circle)), updateCircle: (circle: Circle) => dispatch(circleActions.dbUpdateCircle(circle)),
closeCircleSettings: () => dispatch(circleActions.closeCircleSettings(uid, ownProps.id)), closeCircleSettings: () => dispatch(circleActions.closeCircleSettings(ownProps.id)),
openCircleSettings: () => dispatch(circleActions.openCircleSettings(uid, ownProps.id)), openCircleSettings: () => dispatch(circleActions.openCircleSettings(ownProps.id)),
goTo: (url: string) => dispatch(push(url)) goTo: (url: string) => dispatch(push(url))
} }
@@ -258,9 +258,12 @@ const mapDispatchToProps = (dispatch: any, ownProps: ICircleComponentProps) => {
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: ICircleComponentProps) => { const mapStateToProps = (state: any, ownProps: ICircleComponentProps) => {
let { uid } = state.authorize const {circle, authorize, server} = state
const { uid } = state.authorize
const circles: { [circleId: string]: Circle } = circle ? (circle.circleList || {}) : {}
const currentCircle = (circles ? circles[ownProps.id] : {}) as Circle
return { return {
openSetting: state.circle ? (state.circle.userCircles[uid] ? (state.circle.userCircles[uid][ownProps.id].openCircleSettings || false) : false) : false, openSetting: state.circle ? (currentCircle ? (currentCircle.openCircleSettings || false) : false) : false,
userInfo: state.user.info userInfo: state.user.info
} }

View File

@@ -6,6 +6,8 @@ import PropTypes from 'prop-types'
import moment from 'moment' import moment from 'moment'
import Linkify from 'react-linkify' import Linkify from 'react-linkify'
import { Comment } from 'core/domain/comments'
// - Import material UI libraries // - Import material UI libraries
import { List, ListItem } from 'material-ui/List' import { List, ListItem } from 'material-ui/List'
import Divider from 'material-ui/Divider' import Divider from 'material-ui/Divider'
@@ -183,8 +185,10 @@ export class CommentComponent extends Component<ICommentComponentProps,ICommentC
* @param {event} evt is an event passed by clicking on post button * @param {event} evt is an event passed by clicking on post button
*/ */
handleUpdateComment = (evt: any) => { handleUpdateComment = (evt: any) => {
const {comment} = this.props
this.props.update(this.props.comment.id, this.props.comment.postId, this.state.text) comment.editorStatus = undefined
comment.text = this.state.text
this.props.update(comment)
this.setState({ this.setState({
initialText: this.state.text initialText: this.state.text
}) })
@@ -302,13 +306,18 @@ export class CommentComponent extends Component<ICommentComponentProps,ICommentC
* @param {object} ownProps is the props belong to component * @param {object} ownProps is the props belong to component
* @return {object} props of component * @return {object} props of component
*/ */
const mapDispatchToProps = (dispatch: any, ownProps: any) => { const mapDispatchToProps = (dispatch: any, ownProps: ICommentComponentProps) => {
return { return {
delete: (id: string| null, postId: string) => dispatch(commentActions.dbDeleteComment(id, postId)), delete: (id: string| null, postId: string) => dispatch(commentActions.dbDeleteComment(id, postId)),
update: (id: string, postId: string, comment: string) => dispatch(commentActions.dbUpdateComment(id, postId, comment)), update: (comment: Comment) => {
console.log('====================================')
console.log(comment)
console.log('====================================')
dispatch(commentActions.dbUpdateComment(comment))
},
openEditor: () => dispatch(commentActions.openCommentEditor({ id: ownProps.comment.id, postId: ownProps.comment.postId })), openEditor: () => dispatch(commentActions.openCommentEditor({ id: ownProps.comment.id, postId: ownProps.comment.postId })),
closeEditor: () => dispatch(commentActions.closeCommentEditor({ id: ownProps.comment.id, postId: ownProps.comment.postId })), closeEditor: () => dispatch(commentActions.closeCommentEditor({ id: ownProps.comment.id, postId: ownProps.comment.postId })),
getUserInfo: () => dispatch(userActions.dbGetUserInfoByUserId(ownProps.comment.userId,'')) getUserInfo: () => dispatch(userActions.dbGetUserInfoByUserId(ownProps.comment.userId!,''))
} }
} }

View File

@@ -47,7 +47,7 @@ export interface ICommentComponentProps {
* *
* @memberof ICommentComponentProps * @memberof ICommentComponentProps
*/ */
update: (id?: string | null, postId?: string, comment?: string | null) => any update: (comment: Comment) => any
/** /**
* Delete comment * Delete comment

View File

@@ -1,6 +1,7 @@
// - Import react components // - Import react components
import React, { Component } from 'react' import React, { Component } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import _ from 'lodash'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import Paper from 'material-ui/Paper' import Paper from 'material-ui/Paper'
import FlatButton from 'material-ui/FlatButton' import FlatButton from 'material-ui/FlatButton'
@@ -101,7 +102,7 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
clearCommentWrite = () => { clearCommentWrite = () => {
this.setState({ this.setState({
commentText: '', commentText: '',
postDisable: false postDisable: true
}) })
} }
@@ -109,7 +110,10 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
* Post comment * Post comment
*/ */
handlePostComment = () => { handlePostComment = () => {
this.props.send!(this.state.commentText, this.props.postId, this.clearCommentWrite) this.props.send!(this.state.commentText, this.props.postId, this.clearCommentWrite)
this.clearCommentWrite()
} }
/** /**
@@ -140,9 +144,10 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
commentList = () => { commentList = () => {
let comments = this.props.commentSlides let comments = this.props.commentSlides
if (comments) { if (comments) {
comments = _.fromPairs(_.toPairs(comments)
.sort((a: any, b: any) => parseInt(b[1].creationDate,10) - parseInt(a[1].creationDate,10)))
let parsedComments: Comment[] = [] let parsedComments: Comment[] = []
Object.keys(comments).slice(0, 3).forEach((commentId) => { Object.keys(comments).forEach((commentId) => {
parsedComments.push({ parsedComments.push({
id: commentId, id: commentId,
...comments![commentId] ...comments![commentId]
@@ -222,6 +227,7 @@ export class CommentGroupComponent extends Component<ICommentGroupComponentProps
<FlatButton primary={true} disabled={this.state.postDisable} label='Post' style={{ float: 'right', clear: 'both', zIndex: 5, margin: '0px 5px 5px 0px', fontWeight: 400 }} onClick={this.handlePostComment} /> <FlatButton primary={true} disabled={this.state.postDisable} label='Post' style={{ float: 'right', clear: 'both', zIndex: 5, margin: '0px 5px 5px 0px', fontWeight: 400 }} onClick={this.handlePostComment} />
</Paper> </Paper>
</div>) </div>)
/** /**
* Return Elements * Return Elements
*/ */

View File

@@ -227,7 +227,8 @@ export class EditProfileComponent extends Component<IEditProfileComponentProps,I
fullName: fullNameInput, fullName: fullNameInput,
tagLine: tagLineInput, tagLine: tagLineInput,
avatar: avatar, avatar: avatar,
banner: banner banner: banner,
creationDate: this.props.info!.creationDate
}) })
} }
} }

View File

@@ -7,6 +7,7 @@ import InfiniteScroll from 'react-infinite-scroller'
// - Import app components // - Import app components
import UserBoxList from 'components/userBoxList' import UserBoxList from 'components/userBoxList'
import LoadMoreProgressComponent from 'layouts/loadMoreProgress'
// - Import API // - Import API
@@ -20,10 +21,6 @@ import { IFindPeopleComponentState } from './IFindPeopleComponentState'
*/ */
export class FindPeopleComponent extends Component<IFindPeopleComponentProps, IFindPeopleComponentState> { export class FindPeopleComponent extends Component<IFindPeopleComponentProps, IFindPeopleComponentState> {
static propTypes = {
}
/** /**
* Component constructor * Component constructor
* @param {object} props is an object properties of component * @param {object} props is an object properties of component
@@ -36,49 +33,32 @@ export class FindPeopleComponent extends Component<IFindPeopleComponentProps, IF
} }
// Binding functions to `this`
} }
loadItems (page: number) { /**
console.log('------------------------') * Scroll loader
console.log(page) */
console.log('------------------------') scrollLoad = (page: number) => {
const {loadPeople} = this.props
loadPeople!(page, 10)
} }
componentWillMount () {
this.props.loadPeople!()
}
/** /**
* Reneder component DOM * Reneder component DOM
* @return {react element} return the DOM which rendered by component * @return {react element} return the DOM which rendered by component
*/ */
render () { render () {
const {hasMorePeople} = this.props
const styles = {
paper: {
height: 254,
width: 243,
margin: 10,
textAlign: 'center',
maxWidth: '257px'
},
followButton: {
position: 'absolute',
bottom: '8px',
left: 0,
right: 0
}
}
const loader = <div className='loader'>Loading ...</div>
return ( return (
<div> <div>
<InfiniteScroll <InfiniteScroll
pageStart={0} pageStart={0}
loadMore={this.loadItems.bind(this)} loadMore={this.scrollLoad}
hasMore={false} hasMore={hasMorePeople}
loader={loader}> useWindow={true}
loader={ <LoadMoreProgressComponent />}
>
<div className='tracks'> <div className='tracks'>
@@ -106,7 +86,7 @@ export class FindPeopleComponent extends Component<IFindPeopleComponentProps, IF
*/ */
const mapDispatchToProps = (dispatch: any, ownProps: IFindPeopleComponentProps) => { const mapDispatchToProps = (dispatch: any, ownProps: IFindPeopleComponentProps) => {
return { return {
loadPeople: () => dispatch(userActions.dbGetPeopleInfo()) loadPeople: (page: number, limit: number) => dispatch(userActions.dbGetPeopleInfo(page, limit))
} }
} }
@@ -117,8 +97,10 @@ const mapDispatchToProps = (dispatch: any, ownProps: IFindPeopleComponentProps)
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: IFindPeopleComponentProps) => { const mapStateToProps = (state: any, ownProps: IFindPeopleComponentProps) => {
const {people, info} = state.user
return { return {
peopleInfo: state.user.info peopleInfo: info,
hasMorePeople: people.hasMoreData
} }
} }

View File

@@ -7,7 +7,7 @@ export interface IFindPeopleComponentProps {
* *
* @memberof IFindPeopleComponentProps * @memberof IFindPeopleComponentProps
*/ */
loadPeople?: () => any loadPeople?: (page: number, limit: number) => any
/** /**
* Users' profile * Users' profile
@@ -17,4 +17,9 @@ export interface IFindPeopleComponentProps {
*/ */
peopleInfo?: {[userId: string]: Profile} peopleInfo?: {[userId: string]: Profile}
/**
* If there are more people {true} or not {false}
*/
hasMorePeople: boolean
} }

View File

@@ -8,6 +8,7 @@ import UserBoxList from 'components/userBoxList'
import { IFollowersComponentProps } from './IFollowersComponentProps' import { IFollowersComponentProps } from './IFollowersComponentProps'
import { IFollowersComponentState } from './IFollowersComponentState' import { IFollowersComponentState } from './IFollowersComponentState'
import { Circle } from 'core/domain/circles';
// - Import API // - Import API
@@ -79,10 +80,11 @@ const mapDispatchToProps = (dispatch: any,ownProps: IFollowersComponentProps) =>
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any,ownProps: IFollowersComponentProps) => { const mapStateToProps = (state: any,ownProps: IFollowersComponentProps) => {
const {circle, authorize, server} = state
const { uid } = state.authorize const { uid } = state.authorize
const circles = state.circle ? state.circle.userCircles[uid] : {} const circles: { [circleId: string]: Circle } = circle ? (circle.circleList || {}) : {}
return{ return{
followers: circles ? (circles['-Followers'] ? circles['-Followers'].users || {} : {}) : {} followers: circles ? circles.userTieds : {}
} }
} }

View File

@@ -1,12 +1,12 @@
import { UserFollower } from 'core/domain/circles' import { UserTie } from 'core/domain/circles'
export interface IFollowersComponentProps { export interface IFollowersComponentProps {
/** /**
* User followers info * User followers info
* *
* @type {{[userId: string]: UserFollower}} * @type {{[userId: string]: UserTie}}
* @memberof IFindPeopleComponentProps * @memberof IFindPeopleComponentProps
*/ */
followers?: {[userId: string]: UserFollower} followers?: {[userId: string]: UserTie}
} }

View File

@@ -10,6 +10,7 @@ import UserBoxList from 'components/userBoxList'
import CircleAPI from 'api/CircleAPI' import CircleAPI from 'api/CircleAPI'
import { IFollowingComponentProps } from './IFollowingComponentProps' import { IFollowingComponentProps } from './IFollowingComponentProps'
import { IFollowingComponentState } from './IFollowingComponentState' import { IFollowingComponentState } from './IFollowingComponentState'
import { Circle } from 'core/domain/circles';
// - Import actions // - Import actions
@@ -80,9 +81,10 @@ const mapDispatchToProps = (dispatch: any,ownProp: IFollowingComponentProps) =>
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any,ownProps: IFollowingComponentProps) => { const mapStateToProps = (state: any,ownProps: IFollowingComponentProps) => {
const {circle, authorize, server} = state
const { uid } = state.authorize const { uid } = state.authorize
const circles = state.circle ? state.circle.userCircles[uid] : {} const circles: { [circleId: string]: Circle } = circle ? (circle.circleList || {}) : {}
const followingUsers = CircleAPI.getFollowingUsers(circles) const followingUsers = circle ? circle.userTies : {}
return { return {
uid, uid,
circles, circles,

View File

@@ -1,6 +1,6 @@
import { UserFollower } from 'core/domain/circles' import { UserTie } from 'core/domain/circles'
export interface IFollowingComponentProps { export interface IFollowingComponentProps {
followingUsers?: {[userId: string]: UserFollower} followingUsers?: {[userId: string]: UserTie}
} }

View File

@@ -106,7 +106,6 @@ export class HomeComponent extends Component<IHomeComponentProps, IHomeComponent
} }
componentWillMount () { componentWillMount () {
const {global, clearData, loadData, authed, defaultDataEnable, isVerifide, goTo } = this.props const {global, clearData, loadData, authed, defaultDataEnable, isVerifide, goTo } = this.props
if (!authed) { if (!authed) {
goTo!('/login') goTo!('/login')
@@ -131,7 +130,7 @@ export class HomeComponent extends Component<IHomeComponentProps, IHomeComponent
* @memberof Home * @memberof Home
*/ */
render () { render () {
const {loaded, authed, mergedPosts} = this.props const {loaded, authed, loadDataStream, mergedPosts, hasMorePosts} = 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,7 +152,7 @@ export class HomeComponent extends Component<IHomeComponentProps, IHomeComponent
</SidebarContent> </SidebarContent>
<SidebarMain> <SidebarMain>
<HomeRouter enabled={loaded!} data={{mergedPosts}} /> <HomeRouter enabled={loaded!} data={{ mergedPosts, loadDataStream, hasMorePosts}} />
</SidebarMain> </SidebarMain>
</Sidebar> </Sidebar>
@@ -167,12 +166,15 @@ export class HomeComponent extends Component<IHomeComponentProps, IHomeComponent
const mapDispatchToProps = (dispatch: any, ownProps: IHomeComponentProps) => { const mapDispatchToProps = (dispatch: any, ownProps: IHomeComponentProps) => {
return { return {
loadDataStream:
(page: number, limit: number) => dispatch(postActions.dbGetPosts(page,limit)),
loadData: () => { loadData: () => {
dispatch(imageGalleryActions.dbGetImageGallery())
dispatch(postActions.dbGetPosts()) dispatch(postActions.dbGetPosts())
dispatch(imageGalleryActions.dbGetImageGallery())
dispatch(userActions.dbGetUserInfo()) dispatch(userActions.dbGetUserInfo())
dispatch(notifyActions.dbGetNotifications()) dispatch(notifyActions.dbGetNotifications())
dispatch(circleActions.dbGetCircles()) dispatch(circleActions.dbGetCircles())
dispatch(circleActions.dbGetUserTies())
}, },
clearData: () => { clearData: () => {
@@ -205,9 +207,10 @@ const mapStateToProps = (state: any, ownProps: IHomeComponentProps) => {
const { authorize, global, user, post, imageGallery, 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.circleList || {}) : {}
const followingUsers = CircleAPI.getFollowingUsers(circles) const followingUsers = circle ? circle.userTies : {}
const posts = post.userPosts ? post.userPosts[authorize.uid] : {} const posts = post.userPosts ? post.userPosts[authorize.uid] : {}
const hasMorePosts = post.stream.hasMoreData
Object.keys(followingUsers).forEach((userId) => { Object.keys(followingUsers).forEach((userId) => {
let newPosts = post.userPosts ? post.userPosts[userId] : {} let newPosts = post.userPosts ? post.userPosts[userId] : {}
_.merge(mergedPosts,newPosts) _.merge(mergedPosts,newPosts)
@@ -219,9 +222,10 @@ const mapStateToProps = (state: any, ownProps: IHomeComponentProps) => {
mainStyle: global.sidebarMainStyle, mainStyle: global.sidebarMainStyle,
mergedPosts, mergedPosts,
global, global,
hasMorePosts,
loaded: user.loaded && post.loaded && imageGallery.loaded && notify.loaded && circle.loaded loaded: user.loaded && post.loaded && imageGallery.loaded && notify.loaded && circle.loaded
} }
} }
// - Connect component to redux store // - Connect component to redux store
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(HomeComponent as any)) export default withRouter(connect(mapStateToProps, mapDispatchToProps)(HomeComponent as any) as any)

View File

@@ -24,7 +24,7 @@ export interface IHomeComponentProps {
* @type {string} * @type {string}
* @memberof IHomeComponentProps * @memberof IHomeComponentProps
*/ */
uid: string uid?: string
/** /**
* Merged all users posts to show in stream * Merged all users posts to show in stream
@@ -34,6 +34,11 @@ export interface IHomeComponentProps {
*/ */
mergedPosts?: {[postId: string]: Post} mergedPosts?: {[postId: string]: Post}
/**
* Load the data for stream
*/
loadDataStream: (lastPostId: string, page: number, limit: number) => any
/** /**
* Global state * Global state
* *
@@ -80,6 +85,11 @@ export interface IHomeComponentProps {
*/ */
goTo?: (url: string) => any goTo?: (url: string) => any
/**
* If there is more post {true} or not {false}
*/
hasMorePosts?: boolean
/** /**
* If all requierment data loaded {true} or not {false} * If all requierment data loaded {true} or not {false}
* *

View File

@@ -29,7 +29,6 @@ export class HomeHeaderComponent extends Component<IHomeHeaderComponentProps,IHo
styles = { styles = {
toolbarStyle: { toolbarStyle: {
backgroundColor: '',
transition: 'all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms', transition: 'all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms',
boxSizing: 'border-box', boxSizing: 'border-box',
fontFamily: 'Roboto, sans-serif', fontFamily: 'Roboto, sans-serif',
@@ -187,7 +186,7 @@ export class HomeHeaderComponent extends Component<IHomeHeaderComponentProps,IHo
return ( return (
<Toolbar style={this.styles.toolbarStyle as any} className='g__greenBox'> <Toolbar style={this.styles.toolbarStyle as any}>
<EventListener <EventListener
target='window' target='window'
onResize={this.handleResize} onResize={this.handleResize}

View File

@@ -191,4 +191,4 @@ const mapStateToProps = (state: any) => {
} }
// - Connect commponent to redux store // - Connect commponent to redux store
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(MasterComponent as any)) export default withRouter(connect(mapStateToProps, mapDispatchToProps)(MasterComponent as any) as any)

View File

@@ -17,7 +17,7 @@ export interface IPostComponentProps {
* @type {string} * @type {string}
* @memberof IPostComponentProps * @memberof IPostComponentProps
*/ */
avatar: string avatar?: string
/** /**
* User full name * User full name
@@ -105,7 +105,7 @@ export interface IPostComponentProps {
* *
* @memberof IPostComponentProps * @memberof IPostComponentProps
*/ */
getPostComments: (ownerUserId: string, postId: string) => any getPostComments?: (ownerUserId: string, postId: string) => any
/** /**
* Commnets * Commnets

View File

@@ -87,7 +87,7 @@ export class PostComponent extends Component<IPostComponentProps,IPostComponentS
/** /**
* Post text * Post text
*/ */
text: post.body!, text: post.body ? post.body : '',
/** /**
* It's true if whole the text post is visible * It's true if whole the text post is visible
*/ */
@@ -409,7 +409,7 @@ const mapDispatchToProps = (dispatch: any, ownProps: IPostComponentProps) => {
const mapStateToProps = (state: any, ownProps: IPostComponentProps) => { const mapStateToProps = (state: any, ownProps: IPostComponentProps) => {
const {post, vote, authorize, comment} = state const {post, vote, authorize, comment} = state
const {uid} = authorize const {uid} = authorize
let currentUserVote = post.votes ? post.votes[uid] : false let currentUserVote = ownProps.post.votes ? ownProps.post.votes[uid] : false
const postModel = post.userPosts[ownProps.post.ownerUserId!][ownProps.post.id!] 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 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!] const commentList: { [commentId: string]: Comment } = comment.postComments[ownProps.post.id!]

View File

@@ -50,12 +50,12 @@ export interface IPostWriteComponentProps {
/** /**
* The post owner name * The post owner name
*/ */
ownerDisplayName: string ownerDisplayName?: string
/** /**
* Post model * Post model
*/ */
postModel: Post postModel?: Post
/** /**
* Save a post * Save a post

View File

@@ -49,15 +49,15 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
/** /**
* Post text * Post text
*/ */
postText: this.props.edit ? this.props.text! : '', postText: this.props.edit && postModel ? (postModel.body ? postModel.body! : '' ) : '',
/** /**
* The URL image of the post * The URL image of the post
*/ */
image: this.props.edit ? this.props.image : '', image: this.props.edit && postModel ? (postModel.image ? postModel.image! : '' ) : '',
/** /**
* The path identifier of image on the server * The path identifier of image on the server
*/ */
imageFullPath: this.props.edit ? (postModel.imageFullPath ? postModel.imageFullPath! : '' ) : '', imageFullPath: this.props.edit && postModel ? (postModel.imageFullPath ? postModel.imageFullPath! : '' ) : '',
/** /**
* If it's true gallery will be open * If it's true gallery will be open
*/ */
@@ -69,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 ? postModel.disableComments! : false, disableComments: this.props.edit && postModel ? 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 ? postModel.disableSharing! : false disableSharing: this.props.edit && postModel ? postModel.disableSharing! : false
} }
@@ -190,14 +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
postModel.body = postText postModel!.body = postText
postModel.tags = tags postModel!.tags = tags
postModel.image = image postModel!.image = image
postModel.imageFullPath = imageFullPath postModel!.imageFullPath = imageFullPath
postModel.disableComments = disableComments postModel!.disableComments = disableComments
postModel.disableSharing = disableSharing postModel!.disableSharing = disableSharing
update!(postModel, onRequestClose) update!(postModel!, onRequestClose)
} }
} }
@@ -255,30 +255,34 @@ export class PostWriteComponent extends Component<IPostWriteComponentProps,IPost
if (!nextProps.open) { if (!nextProps.open) {
const {postModel} = this.props const {postModel} = this.props
this.setState({ this.setState({
/** /**
* Post text * Post text
*/ */
postText: this.props.edit ? this.props.text! : '', postText: this.props.edit && postModel ? (postModel.body ? postModel.body! : '' ) : '',
/** /**
* The image of the post * The URL image of the post
*/ */
image: this.props.edit ? this.props.image! : '', image: this.props.edit && postModel ? (postModel.image ? postModel.image! : '' ) : '',
/** /**
* If it's true gallery will be open * The path identifier of image on the server
*/ */
imageFullPath: this.props.edit && postModel ? (postModel.imageFullPath ? postModel.imageFullPath! : '' ) : '',
/**
* If it's true gallery will be open
*/
galleryOpen: false, galleryOpen: false,
/** /**
* If it's true post button will be disabled * If it's true post button will be disabled
*/ */
disabledPost: true, disabledPost: true,
/** /**
* If it's true comment will be disabled on post * If it's true comment will be disabled on post
*/ */
disableComments: this.props.edit ? postModel.disableComments! : false, disableComments: this.props.edit && postModel ? 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 ? postModel.disableSharing! : false disableSharing: this.props.edit && postModel ? postModel.disableSharing! : false
}) })
} }

View File

@@ -79,4 +79,9 @@ export interface IProfileComponentProps {
* @memberof IProfileComponentProps * @memberof IProfileComponentProps
*/ */
loadUserInfo: () => any loadUserInfo: () => any
/**
* If there is more posts to show in profile
*/
hasMorePosts: boolean
} }

View File

@@ -77,6 +77,7 @@ export class ProfileComponent extends Component<IProfileComponentProps,IProfileC
border: '2px solid rgb(255, 255, 255)' border: '2px solid rgb(255, 255, 255)'
} }
} }
const {loadPosts, hasMorePosts} = this.props
return ( return (
<div style={styles.profile}> <div style={styles.profile}>
@@ -91,7 +92,11 @@ export class ProfileComponent extends Component<IProfileComponentProps,IProfileC
</div> </div>
<div style={{ height: '24px' }}></div> <div style={{ height: '24px' }}></div>
<StreamComponent posts={this.props.posts} displayWriting={false} /> <StreamComponent
posts={this.props.posts}
loadStream={loadPosts}
hasMorePosts={hasMorePosts}
displayWriting={false} />
</div>) </div>)
: (<div className='profile__title'> : (<div className='profile__title'>
Nothing shared Nothing shared
@@ -127,6 +132,7 @@ const mapDispatchToProps = (dispatch: any, ownProps: IProfileComponentProps) =>
const mapStateToProps = (state: any, ownProps: IProfileComponentProps) => { const mapStateToProps = (state: any, ownProps: IProfileComponentProps) => {
const { userId } = ownProps.match.params const { userId } = ownProps.match.params
const {uid} = state.authorize const {uid} = state.authorize
const hasMorePosts = state.post.profile.hasMoreData
return { return {
avatar: state.user.info && state.user.info[userId] ? state.user.info[userId].avatar || '' : '', avatar: state.user.info && state.user.info[userId] ? state.user.info[userId].avatar || '' : '',
name: state.user.info && state.user.info[userId] ? state.user.info[userId].fullName || '' : '', name: state.user.info && state.user.info[userId] ? state.user.info[userId].fullName || '' : '',
@@ -134,7 +140,8 @@ const mapStateToProps = (state: any, ownProps: IProfileComponentProps) => {
tagLine: state.user.info && state.user.info[userId] ? state.user.info[userId].tagLine || '' : '', tagLine: state.user.info && state.user.info[userId] ? state.user.info[userId].tagLine || '' : '',
posts: state.post.userPosts ? state.post.userPosts[userId] : {}, posts: state.post.userPosts ? state.post.userPosts[userId] : {},
isAuthedUser: userId === uid, isAuthedUser: userId === uid,
userId userId,
hasMorePosts
} }
} }

View File

@@ -0,0 +1,92 @@
// - Import react components
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { grey400 } from 'material-ui/styles/colors'
import SvgClose from 'material-ui/svg-icons/navigation/close'
import FlatButton from 'material-ui/FlatButton'
import Divider from 'material-ui/Divider'
import { IDialogTitleComponentProps } from './IDialogTitleComponentProps'
import { IDialogTitleComponentState } from './IDialogTitleComponentState'
/**
* Create component class
*/
export default class DialogTitleComponent extends Component<IDialogTitleComponentProps,IDialogTitleComponentState> {
static propTypes = {
/**
* The label of right button
*/
buttonLabel: PropTypes.string,
/**
* If it's true button will be disabled
*/
disabledButton: PropTypes.bool,
/**
* Call the funtion the time is clicked on right button
*/
onClickButton: PropTypes.func,
/**
* The function will be called the time is clicked on close
*/
onRequestClose: PropTypes.func.isRequired,
/**
* The title of dialog box
*/
title: PropTypes.string
}
styles = {
contain: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between'
},
title: {
color: 'rgba(0,0,0,0.87)',
flex: '1 1',
font: '500 20px Roboto,RobotoDraft,Helvetica,Arial,sans-serif'
}
}
/**
* Component constructor
* @param {object} props is an object properties of component
*/
constructor (props: IDialogTitleComponentProps) {
super(props)
// Defaul state
this.state = {
}
// Binding functions to `this`
}
/**
* Reneder component DOM
* @return {react element} return the DOM which rendered by component
*/
render () {
const { buttonLabel, disabledButton, onClickButton, onRequestClose, title } = this.props
return (
<div className='g__dialog-title'>
<div style={this.styles.contain as any}>
<div style={{ paddingRight: '10px' }}>
<SvgClose onClick={onRequestClose} hoverColor={grey400} style={{ cursor: 'pointer' }} />
</div>
<div style={this.styles.title}>
{title || ''}
</div>
{ buttonLabel ? (<div style={{ marginTop: '-9px' }}>
<FlatButton label={buttonLabel || ''} primary={true} disabled={disabledButton ? disabledButton : false} onClick={onClickButton || (x => x)} />
</div>) : ''}
</div>
<Divider />
</div>
)
}
}

View File

@@ -0,0 +1,40 @@
export interface IDialogTitleComponentProps {
/**
* Lable of the button
*
* @type {string}
* @memberof IDialogTitleComponentProps
*/
buttonLabel?: string
/**
* Dialog tile
*
* @type {string}
* @memberof IDialogTitleComponentProps
*/
title: string
/**
* Button is disabled {true} or not {false}
*
* @type {boolean}
* @memberof IDialogTitleComponentProps
*/
disabledButton?: boolean
/**
* On click event
*
* @memberof IDialogTitleComponentProps
*/
onClickButton?: (event: any) => void
/**
* On request close event
*
* @memberof IDialogTitleComponentProps
*/
onRequestClose: (event: any) => void
}

View File

@@ -0,0 +1,3 @@
export interface IDialogTitleComponentState {
}

View File

@@ -0,0 +1,2 @@
import DialogTitleComponent from './DialogTitleComponent'
export default DialogTitleComponent

View File

@@ -66,12 +66,26 @@ export interface IStreamComponentProps {
*/ */
avatar?: string avatar?: string
/**
* Load the data for stream
*/
loadStream: (page: number, limit: number) => any
/**
* If there is more post {true} or not {false}
*/
hasMorePosts: boolean
/** /**
* Posts for stream * Posts for stream
* *
* @type {{[postId: string]: Post}} * @type {{[postId: string]: Post}}
* @memberof IStreamComponentProps * @memberof IStreamComponentProps
*/ */
posts?: {[postId: string]: Post} posts: {[postId: string]: Post}
/**
* Router match property
*/
match: any
} }

View File

@@ -1,3 +1,4 @@
import { Post } from 'core/domain/posts'
export interface IStreamComponentState { export interface IStreamComponentState {
@@ -34,7 +35,15 @@ export interface IStreamComponentState {
divided: boolean divided: boolean
/** /**
* The tile of top bar * If there is more post to show
*
* @type {boolean}
* @memberof IStreamComponentState
*/
hasMorePosts: boolean
/**
* The title of top bar
* *
* @type {string} * @type {string}
* @memberof IStreamComponentState * @memberof IStreamComponentState

View File

@@ -5,15 +5,18 @@ import { connect } from 'react-redux'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { Card, CardActions, CardHeader, CardMedia, CardTitle, CardText } from 'material-ui/Card' import { Card, CardActions, CardHeader, CardMedia, CardTitle, CardText } from 'material-ui/Card'
import FlatButton from 'material-ui/FlatButton' import FlatButton from 'material-ui/FlatButton'
import { grey400, grey800, darkBlack, lightBlack } from 'material-ui/styles/colors' import { grey400, grey800, darkBlack, lightBlack,tealA400 } from 'material-ui/styles/colors'
import CircularProgress from 'material-ui/CircularProgress'
import SvgCamera from 'material-ui/svg-icons/image/photo-camera' import SvgCamera from 'material-ui/svg-icons/image/photo-camera'
import Paper from 'material-ui/Paper' import Paper from 'material-ui/Paper'
import { List, ListItem } from 'material-ui/List' import { List, ListItem } from 'material-ui/List'
import InfiniteScroll from 'react-infinite-scroller'
// - Import app components // - Import app components
import PostComponent from 'components/post' import PostComponent from 'components/post'
import PostWriteComponent from 'components/postWrite' import PostWriteComponent from 'components/postWrite'
import UserAvatarComponent from 'components/userAvatar' import UserAvatarComponent from 'components/userAvatar'
import LoadMoreProgressComponent from 'layouts/loadMoreProgress'
// - Import API // - Import API
import * as PostAPI from 'api/PostAPI' import * as PostAPI from 'api/PostAPI'
@@ -23,6 +26,7 @@ import * as globalActions from 'actions/globalActions'
import { IStreamComponentProps } from './IStreamComponentProps' import { IStreamComponentProps } from './IStreamComponentProps'
import { IStreamComponentState } from './IStreamComponentState' import { IStreamComponentState } from './IStreamComponentState'
import { Post } from 'core/domain/posts'
// - Create StreamComponent component class // - Create StreamComponent component class
export class StreamComponent extends Component<IStreamComponentProps,IStreamComponentState> { export class StreamComponent extends Component<IStreamComponentProps,IStreamComponentState> {
@@ -82,7 +86,12 @@ export class StreamComponent extends Component<IStreamComponentProps,IStreamComp
/** /**
* The title of home header * The title of home header
*/ */
homeTitle: props.homeTitle! homeTitle: props.homeTitle!,
/**
* If there is more post to show {true} or not {false}
*/
hasMorePosts: true
} }
// Binding functions to `this` // Binding functions to `this`
@@ -115,13 +124,13 @@ export class StreamComponent extends Component<IStreamComponentProps,IStreamComp
}) })
} }
/** /**
* Create a list of posts * Create a list of posts
* @return {DOM} posts * @return {DOM} posts
*/ */
postLoad = () => { postLoad = () => {
let { posts ,match }: any = this.props let { posts ,match } = this.props
let {tag} = match.params let {tag} = match.params
if (posts === undefined || !(Object.keys(posts).length > 0)) { if (posts === undefined || !(Object.keys(posts).length > 0)) {
@@ -139,7 +148,7 @@ export class StreamComponent extends Component<IStreamComponentProps,IStreamComp
Object.keys(posts).forEach((postId) => { Object.keys(posts).forEach((postId) => {
if (tag) { if (tag) {
let regex = new RegExp('#' + tag,'g') let regex = new RegExp('#' + tag,'g')
let postMatch = posts[postId].body.match(regex) let postMatch = posts[postId].body!.match(regex)
if (postMatch !== null) { if (postMatch !== null) {
parsedPosts.push({ ...posts[postId]}) parsedPosts.push({ ...posts[postId]})
} }
@@ -177,6 +186,14 @@ export class StreamComponent extends Component<IStreamComponentProps,IStreamComp
} }
/**
* Scroll loader
*/
scrollLoad = (page: number) => {
const {loadStream} = this.props
loadStream(page, 10)
}
componentWillMount () { componentWillMount () {
const {setHomeTitle} = this.props const {setHomeTitle} = this.props
setHomeTitle!() setHomeTitle!()
@@ -188,14 +205,18 @@ export class StreamComponent extends Component<IStreamComponentProps,IStreamComp
*/ */
render () { render () {
let postList: any = this.postLoad() const {tag, displayWriting, hasMorePosts } = this.props
const postList = this.postLoad() as {evenPostList: Post[], oddPostList: Post[], divided: boolean} | any
const {tag, displayWriting } = this.props
return ( return (
<div > <InfiniteScroll
pageStart={0}
loadMore={this.scrollLoad}
hasMore={hasMorePosts}
useWindow={true}
loader={ <LoadMoreProgressComponent />}
>
<div className='grid grid__gutters grid__1of2 grid__space-around animate-top'> <div className='grid grid__gutters grid__1of2 grid__space-around animate-top'>
<div className='grid-cell animate-top' style= {{maxWidth: '530px', minWidth: '280px'}}> <div className='grid-cell animate-top' style= {{maxWidth: '530px', minWidth: '280px'}}>
{displayWriting && !tag {displayWriting && !tag
? (<PostWriteComponent open={this.state.openPostWrite} onRequestClose={this.handleClosePostWrite} edit={false} > ? (<PostWriteComponent open={this.state.openPostWrite} onRequestClose={this.handleClosePostWrite} edit={false} >
@@ -225,9 +246,10 @@ export class StreamComponent extends Component<IStreamComponentProps,IStreamComp
</div> </div>
</div>) </div>)
: ''} : ''}
</div> </div>
</div> </InfiniteScroll>
) )
} }
} }
@@ -254,6 +276,7 @@ const mapDispatchToProps = (dispatch: any, ownProps: IStreamComponentProps) => {
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: IStreamComponentProps) => { const mapStateToProps = (state: any, ownProps: IStreamComponentProps) => {
const {post} = state
return { return {
avatar: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].avatar : '', avatar: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].avatar : '',
fullName: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].fullName : '' fullName: state.user.info && state.user.info[state.authorize.uid] ? state.user.info[state.authorize.uid].fullName : ''
@@ -261,4 +284,4 @@ const mapStateToProps = (state: any, ownProps: IStreamComponentProps) => {
} }
// - Connect component to redux store // - Connect component to redux store
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(StreamComponent as any)) export default withRouter(connect(mapStateToProps, mapDispatchToProps)(StreamComponent as any) as any)

View File

@@ -1,6 +1,6 @@
import { User } from 'core/domain/users' import { User } from 'core/domain/users'
import { Circle } from 'core/domain/circles/circle' import { Circle } from 'core/domain/circles/circle'
import { UserFollower } from 'core/domain/circles' import { UserTie } from 'core/domain/circles'
export interface IUserBoxComponentProps { export interface IUserBoxComponentProps {
@@ -18,7 +18,7 @@ export interface IUserBoxComponentProps {
* @type {User} * @type {User}
* @memberof IUserBoxComponentProps * @memberof IUserBoxComponentProps
*/ */
user: UserFollower user: UserTie
/** /**
* Circles * Circles
@@ -36,6 +36,11 @@ export interface IUserBoxComponentProps {
*/ */
userBelongCircles?: string[] userBelongCircles?: string[]
/**
* Whether current user followed this user
*/
isFollowed: boolean
/** /**
* The number of circles * The number of circles
* *
@@ -80,7 +85,7 @@ export interface IUserBoxComponentProps {
* *
* @memberof IUserBoxComponentProps * @memberof IUserBoxComponentProps
*/ */
addFollowingUser?: (cid: string,user: UserFollower) => any addFollowingUser?: (cid: string,user: UserTie) => any
/** /**
* Delete * Delete

View File

@@ -2,12 +2,20 @@
export interface IUserBoxComponentState { export interface IUserBoxComponentState {
/** /**
* Add new circle button is disabled {true} or not {false} * Create new circle button is disabled {true} or not {false}
* *
* @type {boolean} * @type {boolean}
* @memberof IUserBoxComponentState * @memberof IUserBoxComponentState
*/ */
disabledAddCircle: boolean disabledCreateCircle: boolean
/**
* The button of add user in a circle is disabled {true} or not {false}
*
* @type {boolean}
* @memberof IUserBoxComponentState
*/
disabledAddToCircle: boolean
/** /**
* Circle name * Circle name
@@ -32,4 +40,15 @@ export interface IUserBoxComponentState {
* @memberof IUserBoxComponentState * @memberof IUserBoxComponentState
*/ */
open: boolean open: boolean
/**
* Whether current user changed the selected circles for referer user
*
*/
disabledDoneCircles: boolean
/**
* Keep selected circles for refere user
*/
selectedCircles: string[]
} }

View File

@@ -11,12 +11,17 @@ import Menu from 'material-ui/Menu'
import MenuItem from 'material-ui/MenuItem' import MenuItem from 'material-ui/MenuItem'
import Checkbox from 'material-ui/Checkbox' import Checkbox from 'material-ui/Checkbox'
import TextField from 'material-ui/TextField' import TextField from 'material-ui/TextField'
import { Dialog } from 'material-ui'
import SvgAdd from 'material-ui/svg-icons/content/add'
import IconButton from 'material-ui/IconButton'
import { grey400, grey800, darkBlack, lightBlack } from 'material-ui/styles/colors'
// - Import app components // - Import app components
import UserAvatar from 'components/userAvatar' import UserAvatar from 'components/userAvatar'
// - Import API // - Import API
import CircleAPI from 'api/CircleAPI' import CircleAPI from 'api/CircleAPI'
import StringAPI from 'api/StringAPI'
// - Import actions // - Import actions
import * as circleActions from 'actions/circleActions' import * as circleActions from 'actions/circleActions'
@@ -24,21 +29,22 @@ import * as circleActions from 'actions/circleActions'
import { IUserBoxComponentProps } from './IUserBoxComponentProps' import { IUserBoxComponentProps } from './IUserBoxComponentProps'
import { IUserBoxComponentState } from './IUserBoxComponentState' import { IUserBoxComponentState } from './IUserBoxComponentState'
import { User } from 'core/domain/users' import { User } from 'core/domain/users'
import { UserFollower, Circle } from 'core/domain/circles' import { UserTie, Circle } from 'core/domain/circles'
import { ServerRequestType } from 'constants/serverRequestType'
/** /**
* Create component class * Create component class
*/ */
export class UserBoxComponent extends Component<IUserBoxComponentProps,IUserBoxComponentState> { export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBoxComponentState> {
static propTypes = { static propTypes = {
/** /**
* User identifier * User identifier
*/ */
userId: PropTypes.string, userId: PropTypes.string,
/** /**
* User information * User information
*/ */
user: PropTypes.object user: PropTypes.object
} }
@@ -53,207 +59,297 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps,IUserBoxC
}, },
followButton: { followButton: {
position: 'absolute', position: 'absolute',
bottom: '8px', bottom: '30px',
left: 0, left: 0,
right: 0 right: 0
},
dialog: {
width: '',
maxWidth: '280px',
borderRadius: '4px'
} }
} }
/** /**
* Component constructor * Component constructor
* @param {object} props is an object properties of component * @param {object} props is an object properties of component
*/ */
constructor (props: IUserBoxComponentProps) { constructor (props: IUserBoxComponentProps) {
super(props) super(props)
const { userBelongCircles, circles } = this.props
// Defaul state // Defaul state
this.state = { this.state = {
/** /**
* It will be true if user follow popover is open * It will be true if user follow popover is open
*/ */
open: false, open: false,
/** /**
* The value of circle input * The value of circle input
*/ */
circleName: '', circleName: '',
/** /**
* It will be true if the text field for adding group is empty * It will be true if the text field for adding group is empty
*/ */
disabledAddCircle: true disabledCreateCircle: true,
/**
* The button of add user in a circle is disabled {true} or not {false}
*/
disabledAddToCircle: true,
/**
* Keep selected circles for refere user
*/
selectedCircles: userBelongCircles ? userBelongCircles!.slice() : [],
/**
* Whether current user changed the selected circles for referer user
*/
disabledDoneCircles: true
} }
// Binding functions to `this` // Binding functions to `this`
this.handleChangeName = this.handleChangeName.bind(this) this.handleChangeName = this.handleChangeName.bind(this)
this.handleCreateCricle = this.handleCreateCricle.bind(this) this.onCreateCircle = this.onCreateCircle.bind(this)
this.handleFollowUser = this.handleFollowUser.bind(this)
this.handleFollowUser = this.handleFollowUser.bind(this) this.handleFollowUser = this.handleFollowUser.bind(this)
} }
handleFollowUser = (checked: boolean,cid: string) => { /**
const {userId,user} = this.props * Handle follow user
const {avatar,fullName} = user */
handleFollowUser = (checked: boolean, cid: string) => {
const { userId, user } = this.props
const { avatar, fullName } = user
if (checked) { if (checked) {
this.props.addFollowingUser!(cid,{avatar,userId,fullName}) this.props.addFollowingUser!(cid, { avatar, userId, fullName })
} else { } else {
this.props.deleteFollowingUser!(cid,userId) this.props.deleteFollowingUser!(cid, userId)
} }
} }
/** /**
* Handle create circle * Handle request close for add circle box
* */
* onRequestCloseAddCircle = () => {
* @memberof UserBox
*/
handleCreateCricle = () => {
const {circleName} = this.state
if (circleName && circleName.trim() !== '' ) {
this.props.createCircle!(this.state.circleName)
this.setState({
circleName: '',
disabledAddCircle: true
})
}
}
/**
* Handle change group name input to the state
*
*
* @memberof UserBox
*/
handleChangeName = (event: any) => {
this.setState({
circleName: event.target.value,
disabledAddCircle: (event.target.value === undefined || event.target.value.trim() === '')
})
}
/**
* Handle touch tab on follow popover
*
*
* @memberof UserBox
*/
handleTouchTap = (event: any) => {
// This prevents ghost click.
event.preventDefault()
this.setState({
open: true,
anchorEl: event.currentTarget
})
}
/**
* Handle close follow popover
*
*
* @memberof UserBox
*/
handleRequestClose = () => {
this.setState({ this.setState({
open: false open: false
}) })
} }
/**
* Handle request open for add circle box
*/
onRequestOpenAddCircle = () => {
this.setState({
open: true
})
}
/**
* Create a new circle
*/
onCreateCircle = () => {
const { circleName } = this.state
if (circleName && circleName.trim() !== '') {
this.props.createCircle!(this.state.circleName)
this.setState({
circleName: '',
disabledCreateCircle: true
})
}
}
/**
* Handle change group name input to the state
*/
handleChangeName = (event: any) => {
this.setState({
circleName: event.target.value,
disabledCreateCircle: (event.target.value === undefined || event.target.value.trim() === '')
})
}
handleSelectCircle = (event: object, isInputChecked: boolean, circleId: string) => {
const { userBelongCircles, circles } = this.props
let selectedCircles = this.state.selectedCircles
if (isInputChecked) {
selectedCircles = [
...selectedCircles,
circleId
]
} else {
const circleIndex = selectedCircles.indexOf(circleId)
selectedCircles.splice(circleIndex, 1)
}
this.setState({
selectedCircles: selectedCircles,
disabledDoneCircles: !this.selectedCircleChange(selectedCircles)
})
}
/**
* Handle follow user
*/
onFollowUser = (event: any) => {
// This prevents ghost click
event.preventDefault()
this.onRequestOpenAddCircle()
}
/**
* Add user to the circle/circles
*/
onAddToCircle = () => {
}
/**
* Create a circle list of user which belong to
*/
circleList = () => { circleList = () => {
let { circles, userId, userBelongCircles } = this.props let { circles, userId, userBelongCircles } = this.props
if (circles) { if (circles) {
return Object.keys(circles).map((key, index) => { return Object.keys(circles).map((circleId, index) => {
if (key.trim() !== '-Followers') { const {selectedCircles} = this.state
let isBelong = userBelongCircles!.indexOf(key) > -1 let isBelong = selectedCircles!.indexOf(circleId) > -1
// Create checkbox for selected/unselected circle
return <Checkbox return <Checkbox
key={key} key={circleId}
style={{ padding: '10px' }} style={{ padding: '10px' }}
label={circles![key].name} label={circles![circleId].name}
labelStyle={{ labelStyle={{
overflow: 'hidden', overflow: 'hidden',
textOverflow: 'ellipsis', textOverflow: 'ellipsis',
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
width: '100%' width: '100%'
}} }}
onCheck={(evt,checked) => this.handleFollowUser(checked,key)} onCheck={(event: object, isInputChecked: boolean) => this.handleSelectCircle(event, isInputChecked, circleId)}
checked={isBelong} checked={isBelong}
/> />
}
}) })
} }
} }
/** /**
* Reneder component DOM * Check if the the selected circles changed
* @return {react element} return the DOM which rendered by component */
*/ selectedCircleChange = (selectedCircles: string[]) => {
let isChanged = false
const { userBelongCircles, circles } = this.props
if (selectedCircles.length === userBelongCircles!.length) {
for (let circleIndex: number = 0; circleIndex < selectedCircles.length; circleIndex++) {
const selectedCircleId = selectedCircles[circleIndex]
if (!(userBelongCircles!.indexOf(selectedCircleId) > -1)) {
isChanged = true
break
}
}
} else {
isChanged = true
}
return isChanged
}
/**
* Reneder component DOM
* @return {react element} return the DOM which rendered by component
*/
render () { render () {
const {disabledDoneCircles} = this.state
const writeActions = [
<FlatButton
label='Cancel'
primary={true}
keyboardFocused={false}
onTouchTap={this.onRequestCloseAddCircle}
style={{ color: grey800 }}
/>,
<FlatButton
label='Done'
primary={true}
keyboardFocused={false}
disabled={disabledDoneCircles}
onTouchTap={this.onCreateCircle}
/>
]
const { isFollowed } = this.props
return ( return (
<Paper style={this.styles.paper} zDepth={1} className='grid-cell'> <Paper style={this.styles.paper} zDepth={1} className='grid-cell'>
<div style={{ <div style={{
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
alignItems: 'center', alignItems: 'center',
justifyContent: 'flex-start', justifyContent: 'flex-start',
height: '100%', height: '100%',
position: 'relative', position: 'relative',
padding: '30px' paddingTop: 20
}}> }}>
<div onClick={() => this.props.goTo!(`/${this.props.userId}`)} style={{cursor: 'pointer'}}> <div onClick={() => this.props.goTo!(`/${this.props.userId}`)} style={{ cursor: 'pointer' }}>
<UserAvatar <UserAvatar
fullName={this.props.fullName!} fullName={this.props.fullName!}
fileName={this.props.avatar!} fileName={this.props.avatar!}
size={90} size={90}
/> />
</div> </div>
<div onClick={() => this.props.goTo!(`/${this.props.userId}`)} className='people__name' style={{cursor: 'pointer'}}> <div onClick={() => this.props.goTo!(`/${this.props.userId}`)} className='people__name' style={{ cursor: 'pointer' }}>
<div> <div>
{this.props.user.fullName} {this.props.user.fullName}
</div> </div>
</div> </div>
<div style={this.styles.followButton as any}> <div style={this.styles.followButton as any}>
<FlatButton <FlatButton
label={(this.props.belongCirclesCount && this.props.belongCirclesCount < 1) ? 'Follow' label={!isFollowed ? 'Follow'
: (this.props.belongCirclesCount! > 1 ? `${this.props.belongCirclesCount} Circles` : ((this.props.firstBelongCircle) ? this.props.firstBelongCircle.name : 'Follow'))} : (this.props.belongCirclesCount! > 1 ? `${this.props.belongCirclesCount} Circles` : ((this.props.firstBelongCircle) ? this.props.firstBelongCircle.name : 'Follow'))}
primary={true} primary={true}
onTouchTap={this.handleTouchTap} onTouchTap={this.onFollowUser}
/> />
</div> </div>
</div> </div>
<Popover <Dialog
open={this.state.open} key={this.props.userId || 0}
anchorEl={this.state.anchorEl} actions={writeActions}
anchorOrigin={{ horizontal: 'left', vertical: 'bottom' }} modal={false}
targetOrigin={{ horizontal: 'left', vertical: 'top' }} open={this.state.open}
onRequestClose={this.handleRequestClose} contentStyle={this.styles.dialog}
animation={PopoverAnimationVertical} onRequestClose={this.onRequestCloseAddCircle}
> overlayStyle={{ background: 'rgba(0,0,0,0.12)' }}
<Menu > bodyStyle={{ padding: 0 }}
<div style={{ autoDetectWindowHeight={false}
position: 'relative', actionsContainerStyle={{ borderTop: '1px solid rgb(224, 224, 224)' }}
display: 'block',
maxHeight: '220px'
}}>
<div style={{ overflowY: 'auto', height: '100%' }}>
{this.circleList()}
</div> >
</div> <div style={{
<div style={{ padding: '10px' }}> position: 'relative',
<TextField display: 'block',
hintText='Group name' maxHeight: '220px'
onChange={this.handleChangeName} }}>
value={this.state.circleName} <div style={{ overflowY: 'auto', height: '100%' }}>
/><br /> {this.circleList()}
<FlatButton label='ADD' primary={true} disabled={this.state.disabledAddCircle} onClick={this.handleCreateCricle} />
</div> </div>
</Menu> </div>
</Popover> <div style={{ padding: '10px' }}>
</Paper> <TextField
hintText='Group name'
onChange={this.handleChangeName}
value={this.state.circleName}
/>
<div className='user-box__add-circle'>
<IconButton onClick={this.onCreateCircle} tooltip='Create circle' disabled={this.state.disabledCreateCircle}>
<SvgAdd />
</IconButton>
</div>
<br />
</div>
</Dialog>
</Paper>
) )
} }
} }
@@ -267,8 +363,8 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps,IUserBoxC
const mapDispatchToProps = (dispatch: Function, ownProps: IUserBoxComponentProps) => { const mapDispatchToProps = (dispatch: Function, ownProps: IUserBoxComponentProps) => {
return { return {
createCircle: (name: string) => dispatch(circleActions.dbAddCircle(name)), createCircle: (name: string) => dispatch(circleActions.dbAddCircle(name)),
addFollowingUser: (cid: string,user: UserFollower) => dispatch(circleActions.dbAddFollowingUser(cid,user)), addFollowingUser: (circleIds: string[], user: UserTie) => dispatch(circleActions.dbUpdateUserInCircles(circleIds, user)),
deleteFollowingUser: (cid: string ,followingId: string) => dispatch(circleActions.dbDeleteFollowingUser(cid,followingId)), deleteFollowingUser: (cid: string, followingId: string) => dispatch(circleActions.dbDeleteFollowingUser(followingId)),
goTo: (url: string) => dispatch(push(url)) goTo: (url: string) => dispatch(push(url))
} }
@@ -281,13 +377,17 @@ const mapDispatchToProps = (dispatch: Function, ownProps: IUserBoxComponentProps
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: IUserBoxComponentProps) => { const mapStateToProps = (state: any, ownProps: IUserBoxComponentProps) => {
const { uid } = state.authorize
const circles: {[circleId: string]: Circle} = state.circle ? (state.circle.userCircles[uid] || {}) : {}
const userBelongCircles = CircleAPI.getUserBelongCircles(circles,ownProps.userId)
const { circle, authorize, server } = state
const { uid } = authorize
const { request } = server
const circles: { [circleId: string]: Circle } = circle ? (circle.circleList || {}) : {}
const userBelongCircles = circle ? (circle.userTies[ownProps.userId] ? circle.userTies[ownProps.userId].circleIdList : []) : []
const isFollowed = userBelongCircles.length > 0
return { return {
circles: circles, isFollowed,
userBelongCircles: userBelongCircles || [], circles,
userBelongCircles,
belongCirclesCount: userBelongCircles.length || 0, belongCirclesCount: userBelongCircles.length || 0,
firstBelongCircle: userBelongCircles ? (circles ? circles[userBelongCircles[0]] : {}) : {}, firstBelongCircle: userBelongCircles ? (circles ? circles[userBelongCircles[0]] : {}) : {},
avatar: state.user.info && state.user.info[ownProps.userId] ? state.user.info[ownProps.userId].avatar || '' : '', avatar: state.user.info && state.user.info[ownProps.userId] ? state.user.info[ownProps.userId].avatar || '' : '',

View File

@@ -1,5 +1,5 @@
import { User } from 'core/domain/users' import { User } from 'core/domain/users'
import { UserFollower } from 'core/domain/circles' import { UserTie } from 'core/domain/circles'
export interface IUserBoxListComponentProps { export interface IUserBoxListComponentProps {
@@ -9,7 +9,7 @@ export interface IUserBoxListComponentProps {
* @type {{[userId: string]: User}} * @type {{[userId: string]: User}}
* @memberof IUserBoxListComponentProps * @memberof IUserBoxListComponentProps
*/ */
users: {[userId: string]: UserFollower} users: {[userId: string]: UserTie}
/** /**
* User identifier * User identifier

View File

@@ -8,6 +8,7 @@ import { List } from 'material-ui/List'
import CircleComponent from 'components/circle' import CircleComponent from 'components/circle'
import { IYourCirclesComponentProps } from './IYourCirclesComponentProps' import { IYourCirclesComponentProps } from './IYourCirclesComponentProps'
import { IYourCirclesComponentState } from './IYourCirclesComponentState' import { IYourCirclesComponentState } from './IYourCirclesComponentState'
import { Circle } from 'core/domain/circles';
// - Import API // - Import API
@@ -97,10 +98,12 @@ const mapDispatchToProps = (dispatch: Function, ownProps: IYourCirclesComponentP
* @return {object} props of component * @return {object} props of component
*/ */
const mapStateToProps = (state: any, ownProps: IYourCirclesComponentProps) => { const mapStateToProps = (state: any, ownProps: IYourCirclesComponentProps) => {
const {circle, authorize, server} = state
const { uid } = state.authorize const { uid } = state.authorize
const circles: { [circleId: string]: Circle } = circle ? (circle.circleList || {}) : {}
return { return {
uid, uid,
circles: state.circle ? state.circle.userCircles[uid] : {} circles
} }
} }

View File

@@ -1,5 +1,5 @@
export enum CircleActionType { export enum CircleActionType {
ADD_CIRCLE = 'ADD_CIRCLE', ADD_CIRCLE = 'ADD_CIRCLE',
DELETE_CIRCLE = 'DELETE_CIRCLE', DELETE_CIRCLE = 'DELETE_CIRCLE',
UPDATE_CIRCLE = 'UPDATE_CIRCLE', UPDATE_CIRCLE = 'UPDATE_CIRCLE',
@@ -9,4 +9,13 @@ export enum CircleActionType {
CLOSE_CIRCLE_SETTINGS = 'CLOSE_CIRCLE_SETTINGS', CLOSE_CIRCLE_SETTINGS = 'CLOSE_CIRCLE_SETTINGS',
ADD_FOLLOWING_USER = 'ADD_FOLLOWING_USER', ADD_FOLLOWING_USER = 'ADD_FOLLOWING_USER',
DELETE_FOLLOWING_USER = 'DELETE_FOLLOWING_USER', DELETE_FOLLOWING_USER = 'DELETE_FOLLOWING_USER',
} UPDATE_USER_TIE = 'UPDATE_USER_TIE',
ADD_USER_TIE_LIST = 'ADD_USER_TIE_LIST',
ADD_USER_TIED_LIST = 'ADD_USER_TIED_LIST',
DELETE_USER_FROM_CIRCLE = 'DELETE_USER_FROM_CIRCLE',
SHOW_SELECT_CIRCLE_BOX = 'SHOW_SELECT_CIRCLE_BOX',
HIDE_SELECT_CIRCLE_BOX = 'HIDE_SELECT_CIRCLE_BOX',
SHOW_FOLLOWING_USER_LOADING = 'SHOW_FOLLOWING_USER_LOADING',
HIDE_FOLLOWING_USER_LOADING = 'HIDE_FOLLOWING_USER_LOADING'
}

View File

@@ -1,6 +1,14 @@
export enum PostActionType { export enum PostActionType {
ADD_IMAGE_POST = 'ADD_IMAGE_POST', ADD_IMAGE_POST = 'ADD_IMAGE_POST',
HAS_MORE_DATA_STREAM = 'HAS_MORE_DATA_STREAM',
NOT_MORE_DATA_STREAM = 'NOT_MORE_DATA_STREAM',
REQUEST_PAGE_STREAM = 'REQUEST_PAGE_STREAM',
LAST_POST_STREAM = 'LAST_POST_STREAM',
HAS_MORE_DATA_PROFILE = 'HAS_MORE_DATA_PROFILE',
NOT_MORE_DATA_PROFILE = 'NOT_MORE_DATA_PROFILE',
REQUEST_PAGE_PROFILE = 'REQUEST_PAGE_PROFILE',
LAST_POST_PROFILE = 'LAST_POST_PROFILE',
ADD_VIDEO_POST = 'ADD_VIDEO_POST', ADD_VIDEO_POST = 'ADD_VIDEO_POST',
ADD_POST = 'ADD_POST', ADD_POST = 'ADD_POST',
UPDATE_POST = 'UPDATE_POST', UPDATE_POST = 'UPDATE_POST',
@@ -8,4 +16,3 @@
ADD_LIST_POST = 'ADD_LIST_POST', ADD_LIST_POST = 'ADD_LIST_POST',
CLEAR_ALL_DATA_POST = 'CLEAR_ALL_DATA_POST' CLEAR_ALL_DATA_POST = 'CLEAR_ALL_DATA_POST'
} }

View File

@@ -0,0 +1,8 @@
export enum ServerActionType {
ADD_REQUEST = 'ADD_REQUEST',
DELETE_REQUEST = 'DELETE_REQUEST',
ERROR_REQUEST = 'ERROR_REQUEST',
OK_REQUEST = 'OK_REQUEST',
CLEAR_ALL_DATA_REQUEST = 'CLEAR_ALL_DATA_REQUEST'
}

View File

@@ -0,0 +1,6 @@
export enum ServerRequestType {
CircleAddToCircle = 'CircleAddToCircle',
CircleFollowUser = 'CircleFollowUser',
CircleCreateTieUser = 'CircleCreateTieUser'
}

View File

@@ -7,5 +7,9 @@ export enum UserActionType {
CLEAR_ALL_DATA_USER = 'CLEAR_ALL_DATA_USER', CLEAR_ALL_DATA_USER = 'CLEAR_ALL_DATA_USER',
OPEN_EDIT_PROFILE = 'OPEN_EDIT_PROFILE', OPEN_EDIT_PROFILE = 'OPEN_EDIT_PROFILE',
CLOSE_EDIT_PROFILE = 'CLOSE_EDIT_PROFILE', CLOSE_EDIT_PROFILE = 'CLOSE_EDIT_PROFILE',
ADD_PEOPLE_INFO = 'ADD_PEOPLE_INFO' ADD_PEOPLE_INFO = 'ADD_PEOPLE_INFO',
} HAS_MORE_DATA_PEOPLE = 'HAS_MORE_DATA_PEOPLE',
NOT_MORE_DATA_PEOPLE = 'NOT_MORE_DATA_PEOPLE',
REQUEST_PAGE_PEOPLE = 'REQUEST_PAGE_PEOPLE',
LAST_USER_PEOPLE = 'LAST_POST_PEOPLE'
}

View File

@@ -1,4 +1,3 @@
import { UserFollower } from './userFollower'
import { BaseDomain } from 'core/domain/common' import { BaseDomain } from 'core/domain/common'
export class Circle extends BaseDomain { export class Circle extends BaseDomain {
@@ -35,12 +34,9 @@ export class Circle extends BaseDomain {
*/ */
public name: string public name: string
/** /**
* The users in a circle * Whether circle setting is open
* */
* @type {string} public openCircleSettings?: boolean
* @memberof User
*/
public users: {[userId: string]: UserFollower}
} }

View File

@@ -1,7 +1,7 @@
import {Circle} from './circle' import { Circle } from './circle'
import {UserFollower} from './userFollower' import { UserTie } from './UserTie'
export { export {
Circle, Circle,
UserFollower UserTie
} }

View File

@@ -1,14 +1,15 @@
import { BaseDomain } from 'core/domain/common' import { BaseDomain } from 'core/domain/common'
export class UserFollower extends BaseDomain { export class UserTie extends BaseDomain {
constructor (
/** /**
* User identifier * User identifier
* *
* @type {string} * @type {string}
* @memberof UserFollower * @memberof UserTie
*/ */
userId?: string public userId?: string,
/** /**
* Circle creation date time * Circle creation date time
@@ -16,30 +17,38 @@ export class UserFollower extends BaseDomain {
* @type {Date} * @type {Date}
* @memberof Circle * @memberof Circle
*/ */
public creationDate?: number public creationDate?: number,
/** /**
* User full name * User full name
* *
* @type {string} * @type {string}
* @memberof UserFollower * @memberof UserTie
*/ */
public fullName: string public fullName?: string,
/** /**
* Avatar URL address * Avatar URL address
* *
* @type {string} * @type {string}
* @memberof UserFollower * @memberof UserTie
*/ */
public avatar: string public avatar?: string,
/** /**
* If following user approved {true} or not {false} * If following user approved {true} or not {false}
* *
* @type {Boolean} * @type {Boolean}
* @memberof UserFollower * @memberof UserTie
*/ */
public approved?: Boolean public approved?: Boolean,
/**
* List of circles identifire which this user belong to
*/
public circleIdList?: string[]
) {
super()
}
} }

View File

@@ -0,0 +1,62 @@
import { BaseDomain } from 'core/domain/common'
export class Graph extends BaseDomain {
constructor (
/**
* Left node of graph
*
* @type {string}
* @memberof Graph
*/
public leftNode: string,
/**
* Graph relationship type
*
* @type {number}
* @memberof Graph
*/
public edgeType: string,
/**
* Right node of graph
*
* @type {string}
* @memberof Graph
*/
public rightNode: string,
/**
* Graph left node metadata
*
* @memberof Graph
*/
public LeftMetadata: any,
/**
* Graph right node metadata
*
* @memberof Graph
*/
public rightMetadata: any,
/**
* Graph metadata
*
* @type {string}
* @memberof Graph
*/
public graphMetadata: any,
/**
* Graph node identifier
*
* @type {string}
* @memberof Graph
*/
public nodeId?: string
) { super() }
}

View File

@@ -0,0 +1,5 @@
import {Graph} from './graph'
export {
Graph
}

View File

@@ -1,53 +1,53 @@
import { BaseDomain } from 'core/domain/common' import { BaseDomain } from 'core/domain/common'
export class User extends BaseDomain { export class User extends BaseDomain {
/** /**
* Full name of user * Full name of user
* *
* @type {string} * @type {string}
* @memberof User * @memberof User
*/ */
public fullName: string public fullName: string
/** /**
* User avatar address * User avatar address
* *
* @type {string} * @type {string}
* @memberof User * @memberof User
*/ */
public avatar: string public avatar: string
/** /**
* Email of the user * Email of the user
* *
* @type {string} * @type {string}
* @memberof User * @memberof User
*/ */
public email?: string | null public email?: string | null
/** /**
* Password of the user * Password of the user
* *
* @type {string} * @type {string}
* @memberof User * @memberof User
*/ */
public password?: string | null public password?: string | null
/** /**
* User identifier * User identifier
* *
* @type {string} * @type {string}
* @memberof User * @memberof User
*/ */
public userId?: string | null public userId?: string | null
/** /**
* User creation date * User creation date
* *
* @type {number} * @type {number}
* @memberof User * @memberof User
*/ */
public creationDate: number public creationDate: number
} }

View File

@@ -2,14 +2,6 @@ import { BaseDomain } from 'core/domain/common'
export class Vote extends BaseDomain { export class Vote extends BaseDomain {
/**
* Vote identifier
*
* @type {string}
* @memberof Vote
*/
public id?: string | null
/** /**
* Post identifire which vote on * Post identifire which vote on
* *

View File

@@ -1,6 +1,7 @@
//#region Interfaces //#region Interfaces
import { IServiceProvider } from 'core/factories' import { IServiceProvider } from 'core/factories'
import { injectable } from 'inversify'
import { import {
IAuthorizeService, IAuthorizeService,
ICircleService, ICircleService,
@@ -33,7 +34,7 @@ import {
} from 'data/firestoreClient/services' } from 'data/firestoreClient/services'
//#endregion //#endregion
@injectable()
export class ServiceProvide implements IServiceProvider { export class ServiceProvide implements IServiceProvider {
/** /**

View File

@@ -1,5 +1,5 @@
import { User } from 'core/domain/users' import { User } from 'core/domain/users'
import { Circle, UserFollower } from 'core/domain/circles' import { Circle, UserTie } from 'core/domain/circles'
/** /**
* Circle service interface * Circle service interface
@@ -10,8 +10,6 @@ import { Circle, UserFollower } from 'core/domain/circles'
export interface ICircleService { export interface ICircleService {
addCircle: (userId: string, circle: Circle) => Promise<string> addCircle: (userId: string, circle: Circle) => Promise<string>
addFollowingUser: (userId: string, circleId: string, userCircle: User, userFollower: UserFollower, userFollowingId: string) => Promise<void>
deleteFollowingUser: (userId: string, circleId: string,userFollowingId: string) => Promise<void>
updateCircle: (userId: string, circleId: string, circle: Circle) => Promise<void> updateCircle: (userId: string, circleId: string, circle: Circle) => Promise<void>
deleteCircle: (userId: string, circleId: string) => Promise<void> deleteCircle: (userId: string, circleId: string) => Promise<void>
getCircles: (userId: string) => Promise<{ [circleId: string]: Circle }> getCircles: (userId: string) => Promise<{ [circleId: string]: Circle }>

View File

@@ -0,0 +1,35 @@
import { User, Profile } from 'core/domain/users'
import { UserTie } from 'core/domain/circles'
/**
* User tie service interface
*
* @export
* @interface IUserTieService
*/
export interface IUserTieService {
/**
* Tie users
*/
tieUseres: (userTieSenderInfo: UserTie, userTieReceiveInfo: UserTie, circleIds: string[])
=> Promise<void>
/**
* Remove users' tie
*/
removeUsersTie: (firstUserId: string, secondUserId: string)
=> Promise<void>
/**
* Get user ties
*/
getUserTies: (userId: string)
=> Promise<{[userId: string]: UserTie}>
/**
* Get the users who tied current user
*/
getUserTieSender: (userId: string)
=> Promise<{[userId: string]: UserTie}>
}

View File

@@ -1,5 +1,7 @@
import { ICircleService } from './ICircleService' import { ICircleService } from './ICircleService'
import { IUserTieService } from './IUserTieService'
export { export {
ICircleService ICircleService,
IUserTieService
} }

View File

@@ -13,6 +13,6 @@ export interface ICommentService {
addComment: (comment: Comment) => Promise<string> addComment: (comment: Comment) => Promise<string>
getComments: (postId: string, callback: (resultComments: { [postId: string]: { [commentId: string]: Comment } }) => void) => void getComments: (postId: string, callback: (resultComments: { [postId: string]: { [commentId: string]: Comment } }) => void) => void
updateComment: (comment: Comment) => Promise<void> updateComment: (comment: Comment) => Promise<void>
deleteComment: (commentId: string, postId: string) => Promise<void> deleteComment: (commentId: string) => Promise<void>
} }

View File

@@ -11,6 +11,17 @@ export interface IPostService {
addPost: (post: Post) => Promise<string> addPost: (post: Post) => Promise<string>
updatePost: (post: Post) => Promise<void> updatePost: (post: Post) => Promise<void>
deletePost: (postId: string) => Promise<void> deletePost: (postId: string) => Promise<void>
getPosts: (userId: string) => Promise<{ [postId: string]: Post }> getPosts: (currentUserId: string,lastPostId: string, page: number, limit: number)
=> Promise<{posts: {[postId: string]: Post }[], newLastPostId: string}>
/**
* Get list of post by user identifier
*/
getPostsByUserId: (userId: string, lastPostId?: string, page?: number, limit?: number)
=> Promise<{ posts: { [postId: string]: Post }[], newLastPostId: string }>
/**
* Get post by the post identifier
*/
getPostById: (postId: string) => Promise<Post> getPostById: (postId: string) => Promise<Post>
} }

View File

@@ -9,5 +9,6 @@ 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, lastKey?: string, numberOfItems?: number) => Promise<{[userId: string]: Profile}> getUsersProfile: (userId: string, lastUserId?: string, page?: number, limit?: number)
=> Promise<{ users: { [userId: string]: Profile }[], newLastUserId: string }>
} }

View File

@@ -10,5 +10,5 @@ import { Vote } from 'core/domain/votes'
export interface IVoteService { export interface IVoteService {
addVote: (vote: Vote) => Promise<string> addVote: (vote: Vote) => Promise<string>
getVotes: (postId: string) => Promise<{[postId: string]: {[voteId: string]: Vote}}> getVotes: (postId: string) => Promise<{[postId: string]: {[voteId: string]: Vote}}>
deleteVote: (vote: Vote) => Promise<void> deleteVote: (userId: string, voteId: string) => Promise<void>
} }

View File

@@ -0,0 +1,17 @@
/**
* InversifyJS need to use the type as identifiers at runtime.
* We use symbols as identifiers but you can also use classes and or string literals.
*/
export const SocialProviderTypes = {
AuthorizeService: Symbol('AuthorizeService'),
UserTieService: Symbol('UserTieService'),
CircleService: Symbol('CircleService'),
CommentService: Symbol('CommentService'),
CommonService: Symbol('CommonService'),
StorageService: Symbol('StorageService'),
ImageGalleryService: Symbol('ImageGalleryService'),
NotificationService: Symbol('NotificationService'),
PostService: Symbol('PostService'),
UserService: Symbol('UserService'),
VoteService: Symbol('VoteService')
}

View File

@@ -1,27 +0,0 @@
import firebase from 'firebase'
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 initializer==============')
console.log(error)
console.log('====================================')
}
// - Storage reference
export let storageRef = firebase.storage().ref()
// - Database authorize
export let firebaseAuth = firebase.auth
export let firebaseRef = firebase.database().ref()
// - Firebase default
export default firebase

View File

@@ -1,266 +0,0 @@
import moment from 'moment'
import { Profile } from 'core/domain/users'
// - Import react components
import { firebaseRef, firebaseAuth } from 'data/firebaseClient'
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'
/**
* 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, provider, providerId} = credential
this.storeUserProviderData(uid,email,displayName,photoURL,providerId,provider,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) => {
firebaseRef.child(`users/${userId}/info`)
.set(new Profile(
avatar,
fullName,
'',
'',
moment().unix(),
email
))
.then((result) => {
resolve(new RegisterUserResult(userId))
})
.catch((error: any) => reject(new SocialError(error.name, error.message)))
})
}
/**
* Store user provider information
*
* @private
* @memberof AuthorizeService
*/
private storeUserProviderData = (
userId: string,
email: string,
fullName: string,
avatar: string,
providerId: string,
provider: string,
accessToken: string
) => {
return new Promise<RegisterUserResult>((resolve,reject) => {
firebaseRef.child(`users/${userId}/providerInfo`)
.set(new UserProvider(
userId,
email,
fullName,
avatar,
providerId,
provider,
accessToken
))
.then((result) => {
resolve(new RegisterUserResult(userId))
})
.catch((error: any) => reject(new SocialError(error.name, error.message)))
})
}
}

View File

@@ -1,5 +0,0 @@
import { AuthorizeService } from './AuthorizeService'
export {
AuthorizeService
}

View File

@@ -1,118 +0,0 @@
// - Import react components
import { firebaseRef, firebaseAuth, db } from 'data/firebaseClient'
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 = firebaseRef.child(`users/${userId}/circles`).push(circle)
circleRef.then(() => {
resolve(circleRef.key 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) => {
let updates: any = {}
updates[`users/${userId}/circles/${circleId}/users/${userFollowingId}`] = userCircle
updates[`users/${userFollowingId}/circles/-Followers/users/${userId}`] = userFollower
firebaseRef.update(updates).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) => {
let updates: any = {}
updates[`users/${userId}/circles/${circleId}/users/${userFollowingId}`] = null
updates[`users/${userFollowingId}/circles/-Followers/users/${userId}`] = null
firebaseRef.update(updates).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) => {
let updates: any = {}
updates[`users/${userId}/circles/${circleId}`] = circle
firebaseRef.update(updates).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) => {
let updates: any = {}
updates[`users/${userId}/circles/${circleId}`] = null
firebaseRef.update(updates).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: any = firebaseRef.child(`users/${userId}/circles`)
circlesRef.once('value').then((snapshot: any) => {
let circles: any = snapshot.val() || {}
let parsedCircles: { [circleId: string]: Circle } = {}
Object.keys(circles).forEach((circleId) => {
parsedCircles[circleId] = {
id: circleId,
...circles[circleId]
}
})
resolve(parsedCircles)
})
.catch((error: any) => {
reject(new SocialError(error.code, error.message))
})
})
}
}

View File

@@ -1,5 +0,0 @@
import { CircleService } from './CircleService'
export {
CircleService
}

View File

@@ -1,69 +0,0 @@
// - Import react components
import { firebaseRef, firebaseAuth } from 'data/firebaseClient'
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: (postId: string, comment: Comment)
=> Promise<string> = (postId, comment) => {
return new Promise<string>((resolve,reject) => {
let commentRef: any = firebaseRef.child(`postComments/${postId}`).push(comment)
commentRef.then(() => {
resolve(commentRef.key)
})
.catch((error: any) => {
reject(new SocialError(error.code,error.message))
})
})
}
public getComments: (callback: (resultComments: { [postId: string]: { [commentId: string]: Comment } }) => void)
=> void = (callback) => {
let commentsRef: any = firebaseRef.child(`postComments`)
commentsRef.on('value', (snapshot: any) => {
let comments: {[postId: string]: {[commentId: string]: Comment}} = snapshot!.val() || {}
callback(comments)
})
}
public updateComment: (commentId: string, postId: string, comment: Comment)
=> Promise<void> = (commentId, postId, comment) => {
return new Promise<void>((resolve,reject) => {
let updates: any = {}
updates[`postComments/${postId}/${commentId}`] = comment
firebaseRef.update(updates)
.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) => {
let updates: any = {}
updates[`postComments/${postId}/${commentId}`] = null
firebaseRef.update(updates)
.then(() => {
resolve()
})
.catch((error: any) => {
reject(new SocialError(error.code,error.message))
})
})
}
}

View File

@@ -1,5 +0,0 @@
import { CommentService } from './CommentService'
export {
CommentService
}

View File

@@ -1,16 +0,0 @@
// - 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 {
}

View File

@@ -1,5 +0,0 @@
import { CommonService } from './CommonService'
export {
CommonService
}

View File

@@ -1,41 +0,0 @@
import { storageRef } from 'data/firebaseClient'
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)
})
})
}
}

View File

@@ -1,5 +0,0 @@
import { StorageService } from './StorageService'
export {
StorageService
}

View File

@@ -1,112 +0,0 @@
import { FileResult } from 'models/files/fileResult'
// - Import react components
import { firebaseRef, firebaseAuth, storageRef } from 'data/firebaseClient'
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: any = firebaseRef.child(`userFiles/${userId}/files/images`)
imagesRef.once('value').then((snapshot: any) => {
let images = snapshot.val() || {}
let parsedImages: Image[] = []
Object.keys(images).forEach((imageId) => {
parsedImages.push({
id: imageId,
...images[imageId]
})
})
resolve(parsedImages)
})
.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 = firebaseRef.child(`userFiles/${userId}/files/images`).push(image)
imageRef.then(() => {
resolve(imageRef.key!)
})
.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) => {
let updates: any = {}
updates[`userFiles/${userId}/files/images/${imageId}`] = null
firebaseRef.update(updates).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))
})
})
}
}

View File

@@ -1,5 +0,0 @@
import { ImageGalleryService } from './ImageGalleryService'
export {
ImageGalleryService
}

View File

@@ -1,24 +0,0 @@
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
}

View File

@@ -1,5 +0,0 @@
import { NotificationService } from './NotificationService'
export {
NotificationService
}

View File

@@ -1,69 +0,0 @@
// - Import react components
import { firebaseRef, firebaseAuth } from 'data/firebaseClient'
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) => {
firebaseRef.child(`userNotifies/${notification.notifyRecieverUserId}`)
.push(notification)
.then(() => {
resolve()
})
.catch((error: any) => {
reject(new SocialError(error.code, error.message))
})
})
}
public getNotifications: (userId: string, callback: (resultNotifications: {[notifyId: string]: Notification}) => void)
=> void = (userId,callback) => {
let notificationsRef = firebaseRef.child(`userNotifies/${userId}`)
notificationsRef.on('value', (snapshot: any) => {
let notifications: {[notifyId: string]: Notification} = snapshot.val() || {}
callback(notifications)
})
}
public deleteNotification: (notificationId: string, userId: string)
=> Promise < void > = (notificationId, userId) => {
return new Promise<void>((resolve, reject) => {
let updates: any = {}
updates[`userNotifies/${userId}/${notificationId}`] = null
firebaseRef.update(updates)
.then(() => {
resolve()
})
.catch((error: any) => {
reject(new SocialError(error.code, error.message))
})
})
}
public setSeenNotification: (notificationId: string, userId: string, notification: Notification)
=> Promise <void> = (notificationId, userId, notification) => {
return new Promise<void>((resolve, reject) => {
let updates: any = {}
updates[`userNotifies/${userId}/${notificationId}`] = notification
firebaseRef.update(updates)
.then(() => {
resolve()
})
.catch((error: any) => {
reject(new SocialError(error.code, error.message))
})
})
}
}

View File

@@ -1,101 +0,0 @@
// - Import react components
import { firebaseRef, firebaseAuth } from 'data/firebaseClient'
import { SocialError } from 'core/domain/common'
import { Post } from 'core/domain/posts'
import { IPostService } from 'core/services/posts'
/**
* Firbase post service
*
* @export
* @class PostService
* @implements {IPostService}
*/
export class PostService implements IPostService {
public addPost: (userId: string, post: Post)
=> Promise<string> = (userId, post) => {
return new Promise<string>((resolve,reject) => {
let postRef: any = firebaseRef.child(`users/${userId}/posts`).push(post)
postRef.then(() => {
resolve(postRef.key)
})
.catch((error: any) => {
reject(new SocialError(error.code,error.message))
})
})
}
public updatePost: (userId: string, postId: string, post: Post)
=> Promise<void> = (userId, postId, post) => {
return new Promise<void>((resolve,reject) => {
let updates: any = {}
updates[`users/${userId}/posts/${postId}`] = post
firebaseRef.update(updates).then(() => {
resolve()
})
.catch((error: any) => {
reject(new SocialError(error.code,error.message))
})
})
}
public deletePost: (userId: string, postId: string)
=> Promise<void> = (userId, postId) => {
return new Promise<void>((resolve,reject) => {
let updates: any = {}
updates[`users/${userId}/posts/${postId}`] = null
firebaseRef.update(updates).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: any = firebaseRef.child(`users/${userId}/posts`)
postsRef.once('value').then((snapshot: any) => {
let posts: any = snapshot.val() || {}
let parsedPosts: { [postId: string]: Post } = {}
Object.keys(posts).forEach((postId) => {
parsedPosts[postId] = {
id: postId,
...posts[postId]
}
})
resolve(parsedPosts)
})
.catch((error: any) => {
reject(new SocialError(error.code,error.message))
})
})
}
public getPostById: (userId: string, postId: string)
=> Promise<Post> = (userId, postId) => {
return new Promise<Post>((resolve,reject) => {
let postsRef: any = firebaseRef.child(`users/${userId}/posts/${postId}`)
postsRef.once('value').then((snapshot: any) => {
let newPost = snapshot.val() || {}
let post: Post = {
id: postId,
...newPost
}
resolve(post)
})
.catch((error: any) => {
reject(new SocialError(error.code,error.message))
})
})
}
}

View File

@@ -1,5 +0,0 @@
import { PostService } from './PostService'
export {
PostService
}

View File

@@ -1,109 +0,0 @@
// - Import react components
import { firebaseRef, firebaseAuth } from 'data/firebaseClient'
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: any = firebaseRef.child(`users/${userId}/info`)
userProfileRef.once('value').then((snapshot: any) => {
let userProfile: Profile = snapshot.val() || {}
if (Object.keys(userProfile).length === 0 && userProfile.constructor === Object) {
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(userProfile)
}
})
.catch((error: any) => {
reject(new SocialError(error.code, error.message))
})
})
}
public updateUserProfile: (userId: string, profile: Profile)
=> Promise<void> = (userId, profile) => {
return new Promise<void>((resolve, reject) => {
let updates: any = {}
updates[`users/${userId}/info`] = profile
firebaseRef.update(updates).then(() => {
resolve()
})
.catch((error: any) => {
reject(new SocialError(error.code, error.message))
})
})
}
public getUsersProfile: (userId: string, page?: number, lastKey?: string)
=> Promise<{ [userId: string]: Profile }> = (userId, page, lastKey) => {
return new Promise<{ [userId: string]: Profile }>((resolve, reject) => {
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) => {
let usersProfile: any = snapshot.val() || {}
let parsedusersProfile: { [userId: string]: Profile } = {}
Object.keys(usersProfile).forEach((userKey) => {
if (userId !== userKey) {
let userInfo = usersProfile[userKey].info
parsedusersProfile[userKey] = {
avatar: userInfo.avatar,
email: userInfo.email,
fullName: userInfo.fullName,
banner: userInfo.banner,
tagLine: userInfo.tagLine
}
}
})
resolve(parsedusersProfile)
})
.catch((error: any) => {
reject(new SocialError(error.code, error.message))
})
})
}
private getUserProviderData = (userId: string) => {
return new Promise<UserProvider>((resolve,reject) => {
let userProviderRef: any = firebaseRef.child(`users/${userId}/providerInfo`)
userProviderRef.once('value').then((snapshot: any) => {
let userProviderRef: any = firebaseRef.child(`users/${userId}/info`)
let userProvider: UserProvider = snapshot.val() || {}
console.log('----------------userProfile')
console.log(userProvider)
console.log('-----------------------')
resolve(userProvider)
})
.catch((error: any) => {
reject(new SocialError(error.code, error.message))
})
})
}
}

View File

@@ -1,5 +0,0 @@
import { UserService } from './UserService'
export {
UserService
}

View File

@@ -1,57 +0,0 @@
// - Import react components
import { firebaseRef, firebaseAuth } from 'data/firebaseClient'
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) => {
let voteRef = firebaseRef.child(`postVotes/${vote.postId}`)
.push(vote)
voteRef.then(() => {
resolve(voteRef.key!)
})
.catch((error: any) => {
reject(new SocialError(error.code,error.message))
})
})
}
public getVotes: ()
=> Promise<{ [postId: string]: { [voteId: string]: Vote } }> = () => {
return new Promise<{ [postId: string]: { [voteId: string]: Vote } }>((resolve,reject) => {
let votesRef: any = firebaseRef.child(`postVotes`)
votesRef.on('value',(snapshot: any) => {
let postVotes: {[postId: string]: {[voteId: string]: Vote}} = snapshot.val() || {}
resolve(postVotes)
})
})
}
public deleteVote: (voteId: string, postId: string)
=> Promise<void> = (voteId, postId) => {
return new Promise<void>((resolve,reject) => {
let updates: any = {}
updates[`postVotes/${postId}/${voteId}`] = null
firebaseRef.update(updates).then(() => {
resolve()
})
.catch((error: any) => {
reject(new SocialError(error.code,error.message))
})
})
}
}

View File

@@ -0,0 +1,49 @@
import { GraphService } from './services/graphs/GraphService'
import { IGraphService } from './services/graphs/IGraphService'
import { VoteService } from './services/votes/VoteService'
import { PostService } from './services/posts/PostService'
import { StorageService } from './services/files/StorageService'
import { CommonService } from './services/common/CommonService'
import { CommentService } from './services/comments/CommentService'
import { ICircleService } from 'core/services/circles/ICircleService'
import { Container } from 'inversify'
import { IUserService } from 'core/services/users'
import { SocialProviderTypes } from 'core/socialProviderTypes'
import { UserService } from './services/users/UserService'
import { IAuthorizeService } from 'core/services/authorize'
import { AuthorizeService } from './services/authorize/AuthorizeService'
import { CircleService } from './services/circles/CircleService'
import { ICommentService } from 'core/services/comments'
import { ICommonService } from 'core/services/common'
import { IStorageService } from 'core/services/files'
import { IImageGalleryService } from 'core/services/imageGallery'
import { INotificationService } from 'core/services/notifications'
import { IPostService } from 'core/services/posts'
import { IVoteService } from 'core/services/votes'
import { ImageGalleryService } from './services/imageGallery/ImageGalleryService'
import { NotificationService } from './services/notifications/NotificationService'
import { FirestoreClientTypes } from './firestoreClientTypes'
import { IUserTieService } from 'core/services/circles'
import { UserTieService } from './services/circles/UserTieService'
/**
* Register firestore client dependecies
* @param container DI container
*/
export const useFirestore = (container: Container) => {
container.bind<IAuthorizeService>(SocialProviderTypes.AuthorizeService).to(AuthorizeService)
container.bind<ICircleService>(SocialProviderTypes.CircleService).to(CircleService)
container.bind<ICommentService>(SocialProviderTypes.CommentService).to(CommentService)
container.bind<ICommonService>(SocialProviderTypes.CommonService).to(CommonService)
container.bind<IStorageService>(SocialProviderTypes.StorageService).to(StorageService)
container.bind<IImageGalleryService>(SocialProviderTypes.ImageGalleryService).to(ImageGalleryService)
container.bind<INotificationService>(SocialProviderTypes.NotificationService).to(NotificationService)
container.bind<IPostService>(SocialProviderTypes.PostService).to(PostService)
container.bind<IUserService>(SocialProviderTypes.UserService).to(UserService)
container.bind<IVoteService>(SocialProviderTypes.VoteService).to(VoteService)
container.bind<IGraphService>(FirestoreClientTypes.GraphService).to(GraphService)
container.bind<IUserTieService>(SocialProviderTypes.UserTieService).to(UserTieService)
}

View File

@@ -0,0 +1,7 @@
/**
* InversifyJS need to use the type as identifiers at runtime.
* We use symbols as identifiers but you can also use classes and or string literals.
*/
export const FirestoreClientTypes = {
GraphService: Symbol('GraphService')
}

View File

@@ -10,6 +10,7 @@ import { SocialError } from 'core/domain/common'
import { OAuthType } from 'core/domain/authorize/oauthType' import { OAuthType } from 'core/domain/authorize/oauthType'
import moment from 'moment' import moment from 'moment'
import { injectable } from 'inversify'
/** /**
* Firbase authorize service * Firbase authorize service
* *
@@ -17,6 +18,7 @@ import moment from 'moment'
* @class AuthorizeService * @class AuthorizeService
* @implements {IAuthorizeService} * @implements {IAuthorizeService}
*/ */
@injectable()
export class AuthorizeService implements IAuthorizeService { export class AuthorizeService implements IAuthorizeService {
/** /**
@@ -215,6 +217,8 @@ export class AuthorizeService implements IAuthorizeService {
return new Promise<RegisterUserResult>((resolve,reject) => { return new Promise<RegisterUserResult>((resolve,reject) => {
db.doc(`userInfo/${userId}`).set( db.doc(`userInfo/${userId}`).set(
{ {
id: userId,
state: 'active',
avatar, avatar,
fullName, fullName,
creationDate: moment().unix(), creationDate: moment().unix(),

View File

@@ -3,8 +3,9 @@ import { firebaseRef, firebaseAuth, db } from 'data/firestoreClient'
import { SocialError } from 'core/domain/common' import { SocialError } from 'core/domain/common'
import { ICircleService } from 'core/services/circles' import { ICircleService } from 'core/services/circles'
import { Circle, UserFollower } from 'core/domain/circles' import { Circle, UserTie } from 'core/domain/circles'
import { User } from 'core/domain/users' import { User } from 'core/domain/users'
import { injectable } from 'inversify'
/** /**
* Firbase circle service * Firbase circle service
@@ -13,12 +14,13 @@ import { User } from 'core/domain/users'
* @class CircleService * @class CircleService
* @implements {ICircleService} * @implements {ICircleService}
*/ */
@injectable()
export class CircleService implements ICircleService { 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 = db.doc(`users/${userId}`).collection(`circles`).add(circle) let circleRef = db.doc(`users/${userId}`).collection(`circles`).add({...circle})
circleRef.then((result) => { circleRef.then((result) => {
resolve(result.id as string) resolve(result.id as string)
}) })
@@ -27,43 +29,6 @@ export class CircleService implements ICircleService {
} }
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) public updateCircle: (userId: string, circleId: string, circle: Circle)
=> Promise<void> = (userId, circleId, circle) => { => Promise<void> = (userId, circleId, circle) => {
return new Promise<void>((resolve,reject) => { return new Promise<void>((resolve,reject) => {
@@ -71,7 +36,7 @@ export class CircleService implements ICircleService {
const batch = db.batch() const batch = db.batch()
const circleRef = db.doc(`users/${userId}/circles/${circleId}`) const circleRef = db.doc(`users/${userId}/circles/${circleId}`)
batch.update(circleRef,circle) batch.update(circleRef,{...circle})
batch.commit().then(() => { batch.commit().then(() => {
resolve() resolve()
}) })

View File

@@ -0,0 +1,153 @@
// - Import react components
import { datumString } from 'aws-sdk/clients/athena'
import firebase, { firebaseRef, firebaseAuth, db } from 'data/firestoreClient'
import moment from 'moment'
import { SocialError } from 'core/domain/common'
import { Profile, UserProvider, User } from 'core/domain/users'
import { IUserTieService } from 'core/services/circles'
import { inject, injectable } from 'inversify'
import { FirestoreClientTypes } from '../../firestoreClientTypes'
import { IGraphService } from '../graphs/IGraphService'
import { Graph } from 'core/domain/graphs'
import { UserTie } from 'core/domain/circles'
/**
* Firbase user service
*
* @export
* @class UserTieService
* @implements {IUserTieService}
*/
@injectable()
export class UserTieService implements IUserTieService {
constructor (
@inject(FirestoreClientTypes.GraphService) private _graphService: IGraphService
) {
}
/**
* Tie users
*/
public tieUseres: (userTieSenderInfo: UserTie, userTieReceiveInfo: UserTie, circleIds: string[])
=> Promise<void> = (userTieSenderInfo, userTieReceiveInfo, circleIds) => {
return new Promise<void>((resolve, reject) => {
this._graphService
.addGraph(
new Graph(
userTieSenderInfo.userId!,
'TIE',
userTieReceiveInfo.userId!,
{...userTieSenderInfo},
{...userTieReceiveInfo},
{creationDate: Date.now(), circleIds}
)
,'users'
).then((result) => {
resolve()
})
.catch((error: any) => reject(new SocialError(error.code, 'firestore/tieUseres :' + error.message)))
})
}
/**
* Remove users' tie
*/
public removeUsersTie: (firstUserId: string, secondUserId: string)
=> Promise<void> = (firstUserId, secondUserId) => {
return new Promise<void>((resolve, reject) => {
this.getUserTiesWithSeconUser(firstUserId,secondUserId).then((userTies) => {
if (userTies.length > 0) {
this._graphService.deleteGraphByNodeId(userTies[0].nodeId!).then(resolve)
}
})
.catch((error: any) => reject(new SocialError(error.code, 'firestore/removeUsersTie :' + error.message)))
})
}
/**
* Get user ties
*/
public getUserTies: (userId: string)
=> Promise<{[userId: string]: UserTie}> = (userId) => {
return new Promise<{[userId: string]: UserTie}>((resolve, reject) => {
this._graphService
.getGraphs(
'users',
userId,
'TIE')
.then((result) => {
let parsedData: {[userId: string]: UserTie} = {}
result.forEach((node) => {
const leftUserInfo: UserTie = node.LeftMetadata
const rightUserInfo: UserTie = node.rightMetadata
const metadata: {creationDate: number, circleIds: string[]} = node.graphMetadata
parsedData = {
...parsedData,
[rightUserInfo.userId!] : {
...node.rightMetadata,
circleIdList: metadata ? metadata.circleIds : []
}
}
})
resolve(parsedData)
})
.catch((error: any) => reject(new SocialError(error.code, 'firestore/getUserTies :' + error.message)))
})
}
/**
* Get the users who tied current user
*/
public getUserTieSender: (userId: string)
=> Promise<{[userId: string]: UserTie}> = (userId) => {
return new Promise<{[userId: string]: UserTie}>((resolve, reject) => {
this._graphService
.getGraphs(
'users',
null,
'TIE',
userId
)
.then((result) => {
let parsedData: {[userId: string]: UserTie} = {}
result.forEach((node) => {
const leftUserInfo: UserTie = node.LeftMetadata
const rightUserInfo: UserTie = node.rightMetadata
const metada: {creationDate: number, circleIds: string[]} = node.graphMetadata
parsedData = {
...parsedData,
[leftUserInfo.userId!] : {
...parsedData[leftUserInfo.userId!],
circleIdList: []
}
}
})
resolve(parsedData)
})
.catch((error: any) => reject(new SocialError(error.code, 'firestore/getUserTieSender :' + error.message)))
})
}
/**
* Get user ties with second user identifier
*/
private getUserTiesWithSeconUser: (userId: string, secondUserId: string)
=> Promise<Graph[]> = (userId, secondUserId) => {
return new Promise<Graph[]>((resolve, reject) => {
this._graphService
.getGraphs(
'users',
userId,
'TIE',
secondUserId
).then(resolve)
.catch(reject)
})
}
}

View File

@@ -5,6 +5,7 @@ import _ from 'lodash'
import { SocialError } from 'core/domain/common' import { SocialError } from 'core/domain/common'
import { ICommentService } from 'core/services/comments' import { ICommentService } from 'core/services/comments'
import { Comment } from 'core/domain/comments' import { Comment } from 'core/domain/comments'
import { injectable } from 'inversify'
/** /**
* Firbase comment service * Firbase comment service
@@ -13,42 +14,20 @@ import { Comment } from 'core/domain/comments'
* @class CommentService * @class CommentService
* @implements {ICommentService} * @implements {ICommentService}
*/ */
@injectable()
export class CommentService implements ICommentService { export class CommentService implements ICommentService {
/**
* Add comment
*
* @memberof CommentService
*/
public addComment: (comment: Comment) public addComment: (comment: Comment)
=> Promise<string> = (comment) => { => Promise<string> = (comment) => {
return new Promise<string>((resolve,reject) => { return new Promise<string>((resolve,reject) => {
const postRef = db.doc(`posts/${comment.postId}`) let commentRef = db.collection('comments')
let commentRef = postRef.collection('comments')
commentRef.add(comment).then((result) => { commentRef.add(comment).then((result) => {
resolve(result.id) 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) => { .catch((error: any) => {
reject(new SocialError(error.code,error.message)) reject(new SocialError(error.code,error.message))
@@ -56,9 +35,14 @@ export class CommentService implements ICommentService {
}) })
} }
/**
* Get comments
*
* @memberof CommentService
*/
public getComments: (postId: string, callback: (resultComments: { [postId: string]: { [commentId: string]: Comment } }) => void) public getComments: (postId: string, callback: (resultComments: { [postId: string]: { [commentId: string]: Comment } }) => void)
=> void = (postId, callback) => { => void = (postId, callback) => {
let commentsRef = db.doc(`posts/${postId}`).collection(`comments`) let commentsRef = db.collection(`comments`).where('postId', '==', postId)
commentsRef.onSnapshot((snapshot) => { commentsRef.onSnapshot((snapshot) => {
let parsedData: {[postId: string]: {[commentId: string]: Comment}} = {[postId]: {}} let parsedData: {[postId: string]: {[commentId: string]: Comment}} = {[postId]: {}}
snapshot.forEach((result) => { snapshot.forEach((result) => {
@@ -73,13 +57,18 @@ export class CommentService implements ICommentService {
}) })
} }
/**
* Update comment
*
* @memberof CommentService
*/
public updateComment: (comment: Comment) public updateComment: (comment: Comment)
=> Promise<void> = (comment) => { => Promise<void> = (comment) => {
return new Promise<void>((resolve,reject) => { return new Promise<void>((resolve,reject) => {
const batch = db.batch() const batch = db.batch()
const commentRef = db.doc(`posts/${comment.postId}/comments/${comment.id}`) const commentRef = db.collection(`comments`).doc(comment.id!)
batch.update(commentRef, comment) batch.update(commentRef, {...comment})
batch.commit().then(() => { batch.commit().then(() => {
resolve() resolve()
}) })
@@ -89,45 +78,25 @@ export class CommentService implements ICommentService {
}) })
} }
public deleteComment: (commentId: string, postId: string) /**
=> Promise<void> = (commentId, postId) => { * Delete comment
*
* @memberof CommentService
*/
public deleteComment: (commentId: string)
=> Promise<void> = (commentId) => {
return new Promise<void>((resolve,reject) => { return new Promise<void>((resolve,reject) => {
const batch = db.batch() const commentCollectionRef = db.collection(`comments`)
const postRef = db.doc(`posts/${postId}`) const commentRef = commentCollectionRef.doc(commentId)
const commentRef = postRef.collection(`comments`).doc(commentId)
const batch = db.batch()
batch.delete(commentRef) batch.delete(commentRef)
batch.commit().then(() => { batch.commit().then(() => {
resolve() 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) => { .catch((error: any) => {
reject(new SocialError(error.code,error.message)) reject(new SocialError(error.code,error.message))
}) })
}) })
} }
} }

View File

@@ -3,6 +3,7 @@ import { firebaseRef, firebaseAuth } from 'data/firebaseClient'
import { SocialError } from 'core/domain/common' import { SocialError } from 'core/domain/common'
import { ICommonService } from 'core/services/common' import { ICommonService } from 'core/services/common'
import { injectable } from 'inversify'
/** /**
* Firbase common service * Firbase common service
@@ -11,6 +12,7 @@ import { ICommonService } from 'core/services/common'
* @class CommonService * @class CommonService
* @implements {ICommonService} * @implements {ICommonService}
*/ */
@injectable()
export class CommonService implements ICommonService { export class CommonService implements ICommonService {
} }

Some files were not shown because too many files have changed in this diff Show More