[Next Version] Realat to #5 #6 #9 #15 #17 #20 #23 #24 #27 #35 #36 #42

This commit is contained in:
Qolzam
2018-01-14 15:10:00 +07:00
parent e38dbc9fd3
commit 051cfa6f07
59 changed files with 1570 additions and 495 deletions

119
README.md
View File

@@ -6,12 +6,13 @@
</p> </p>
<!-- Name --> <!-- Name -->
<h1 align="center"> <h1 align="center">
<a href="https://github.com/Qolzam/react-social-network">React Social Network</a> <a href="https://github.com/Qolzam/react-social-network">React Social Network </a>:rocket:<span style="font-variant-caps: petite-caps;font-size: 30px;font-weight: 100;"> Version NEXT! </span>:rocket:
</h1> </h1>
[![Gitter](https://badges.gitter.im/react-social-network/Lobby.svg)](https://gitter.im/react-social-network/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Gitter](https://badges.gitter.im/react-social-network/Lobby.svg)](https://gitter.im/react-social-network/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
The React Social Network is an open source project relying on [React](https://facebook.github.io/react/docs/hello-world.html) a powerful javascript library for building the user interface. In this project, I tried to show some features of react/react components as a social network. The React Social Network is an open source project relying on [React](https://facebook.github.io/react/docs/hello-world.html) a powerful javascript library for building the user interface. In this project, I tried to show some features of react/react components as a social network.
The structure of this project give the ability to devoloper to develop their project on thier own idea and environment.
<p align="center"> <p align="center">
<a href="http://greensocial.herokuapp.com/"> <a href="http://greensocial.herokuapp.com/">
@@ -27,9 +28,9 @@ For those who prefer writing code by typescript, now React Social Network suppor
This project adheres to the Contributor Covenant [code of conduct](https://github.com/Qolzam/react-social-network/blob/master/CODE_OF_CONDUCT.md). This project adheres to the Contributor Covenant [code of conduct](https://github.com/Qolzam/react-social-network/blob/master/CODE_OF_CONDUCT.md).
By participating, you are expected to uphold this code. Please report unacceptable behavior to amir.gholzam@live.com. By participating, you are expected to uphold this code. Please report unacceptable behavior to amir.gholzam@live.com.
## DEMO ## Example
[Green Open Social](http://greensocial.herokuapp.com) [Love Open Social](https://love-social.firebaseapp.com)
## Required Knowledge ## Required Knowledge
@@ -39,44 +40,37 @@ I recommend that you get to know React before using React Social Network. React
## Document ## Document
Use [Documentation](https://qolzam.gitbooks.io/react-social-network/) to find out more details about this project. Comming soon :) ...
## Features
* [TypeScript](https://www.typescriptlang.org/) TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
* [React](https://facebook.github.io/react/docs/hello-world.html) A javascript library for building user interfaces.
* [Redux](http://redux.js.org/) is a predictable state container for JavaScript apps.
* [Material-UI](http://www.material-ui.com/#/) A Set of React Components that Implement Google's Material Design.
* [react-redux](https://github.com/reactjs/react-redux) Official React bindings for Redux.
* [Firebase](https://firebase.google.com/) products like Analytics, Realtime Database, Messaging, and Crash Reporting let you move quickly and focus on your users.
* [redux-thunk](https://github.com/gaearon/redux-thunk) Redux Thunk middleware allows you to write action creators that return a function instead of an action. The thunk can be used to delay the dispatch of an action, or to dispatch only if a certain condition is met. The inner function receives the store methods dispatch and getState as parameters.
* [Express](https://expressjs.com/) Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.
* [React Router V4](https://github.com/ReactTraining/react-router) for routing website location
* [Sass](http://sass-lang.com/) CSS with superpowers. Sass boasts more features and abilities than any other CSS extension language out there.
* [Webpack](https://webpack.js.org/) for bundling code
## In my todo list
* Documentation
* Testing
* Security issues
* Performance
* Add some features and solving bugs
* Sharing post in social itself and other socials
* Add link feature to post
* Add vido post
* Add image gallery post
* Search post and people
...
# Prerequisites ## Road map
1. Support Firebase/Firestore -> on developing
2. Support AWS -> on developing
3. Support Azure
4. Support ASP.NET -> on developing
## Getting Started
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system.
### Prerequisites
Install [NodeJs](https://nodejs.org/en/) Install [NodeJs](https://nodejs.org/en/)
#### Note
# Installing - If you're using Windows you should install all node-gyp dependencies with following commands:
[![Build social network in 5 minutes](https://img.youtube.com/vi/E12PNKKjzqA/0.jpg)](https://www.youtube.com/watch?v=E12PNKKjzqA) `$ npm install --global --production windows-build-tools`
and then install the package
`$ npm install --global node-gyp`
### Installing
## Install back-end server/serverless
Comming soon :) ...
1. Fork the [react-social-network](https://github.com/Qolzam/react-social-network) repository on Github 1. Fork the [react-social-network](https://github.com/Qolzam/react-social-network) repository on Github
2. Clone your fork to your local machine `git clone git@github.com:<yourname>/react-social-network.git` 2. Clone your fork to your local machine `git clone git@github.com:<yourname>/react-social-network.git`
@@ -101,31 +95,56 @@ I recommend that you get to know React before using React Social Network. React
5. Installing all nodejs modules: 5. Installing all nodejs modules:
`npm install` `npm install`
6. Rub webpack to build bundle file 6. Go ahead ;)
`webpack` `npm start`
5. Running server:
`node server.js`
# Warning
- If you're using Windows you should install all node-gyp dependencies with following commands:
`$ npm install --global --production windows-build-tools`
and then install the package
`$ npm install --global node-gyp` ## Deployment
Follow [firebase instruction](https://firebase.google.com/docs/hosting/deploying)
`firebase deploy`
## Contribute ## Built With
[React Social Network](http://greensocial.herokuapp.com/) has been made by love. I planed to build a back-end for this project and improve the performance as I process all procedures on the front-end side. If you'd like to help, * [TypeScript](https://www.typescriptlang.org/) TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
* [JSX/TSX](https://jsx.github.io/) This project support both *.jsx and *.tsx files. JSX is a statically-typed, object-oriented programming language designed to run on modern web browsers. Being developed at DeNA as a research project, the language has following characteristics.
* [React](https://facebook.github.io/react/docs/hello-world.html) A javascript library for building user interfaces.
* [Redux](http://redux.js.org/) is a predictable state container for JavaScript apps.
* [Material-UI](http://www.material-ui.com/#/) A Set of React Components that Implement Google's Material Design.
* [react-redux](https://github.com/reactjs/react-redux) Official React bindings for Redux.
* [Firebase](https://firebase.google.com/) products like Analytics, Realtime Database, Messaging, and Crash Reporting let you move quickly and focus on your users.
* [redux-thunk](https://github.com/gaearon/redux-thunk) Redux Thunk middleware allows you to write action creators that return a function instead of an action. The thunk can be used to delay the dispatch of an action, or to dispatch only if a certain condition is met. The inner function receives the store methods dispatch and getState as parameters.
* [Express](https://expressjs.com/) Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.
* [React Router V4](https://github.com/ReactTraining/react-router) for routing website location
* [Sass](http://sass-lang.com/) CSS with superpowers. Sass boasts more features and abilities than any other CSS extension language out there.
* [Webpack](https://webpack.js.org/) for bundling code
## Contributing
[React Social Network](https://love-social.firebaseapp.com) has been made by love. I planed to build a back-end for this project and improve the performance as I process all procedures on the front-end side. If you'd like to help,
check out the [document](https://qolzam.gitbooks.io/react-social-network/). check out the [document](https://qolzam.gitbooks.io/react-social-network/).
I'd greatly appreciate any [contribution](https://github.com/Qolzam/react-social-network/blob/master/CONTRIBUTING.md) I'd greatly appreciate any [contribution](https://github.com/Qolzam/react-social-network/blob/master/CONTRIBUTING.md)
you make. :)
# Authors ## Versioning
We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/Qolzam/react-social-network/tags).
## Authors
- Amir Movahedi - Amir Movahedi
- See also the list of [contributors](https://github.com/Qolzam/react-social-network/contributors) who participated in this project.
# License ## License
This project is licensed under the MIT License - see the [LICENSE](https://github.com/Qolzam/react-social-network/blob/master/LICENSE) file for details This project is licensed under the MIT License - see the [LICENSE](https://github.com/Qolzam/react-social-network/blob/master/LICENSE) file for details
## Acknowledgments
* React
* Firebase
* JavaScript
* TypeScript

View File

@@ -6,12 +6,13 @@
</p> </p>
<!-- Name --> <!-- Name -->
<h1 align="center"> <h1 align="center">
<a href="https://github.com/Qolzam/react-social-network">React Social Network</a> <a href="https://github.com/Qolzam/react-social-network">React Social Network </a>:rocket:<span style="font-variant-caps: petite-caps;font-size: 30px;font-weight: 100;"> Version NEXT! </span>:rocket:
</h1> </h1>
[![Gitter](https://badges.gitter.im/react-social-network/Lobby.svg)](https://gitter.im/react-social-network/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Gitter](https://badges.gitter.im/react-social-network/Lobby.svg)](https://gitter.im/react-social-network/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
The React Social Network is an open source project relying on [React](https://facebook.github.io/react/docs/hello-world.html) a powerful javascript library for building the user interface. In this project, I tried to show some features of react/react components as a social network. The React Social Network is an open source project relying on [React](https://facebook.github.io/react/docs/hello-world.html) a powerful javascript library for building the user interface. In this project, I tried to show some features of react/react components as a social network.
The structure of this project give the ability to devoloper to develop their project on thier own idea and environment.
<p align="center"> <p align="center">
<a href="http://greensocial.herokuapp.com/"> <a href="http://greensocial.herokuapp.com/">
@@ -27,9 +28,9 @@ For those who prefer writing code by typescript, now React Social Network suppor
This project adheres to the Contributor Covenant [code of conduct](https://github.com/Qolzam/react-social-network/blob/master/CODE_OF_CONDUCT.md). This project adheres to the Contributor Covenant [code of conduct](https://github.com/Qolzam/react-social-network/blob/master/CODE_OF_CONDUCT.md).
By participating, you are expected to uphold this code. Please report unacceptable behavior to amir.gholzam@live.com. By participating, you are expected to uphold this code. Please report unacceptable behavior to amir.gholzam@live.com.
## DEMO ## Example
[Green Open Social](http://greensocial.herokuapp.com) [Love Open Social](https://love-social.firebaseapp.com)
## Required Knowledge ## Required Knowledge
@@ -39,44 +40,37 @@ I recommend that you get to know React before using React Social Network. React
## Document ## Document
Use [Documentation](https://qolzam.gitbooks.io/react-social-network/) to find out more details about this project. Comming soon :) ...
## Features
* [TypeScript](https://www.typescriptlang.org/) TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
* [React](https://facebook.github.io/react/docs/hello-world.html) A javascript library for building user interfaces.
* [Redux](http://redux.js.org/) is a predictable state container for JavaScript apps.
* [Material-UI](http://www.material-ui.com/#/) A Set of React Components that Implement Google's Material Design.
* [react-redux](https://github.com/reactjs/react-redux) Official React bindings for Redux.
* [Firebase](https://firebase.google.com/) products like Analytics, Realtime Database, Messaging, and Crash Reporting let you move quickly and focus on your users.
* [redux-thunk](https://github.com/gaearon/redux-thunk) Redux Thunk middleware allows you to write action creators that return a function instead of an action. The thunk can be used to delay the dispatch of an action, or to dispatch only if a certain condition is met. The inner function receives the store methods dispatch and getState as parameters.
* [Express](https://expressjs.com/) Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.
* [React Router V4](https://github.com/ReactTraining/react-router) for routing website location
* [Sass](http://sass-lang.com/) CSS with superpowers. Sass boasts more features and abilities than any other CSS extension language out there.
* [Webpack](https://webpack.js.org/) for bundling code
## In my todo list
* Documentation
* Testing
* Security issues
* Performance
* Add some features and solving bugs
* Sharing post in social itself and other socials
* Add link feature to post
* Add vido post
* Add image gallery post
* Search post and people
...
# Prerequisites ## Road map
1. Support Firebase/Firestore -> on developing
2. Support AWS -> on developing
3. Support Azure
4. Support ASP.NET -> on developing
## Getting Started
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system.
### Prerequisites
Install [NodeJs](https://nodejs.org/en/) Install [NodeJs](https://nodejs.org/en/)
#### Note
# Installing - If you're using Windows you should install all node-gyp dependencies with following commands:
[![Build social network in 5 minutes](https://img.youtube.com/vi/E12PNKKjzqA/0.jpg)](https://www.youtube.com/watch?v=E12PNKKjzqA) `$ npm install --global --production windows-build-tools`
and then install the package
`$ npm install --global node-gyp`
### Installing
## Install back-end server/serverless
Comming soon :) ...
1. Fork the [react-social-network](https://github.com/Qolzam/react-social-network) repository on Github 1. Fork the [react-social-network](https://github.com/Qolzam/react-social-network) repository on Github
2. Clone your fork to your local machine `git clone git@github.com:<yourname>/react-social-network.git` 2. Clone your fork to your local machine `git clone git@github.com:<yourname>/react-social-network.git`
@@ -101,31 +95,56 @@ I recommend that you get to know React before using React Social Network. React
5. Installing all nodejs modules: 5. Installing all nodejs modules:
`npm install` `npm install`
6. Rub webpack to build bundle file 6. Go ahead ;)
`webpack` `npm start`
5. Running server:
`node server.js`
# Warning
- If you're using Windows you should install all node-gyp dependencies with following commands:
`$ npm install --global --production windows-build-tools`
and then install the package
`$ npm install --global node-gyp` ## Deployment
Follow [firebase instruction](https://firebase.google.com/docs/hosting/deploying)
`firebase deploy`
## Contribute ## Built With
[React Social Network](http://greensocial.herokuapp.com/) has been made by love. I planed to build a back-end for this project and improve the performance as I process all procedures on the front-end side. If you'd like to help, * [TypeScript](https://www.typescriptlang.org/) TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
* [JSX/TSX](https://jsx.github.io/) This project support both *.jsx and *.tsx files. JSX is a statically-typed, object-oriented programming language designed to run on modern web browsers. Being developed at DeNA as a research project, the language has following characteristics.
* [React](https://facebook.github.io/react/docs/hello-world.html) A javascript library for building user interfaces.
* [Redux](http://redux.js.org/) is a predictable state container for JavaScript apps.
* [Material-UI](http://www.material-ui.com/#/) A Set of React Components that Implement Google's Material Design.
* [react-redux](https://github.com/reactjs/react-redux) Official React bindings for Redux.
* [Firebase](https://firebase.google.com/) products like Analytics, Realtime Database, Messaging, and Crash Reporting let you move quickly and focus on your users.
* [redux-thunk](https://github.com/gaearon/redux-thunk) Redux Thunk middleware allows you to write action creators that return a function instead of an action. The thunk can be used to delay the dispatch of an action, or to dispatch only if a certain condition is met. The inner function receives the store methods dispatch and getState as parameters.
* [Express](https://expressjs.com/) Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.
* [React Router V4](https://github.com/ReactTraining/react-router) for routing website location
* [Sass](http://sass-lang.com/) CSS with superpowers. Sass boasts more features and abilities than any other CSS extension language out there.
* [Webpack](https://webpack.js.org/) for bundling code
## Contributing
[React Social Network](https://love-social.firebaseapp.com) has been made by love. I planed to build a back-end for this project and improve the performance as I process all procedures on the front-end side. If you'd like to help,
check out the [document](https://qolzam.gitbooks.io/react-social-network/). check out the [document](https://qolzam.gitbooks.io/react-social-network/).
I'd greatly appreciate any [contribution](https://github.com/Qolzam/react-social-network/blob/master/CONTRIBUTING.md) I'd greatly appreciate any [contribution](https://github.com/Qolzam/react-social-network/blob/master/CONTRIBUTING.md)
you make. :)
# Authors ## Versioning
We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/Qolzam/react-social-network/tags).
## Authors
- Amir Movahedi - Amir Movahedi
- See also the list of [contributors](https://github.com/Qolzam/react-social-network/contributors) who participated in this project.
# License ## License
This project is licensed under the MIT License - see the [LICENSE](https://github.com/Qolzam/react-social-network/blob/master/LICENSE) file for details This project is licensed under the MIT License - see the [LICENSE](https://github.com/Qolzam/react-social-network/blob/master/LICENSE) file for details
## Acknowledgments
* React
* Firebase
* JavaScript
* TypeScript

View File

@@ -2,22 +2,6 @@
Is a decoupled layer of interfaces to data and/or functionality of one or more components. Is a decoupled layer of interfaces to data and/or functionality of one or more components.
## CircleAPI.jsx
We provided some functions for doing some procedures on user circles.
```javascript
getUserBelongCircles = (circles,followingId) => {}
```
To get circles which the user is added in. `circles` is the parameter that we want to know if user is exist in any of them or not. `followingId` is the parameter that show the use identifier who we are looking for in `circles` parameter.
```javascript
getFollowingUsers = (circles) => {}
```
Get the user who are in circles. `circles` is the paramater that we get users from.
## FileAPI.jsx ## FileAPI.jsx
A set of functions for working with files. A set of functions for working with files.

View File

@@ -39,7 +39,7 @@
"node-sass": "^4.5.2", "node-sass": "^4.5.2",
"npm": "^5.6.0", "npm": "^5.6.0",
"prop-types": "^15.6.0", "prop-types": "^15.6.0",
"react": "^16.0.0", "react": "^16.2.0",
"react-addons-test-utils": "^15.6.2", "react-addons-test-utils": "^15.6.2",
"react-avatar-editor": "^10.3.0", "react-avatar-editor": "^10.3.0",
"react-dom": "^16.0.0", "react-dom": "^16.0.0",
@@ -68,6 +68,7 @@
"devDependencies": { "devDependencies": {
"@types/lodash": "^4.14.77", "@types/lodash": "^4.14.77",
"@types/material-ui": "^0.18.2", "@types/material-ui": "^0.18.2",
"@types/moment": "^2.13.0",
"@types/node": "^8.0.33", "@types/node": "^8.0.33",
"@types/prop-types": "^15.5.2", "@types/prop-types": "^15.5.2",
"@types/react": "^16.0.10", "@types/react": "^16.0.10",

View File

@@ -14,12 +14,17 @@ import * as globalActions from 'actions/globalActions'
import * as postActions from 'actions/postActions' 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 * as serverActions from 'actions/serverActions'
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 { SocialProviderTypes } from 'core/socialProviderTypes'
import { provider } from '../socialEngine' import { provider } from '../socialEngine'
import { IUserTieService } from 'core/services/circles' import { IUserTieService } from 'core/services/circles'
import StringAPI from 'api/StringAPI'
import { ServerRequestStatusType } from 'actions/serverRequestStatusType'
import { ServerRequestType } from 'constants/serverRequestType'
import { ServerRequestModel } from 'models/server/serverRequestModel'
/** /**
* Get service providers * Get service providers
@@ -39,7 +44,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,
isSystem : false,
ownerId: uid
} }
return circleService.addCircle(uid, circle).then((circleKey: string) => { return circleService.addCircle(uid, circle).then((circleKey: string) => {
circle.id = circleKey circle.id = circleKey
@@ -51,6 +58,60 @@ export let dbAddCircle = (circleName: string) => {
} }
} }
/**
* Add referer user to the `Following` circle of current user
*/
export const dbFollowUser = (followingCircleId: string, userFollowing: UserTie) => {
return (dispatch: Function, getState: Function) => {
const state = getState()
let uid: string = state.authorize.uid
let user: User = { ...state.user.info[uid], userId: uid }
// Set server request status to {Sent} for following user
const followReqestModel = createFollowRequest(userFollowing.userId!)
dispatch(serverActions.sendRequest(followReqestModel))
// Call server API
return userTieService.tieUseres(
{ userId: user.userId!, fullName: user.fullName, avatar: user.avatar, approved: false },
{ userId: userFollowing.userId!, fullName: userFollowing.fullName, avatar: userFollowing.avatar, approved: false },
[followingCircleId]
)
.then(() => {
dispatch(addFollowingUser(
new UserTie(
userFollowing.userId!,
moment().unix(),
userFollowing.fullName,
userFollowing.avatar,
false,
[followingCircleId]
)))
// Set server request status to {OK} for following user
followReqestModel.status = ServerRequestStatusType.OK
dispatch(serverActions.sendRequest(followReqestModel))
// Send notification
dispatch(notifyActions.dbAddNotification(
{
description: `${user.fullName} follow you.`,
url: `/${uid}`,
notifyRecieverUserId: userFollowing.userId as string,
notifierUserId: uid,
isSeen: false
}))
}, (error: SocialError) => {
dispatch(globalActions.showErrorMessage(error.message))
// Set server request status to {Error} for following user
followReqestModel.status = ServerRequestStatusType.Error
dispatch(serverActions.sendRequest(followReqestModel))
})
}
}
/** /**
* Update user in circle/circles * Update user in circle/circles
*/ */
@@ -60,7 +121,14 @@ export let dbUpdateUserInCircles = (circleIdList: string[], userFollowing: UserT
let uid: string = state.authorize.uid let uid: string = state.authorize.uid
let user: User = { ...state.user.info[uid], userId: uid } let user: User = { ...state.user.info[uid], userId: uid }
return userTieService.tieUseres( // Set server request status to {Sent}
const addToCircleRequest = createAddToCircleRequest(userFollowing.userId!)
dispatch(serverActions.sendRequest(addToCircleRequest))
dispatch(globalActions.showMasterLoading())
// Call server API
return userTieService.updateUsersTie(
{ userId: user.userId!, fullName: user.fullName, avatar: user.avatar, approved: false }, { userId: user.userId!, fullName: user.fullName, avatar: user.avatar, approved: false },
{ userId: userFollowing.userId!, fullName: userFollowing.fullName, avatar: userFollowing.avatar, approved: false }, { userId: userFollowing.userId!, fullName: userFollowing.fullName, avatar: userFollowing.avatar, approved: false },
circleIdList circleIdList
@@ -76,17 +144,23 @@ export let dbUpdateUserInCircles = (circleIdList: string[], userFollowing: UserT
circleIdList circleIdList
))) )))
dispatch(notifyActions.dbAddNotification( // Set server request status to {OK}
{ addToCircleRequest.status = ServerRequestStatusType.OK
description: `${user.fullName} follow you.`, dispatch(serverActions.sendRequest(addToCircleRequest))
url: `/${uid}`,
notifyRecieverUserId: userFollowing.userId as string, dispatch(globalActions.hideMasterLoading())
notifierUserId: uid,
isSeen: false // Close select circle box
})) dispatch(closeSelectCircleBox(userFollowing.userId!))
}, (error: SocialError) => { }, (error: SocialError) => {
dispatch(globalActions.showErrorMessage(error.message)) dispatch(globalActions.showErrorMessage(error.message))
dispatch(globalActions.hideMasterLoading())
// Set server request status to {Error}
addToCircleRequest.status = ServerRequestStatusType.Error
dispatch(serverActions.sendRequest(addToCircleRequest))
}) })
} }
} }
@@ -99,11 +173,36 @@ export let dbDeleteFollowingUser = (userFollowingId: string) => {
let uid: string = getState().authorize.uid let uid: string = getState().authorize.uid
// Set server request status to {Sent}
const deleteFollowingUserRequest = createdeleteFollowingUserRequest(userFollowingId)
dispatch(serverActions.sendRequest(deleteFollowingUserRequest))
dispatch(globalActions.showMasterLoading())
// Call server API
return userTieService.removeUsersTie(uid, userFollowingId) return userTieService.removeUsersTie(uid, userFollowingId)
.then(() => { .then(() => {
dispatch(deleteFollowingUser(userFollowingId)) dispatch(deleteFollowingUser(userFollowingId))
dispatch(globalActions.hideMasterLoading())
// Close select circle box
dispatch(closeSelectCircleBox(userFollowingId))
// Set server request status to {OK}
deleteFollowingUserRequest.status = ServerRequestStatusType.OK
dispatch(serverActions.sendRequest(deleteFollowingUserRequest))
}, (error: SocialError) => { }, (error: SocialError) => {
dispatch(globalActions.showErrorMessage(error.message)) dispatch(globalActions.showErrorMessage(error.message))
dispatch(globalActions.hideMasterLoading())
// Close select circle box
dispatch(closeSelectCircleBox(userFollowingId))
// Set server request status to {Error}
deleteFollowingUserRequest.status = ServerRequestStatusType.Error
dispatch(serverActions.sendRequest(deleteFollowingUserRequest))
}) })
} }
} }
@@ -120,7 +219,8 @@ export const dbUpdateCircle = (newCircle: Circle) => {
// Write the new data simultaneously in the list // Write the new data simultaneously in the list
let circle: Circle = getState().circle.userTies[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,
isSystem : false
} }
return circleService.updateCircle(uid, newCircle.id!, circle) return circleService.updateCircle(uid, newCircle.id!, circle)
.then(() => { .then(() => {
@@ -198,10 +298,10 @@ export const dbGetFollowers = () => {
return (dispatch: any, getState: Function) => { return (dispatch: any, getState: Function) => {
let uid: string = getState().authorize.uid let uid: string = getState().authorize.uid
if (uid) { if (uid) {
userTieService.getUserTies(uid).then((result) => { userTieService.getUserTieSender(uid).then((result) => {
dispatch(userActions.addPeopleInfo(result as any)) dispatch(userActions.addPeopleInfo(result as any))
dispatch(addUserTies(result)) dispatch(addUserTieds(result))
}) })
.catch((error: SocialError) => { .catch((error: SocialError) => {
@@ -230,6 +330,45 @@ export const dbGetCirclesByUserId = (uid: string) => {
} }
} }
/**
* Create follow user serevr request model
*/
const createFollowRequest = (userFollowingId: string) => {
const requestId = StringAPI.createServerRequestId(ServerRequestType.CircleFollowUser, userFollowingId)
return new ServerRequestModel(
ServerRequestType.CircleFollowUser,
requestId,
'',
ServerRequestStatusType.Sent
)
}
/**
* Create add referer user to circle serevr request model
*/
const createAddToCircleRequest = (userFollowingId: string) => {
const requestId = StringAPI.createServerRequestId(ServerRequestType.CircleAddToCircle, userFollowingId)
return new ServerRequestModel(
ServerRequestType.CircleAddToCircle,
requestId,
'',
ServerRequestStatusType.Sent
)
}
/**
* Create delete referer user serevr request model
*/
const createdeleteFollowingUserRequest = (userFollowingId: string) => {
const requestId = StringAPI.createServerRequestId(ServerRequestType.CircleDeleteFollowingUser, userFollowingId)
return new ServerRequestModel(
ServerRequestType.CircleDeleteFollowingUser,
requestId,
'',
ServerRequestStatusType.Sent
)
}
/* _____________ CRUD State _____________ */ /* _____________ CRUD State _____________ */
/** /**
@@ -396,6 +535,49 @@ export const showFollowingUserLoading = (userId: string) => {
} }
/**
* Set current user selected circles for referer user
*/
export const setSelectedCircles = (userId: string, circleList: string[]) => {
return {
type: CircleActionType.SET_SELECTED_CIRCLES_USER_BOX_COMPONENT,
payload: { userId, circleList }
}
}
/**
* Remove current user selected circles for referer user
*/
export const removeSelectedCircles = (userId: string) => {
return {
type: CircleActionType.REMOVE_SELECTED_CIRCLES_USER_BOX_COMPONENT,
payload: { userId }
}
}
/**
* Open select circle box
*/
export const openSelectCircleBox = (userId: string) => {
return {
type: CircleActionType.OPEN_SELECT_CIRCLES_USER_BOX_COMPONENT,
payload: { userId}
}
}
/**
* Close select circle box
*/
export const closeSelectCircleBox = (userId: string) => {
return {
type: CircleActionType.CLOSE_SELECT_CIRCLES_USER_BOX_COMPONENT,
payload: { userId}
}
}
/** /**
* Hide loading on following user * Hide loading on following user
*/ */
@@ -405,4 +587,4 @@ export const hideFollowingUserLoading = (userId: string) => {
payload: { userId } payload: { userId }
} }
} }

View File

@@ -97,7 +97,7 @@ export const dbGetComments = (ownerUserId: string, postId: string) => {
} }
const desiredComments = comments[postId] const desiredComments = comments[postId]
if (desiredComments && Object.keys(desiredComments).length > 0) { if (desiredComments) {
commentsCount = Object.keys(desiredComments).length commentsCount = Object.keys(desiredComments).length
let sortedObjects = desiredComments as any let sortedObjects = desiredComments as any
// Sort posts with creation date // Sort posts with creation date

View File

@@ -5,6 +5,50 @@ import { GlobalActionType } from 'constants/globalActionType'
import * as postActions from 'actions/postActions' import * as postActions from 'actions/postActions'
import * as commentActions from 'actions/commentActions' import * as commentActions from 'actions/commentActions'
import * as userActions from 'actions/userActions' import * as userActions from 'actions/userActions'
import * as serverActions from 'actions/serverActions'
import { ICommonService } from 'core/services/common/ICommonService'
import { provider } from 'src/socialEngine'
import { SocialProviderTypes } from 'core/socialProviderTypes'
import { Feed, SocialError } from 'core/domain/common'
import { ServerRequestType } from 'constants/serverRequestType'
import StringAPI from 'api/StringAPI'
import { ServerRequestModel } from 'models/server'
import { ServerRequestStatusType } from 'actions/serverRequestStatusType'
/**
* Get service providers
*/
const commonService: ICommonService = provider.get<ICommonService>(SocialProviderTypes.CommonService)
/**
* Add a normal feed
* @param {any} newFeed
* @param {Function} callBack
*/
export let dbSendFeed = (newFeed: Feed) => {
return (dispatch: any, getState: Function) => {
let uid: string = getState().authorize.uid
// Set server request status to {Sent}
const feedbackRequest = createFeedbackRequest(uid)
dispatch(serverActions.sendRequest(feedbackRequest))
return commonService.addFeed(newFeed).then(() => {
// Set server request status to {OK}
feedbackRequest.status = ServerRequestStatusType.OK
dispatch(serverActions.sendRequest(feedbackRequest))
})
.catch((error: SocialError) => {
dispatch(showErrorMessage(error.message))
// Set server request status to {Error}
feedbackRequest.status = ServerRequestStatusType.Error
dispatch(serverActions.sendRequest(feedbackRequest))
})
}
}
/** /**
* Progress change * Progress change
@@ -14,7 +58,7 @@ import * as userActions from 'actions/userActions'
export const progressChange = (percent: number, visible: Boolean) => { export const progressChange = (percent: number, visible: Boolean) => {
return { return {
type: GlobalActionType.PROGRESS_CHANGE, type: GlobalActionType.PROGRESS_CHANGE,
payload: {percent, visible} payload: { percent, visible }
} }
} }
@@ -23,7 +67,7 @@ export const progressChange = (percent: number, visible: Boolean) => {
* Default data loaded status will be true * Default data loaded status will be true
*/ */
export const defaultDataEnable = () => { export const defaultDataEnable = () => {
return{ return {
type: GlobalActionType.DEFAULT_DATA_ENABLE type: GlobalActionType.DEFAULT_DATA_ENABLE
} }
} }
@@ -33,21 +77,21 @@ export const defaultDataEnable = () => {
* @param {boolean} status * @param {boolean} status
*/ */
export const defaultDataDisable = () => { export const defaultDataDisable = () => {
return{ return {
type: GlobalActionType.DEFAULT_DATA_DISABLE type: GlobalActionType.DEFAULT_DATA_DISABLE
} }
} }
// - Show notification of request // - Show notification of request
export const showNotificationRequest = () => { export const showNotificationRequest = () => {
return{ return {
type: GlobalActionType.SHOW_SEND_REQUEST_MESSAGE_GLOBAL type: GlobalActionType.SHOW_SEND_REQUEST_MESSAGE_GLOBAL
} }
} }
// - Show notification of success // - Show notification of success
export const showNotificationSuccess = () => { export const showNotificationSuccess = () => {
return{ return {
type: GlobalActionType.SHOW_REQUEST_SUCCESS_MESSAGE_GLOBAL type: GlobalActionType.SHOW_REQUEST_SUCCESS_MESSAGE_GLOBAL
} }
} }
@@ -57,7 +101,7 @@ export const showNotificationSuccess = () => {
*/ */
export const hideMessage = () => { export const hideMessage = () => {
hideTopLoading() hideTopLoading()
return{ return {
type: GlobalActionType.HIDE_MESSAGE_GLOBAL type: GlobalActionType.HIDE_MESSAGE_GLOBAL
} }
@@ -68,23 +112,6 @@ 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
@@ -95,8 +122,8 @@ export const showErrorMessage = (message: string) => {
/** /**
* Set header title * Set header title
*/ */
export const setHeaderTitleOpt = (callerKey: string,payload: any) => { export const setHeaderTitleOpt = (callerKey: string, payload: any) => {
return (dispatch: any,getState: Function) => { return (dispatch: any, getState: Function) => {
switch (callerKey) { switch (callerKey) {
case 'profile': case 'profile':
const userName = getState().user.info && getState().user.info[payload] ? getState().user.info[payload].fullName : '' const userName = getState().user.info && getState().user.info[payload] ? getState().user.info[payload].fullName : ''
@@ -114,7 +141,7 @@ export const setHeaderTitleOpt = (callerKey: string,payload: any) => {
* Set header title * Set header title
*/ */
export const setHeaderTitle = (text: string) => { export const setHeaderTitle = (text: string) => {
return{ return {
type: GlobalActionType.SET_HEADER_TITLE, type: GlobalActionType.SET_HEADER_TITLE,
payload: text payload: text
} }
@@ -125,7 +152,7 @@ export const setHeaderTitle = (text: string) => {
* Open post write * Open post write
*/ */
export const openPostWrite = () => { export const openPostWrite = () => {
return{ return {
type: GlobalActionType.OPEN_POST_WRITE type: GlobalActionType.OPEN_POST_WRITE
} }
@@ -135,7 +162,7 @@ export const openPostWrite = () => {
* Close post write * Close post write
*/ */
export const closePostWrite = () => { export const closePostWrite = () => {
return{ return {
type: GlobalActionType.CLOSE_POST_WRITE type: GlobalActionType.CLOSE_POST_WRITE
} }
@@ -145,7 +172,7 @@ export const closePostWrite = () => {
* Show top loading * Show top loading
*/ */
export const showTopLoading = () => { export const showTopLoading = () => {
return{ return {
type: GlobalActionType.SHOW_TOP_LOADING type: GlobalActionType.SHOW_TOP_LOADING
} }
@@ -155,17 +182,57 @@ export const showTopLoading = () => {
* Hide top loading * Hide top loading
*/ */
export const hideTopLoading = () => { export const hideTopLoading = () => {
return{ return {
type: GlobalActionType.HIDE_TOP_LOADING type: GlobalActionType.HIDE_TOP_LOADING
} }
} }
/**
* Show master loading
*/
export const showMasterLoading = () => {
return {
type: GlobalActionType.SHOW_MASTER_LOADING
}
}
/**
* Show send feedback
*/
export const showSendFeedback = () => {
return {
type: GlobalActionType.SHOW_SEND_FEEDBACK
}
}
/**
* Hide send feedback
*/
export const hideSendFeedback = () => {
return {
type: GlobalActionType.HIDE_SEND_FEEDBACK
}
}
/**
* Hide master loading
*/
export const hideMasterLoading = () => {
return {
type: GlobalActionType.HIDE_MASTER_LOADING
}
}
/** /**
* Store temp data * Store temp data
*/ */
export const temp = (data: any) => { export const temp = (data: any) => {
return{ return {
type: GlobalActionType.TEMP, type: GlobalActionType.TEMP,
payload: data payload: data
} }
@@ -176,7 +243,7 @@ export const temp = (data: any) => {
* Clear temp data * Clear temp data
*/ */
export const clearTemp = () => { export const clearTemp = () => {
return{ return {
type: GlobalActionType.CLEAR_TEMP type: GlobalActionType.CLEAR_TEMP
} }
@@ -185,7 +252,43 @@ export const clearTemp = () => {
// - Load data for guest // - Load data for guest
export const loadDataGuest = () => { export const loadDataGuest = () => {
// tslint:disable-next-line:no-empty // tslint:disable-next-line:no-empty
return (dispatch: any,getState: Function) => { return (dispatch: any, getState: Function) => {
} }
} }
/**
* Show error report dialog
*/
const showErrorReport = (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)
}
/**
* Create send feedback serevr request model
*/
const createFeedbackRequest = (userId: string) => {
const requestId = StringAPI.createServerRequestId(ServerRequestType.CommonSendFeedback, userId)
return new ServerRequestModel(
ServerRequestType.CommonSendFeedback,
requestId,
'',
ServerRequestStatusType.Sent
)
}

View File

@@ -15,7 +15,7 @@ import { SocialError } from 'core/domain/common/socialError'
* @param {Request} request * @param {Request} request
*/ */
export const sendRequest = (request: ServerRequestModel) => { export const sendRequest = (request: ServerRequestModel) => {
return { type: ServerActionType.ADD_REQUEST, payload: request } return { type: ServerActionType.ADD_REQUEST, payload: {request} }
} }

View File

@@ -1,47 +0,0 @@
import { Circle, UserTie } from 'core/domain/circles'
/**
* Get the circles' id which the specify users is in that circle
* @param {object} circles
* @param {string} followingId
*/
export const getUserBelongCircles = (circles: {[circleId: string]: Circle},followingId: string) => {
let userBelongCircles: string[] = []
Object.keys(circles).forEach((cid) => {
if (cid.trim() !== '-Followers' && circles[cid].users) {
let isExist = Object.keys(circles[cid].users).indexOf(followingId) > -1
if (isExist) {
userBelongCircles.push(cid)
}
}
})
return userBelongCircles
}
/**
* Get the following users
* @param {object} circles
*/
export const getFollowingUsers = (circles: {[circleId: string]: Circle}) => {
let followingUsers: {[userId: string]: UserTie} = {}
Object.keys(circles).forEach((cid) => {
if (cid.trim() !== '-Followers' && circles[cid].users) {
Object.keys(circles[cid].users).forEach((userId) => {
let isExist = Object.keys(followingUsers).indexOf(userId) > -1
if (!isExist) {
followingUsers[userId] = {
...circles[cid].users[userId]
}
}
})
}
})
return followingUsers
}
export default {
getUserBelongCircles,
getFollowingUsers
}

View File

@@ -1,17 +1,15 @@
import * as moment from 'moment'
/** /**
* Log the data * Log the data
* @param title log title * @param title log title
* @param data log data object * @param data log data object
*/ */
const logger = (title: string, data: any, trace?: boolean) => { const logger = (title: string, data: any) => {
const randomColor = getRandomColor() const randomColor = getRandomColor()
if (trace) {
console.trace() console.log(`\n\n%c ======= ${title} ======= %c${moment().format('HH:mm:ss SSS')} \n`, `color:${getRandomColor()};font-size:15`
} , `color:${getRandomColor()};font-size:15`, data,`\n\n =========================================`)
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}`)
} }
/** /**

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,65 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<!--
2018-1-14: Created with FontForge (http://fontforge.org)
-->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<metadata>
Created by FontForge 20160405 at Sun Jan 14 05:05:09 2018
By Apache
Copyright (c) 2018, Apache
</metadata>
<defs>
<font id="Flaticon" horiz-adv-x="512" >
<font-face
font-family="Flaticon"
font-weight="400"
font-stretch="normal"
units-per-em="512"
panose-1="2 0 5 3 0 0 0 0 0 0"
ascent="448"
descent="-64"
bbox="0 -63.999 512 448"
underline-thickness="25.6"
underline-position="-51.2"
unicode-range="U+0020-F106"
/>
<missing-glyph />
<glyph glyph-name="space" unicode=" " horiz-adv-x="200"
/>
<glyph glyph-name="uniF100" unicode="&#xf100;"
d="M345.6 217.601c-21.248 0 -38.3994 17.1514 -38.3994 38.3994s17.1514 38.4004 38.3994 38.4004s38.4004 -17.1523 38.4004 -38.4004s-17.1523 -38.3994 -38.4004 -38.3994zM166.4 217.601c-21.248 0 -38.4004 17.1514 -38.4004 38.3994s17.1523 38.4004 38.4004 38.4004
s38.3994 -17.1523 38.3994 -38.4004s-17.1514 -38.3994 -38.3994 -38.3994zM255.999 140.8c59.6484 0 110.208 -37.2471 130.688 -89.5996h-261.376c20.4795 52.3525 71.0391 89.5996 130.688 89.5996zM255.872 448c141.439 0 256.128 -114.561 256.128 -255.999
c0 -141.439 -114.688 -256 -256.128 -256s-255.872 114.561 -255.872 256c0 141.438 114.432 255.999 255.872 255.999zM255.999 -12.7998c113.153 0 204.801 91.6484 204.801 204.801c0 113.151 -91.6484 204.799 -204.801 204.799
c-113.151 0 -204.8 -91.6465 -204.8 -204.799c0 -113.153 91.6484 -204.801 204.8 -204.801z" />
<glyph glyph-name="uniF106" unicode="&#xf106;"
d="M255.999 51.2002c-59.6465 0 -110.335 37.376 -130.815 89.5996h261.631c-20.4785 -52.2236 -71.167 -89.5996 -130.815 -89.5996zM255.872 448c141.439 0 256.128 -114.561 256.128 -255.999c0 -141.439 -114.688 -256 -256.128 -256s-255.872 114.561 -255.872 256
c0 141.438 114.432 255.999 255.872 255.999zM255.999 -12.7998c113.153 0 204.801 91.6484 204.801 204.801c0 113.151 -91.6484 204.799 -204.801 204.799c-113.151 0 -204.8 -91.6465 -204.8 -204.799c0 -113.153 91.6484 -204.801 204.8 -204.801zM176.128 244.735
l-27.1357 -27.1348l-27.2646 27.1348l54.4004 54.2725l54.2715 -54.2725l-27.1348 -27.1348zM281.601 244.735l54.2705 54.2725l54.4014 -54.2725l-27.2646 -27.1348l-27.1367 27.1348l-27.1357 -27.1348z" />
<glyph glyph-name="uniF104" unicode="&#xf104;"
d="M166.4 217.601c-21.248 0 -38.4004 17.1514 -38.4004 38.3994s17.1523 38.4004 38.4004 38.4004s38.3994 -17.1523 38.3994 -38.4004s-17.1514 -38.3994 -38.3994 -38.3994zM345.6 217.601c-21.248 0 -38.3994 17.1514 -38.3994 38.3994s17.1514 38.4004 38.3994 38.4004
s38.4004 -17.1523 38.4004 -38.4004s-17.1523 -38.3994 -38.4004 -38.3994zM255.744 448c141.567 0 256.256 -114.688 256.256 -255.999c0 -141.312 -114.688 -256 -256.256 -256c-141.312 0 -255.744 114.688 -255.744 256c0 141.312 114.432 255.999 255.744 255.999z
M255.999 -12.7998c113.153 0 204.801 91.6484 204.801 204.801c0 113.151 -91.6484 204.799 -204.801 204.799c-113.151 0 -204.8 -91.6465 -204.8 -204.799c0 -113.153 91.6484 -204.801 204.8 -204.801zM255.999 140.8c59.6484 0 110.593 -37.2471 131.071 -89.5996
h-42.8789c-17.792 30.4639 -50.4326 51.2002 -88.1924 51.2002c-37.7588 0 -70.3984 -20.7363 -88.1924 -51.2002h-42.8799c20.4805 52.3525 71.4238 89.5996 131.072 89.5996z" />
<glyph glyph-name="uniF101" unicode="&#xf101;"
d="M148.992 192.001l-27.2646 27.1348l27.2646 27.1357l-27.2646 27.1367l27.2646 27.2646l27.1357 -27.2646l27.1367 27.2646l27.1348 -27.2646l-27.1348 -27.1367l27.1348 -27.1357l-27.1348 -27.1348l-27.1367 27.1348zM255.999 140.8
c59.6484 0 110.337 -37.376 130.817 -89.5996h-261.633c20.4795 52.2236 71.167 89.5996 130.815 89.5996zM363.008 300.673l27.2646 -27.2646l-27.2646 -27.1367l27.2646 -27.1357l-27.2646 -27.1348l-27.1367 27.1348l-27.1357 -27.1348l-27.1348 27.1348l27.1348 27.1357
l-27.1348 27.1367l27.1348 27.2646l27.1357 -27.2646zM255.872 448c141.439 0 256.128 -114.561 256.128 -255.999c0 -141.439 -114.688 -256 -256.128 -256s-255.872 114.561 -255.872 256c0 141.438 114.432 255.999 255.872 255.999zM255.999 -12.7998
c113.153 0 204.801 91.6484 204.801 204.801c0 113.151 -91.6484 204.799 -204.801 204.799c-113.151 0 -204.8 -91.6465 -204.8 -204.799c0 -113.153 91.6484 -204.801 204.8 -204.801z" />
<glyph glyph-name="uniF102" unicode="&#xf102;"
d="M255.744 448c141.567 0 256.256 -114.688 256.256 -255.999c0 -141.312 -114.688 -256 -256.256 -256c-141.312 0 -255.744 114.688 -255.744 256c0 141.312 114.432 255.999 255.744 255.999zM255.999 -12.7998c113.153 0 204.801 91.6484 204.801 204.801
c0 113.151 -91.6484 204.799 -204.801 204.799c-113.151 0 -204.8 -91.6465 -204.8 -204.799c0 -113.153 91.6484 -204.801 204.8 -204.801zM179.199 102.4v38.3994h153.601v-38.3994h-153.601zM204.8 256c0 -21.248 -17.1514 -38.3994 -38.3994 -38.3994
s-38.4004 17.1514 -38.4004 38.3994s17.1523 38.4004 38.4004 38.4004s38.3994 -17.1523 38.3994 -38.4004zM345.6 294.4c21.248 0 38.4004 -17.1523 38.4004 -38.4004s-17.1523 -38.3994 -38.4004 -38.3994s-38.3994 17.1514 -38.3994 38.3994
s17.1514 38.4004 38.3994 38.4004z" />
<glyph glyph-name="uniF105" unicode="&#xf105;"
d="M166.4 217.601c-21.248 0 -38.4004 17.1514 -38.4004 38.3994s17.1523 38.4004 38.4004 38.4004s38.3994 -17.1523 38.3994 -38.4004s-17.1514 -38.3994 -38.3994 -38.3994zM345.6 217.601c-21.248 0 -38.3994 17.1514 -38.3994 38.3994s17.1514 38.4004 38.3994 38.4004
s38.4004 -17.1523 38.4004 -38.4004s-17.1523 -38.3994 -38.4004 -38.3994zM255.999 89.5996c37.7607 0 70.4004 20.7363 88.1943 51.2002h42.8799c-20.4805 -52.3516 -71.4238 -89.5996 -131.072 -89.5996s-110.593 37.248 -131.072 89.5996h42.8789
c17.791 -30.4639 50.4316 -51.2002 88.1914 -51.2002zM255.744 448c141.567 0 256.256 -114.688 256.256 -255.999c0 -141.312 -114.688 -256 -256.256 -256c-141.312 0 -255.744 114.688 -255.744 256c0 141.312 114.432 255.999 255.744 255.999zM255.999 -12.7998
c113.153 0 204.801 91.6484 204.801 204.801c0 113.151 -91.6484 204.799 -204.801 204.799c-113.151 0 -204.8 -91.6465 -204.8 -204.799c0 -113.153 91.6484 -204.801 204.8 -204.801z" />
<glyph glyph-name="uniF103" unicode="&#xf103;"
d="M345.6 217.601c-21.248 0 -38.3994 17.1514 -38.3994 38.3994s17.1514 38.4004 38.3994 38.4004s38.4004 -17.1523 38.4004 -38.4004s-17.1523 -38.3994 -38.4004 -38.3994zM255.872 448c141.439 0 256.128 -114.561 256.128 -255.999
c0 -141.439 -114.688 -256 -256.128 -256s-255.872 114.561 -255.872 256c0 141.438 114.432 255.999 255.872 255.999zM255.999 -12.7998c113.153 0 204.801 91.6484 204.801 204.801c0 113.151 -91.6484 204.799 -204.801 204.799
c-113.151 0 -204.8 -91.6465 -204.8 -204.799c0 -113.153 91.6484 -204.801 204.8 -204.801zM166.4 217.601c-21.248 0 -38.4004 17.1514 -38.4004 38.3994s17.1523 38.4004 38.4004 38.4004s38.3994 -17.1523 38.3994 -38.4004s-17.1514 -38.3994 -38.3994 -38.3994z
M255.999 51.2002c-59.6465 0 -110.208 37.248 -130.687 89.5996h261.375c-20.4805 -52.3516 -71.04 -89.5996 -130.688 -89.5996z" />
</font>
</defs></svg>

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -29,7 +29,7 @@ import * as circleActions from 'actions/circleActions'
import { ICircleComponentProps } from './ICircleComponentProps' import { ICircleComponentProps } from './ICircleComponentProps'
import { ICircleComponentState } from './ICircleComponentState' import { ICircleComponentState } from './ICircleComponentState'
import { Circle } from 'core/domain/circles' import { Circle, UserTie } from 'core/domain/circles'
import { Profile } from 'core/domain/users/profile' import { Profile } from 'core/domain/users/profile'
/** /**
@@ -135,21 +135,21 @@ export class CircleComponent extends Component<ICircleComponentProps, ICircleCom
} }
userList = () => { userList = () => {
const { users } = this.props.circle const { usersOfCircle } = this.props
const { userInfo } = this.props
let usersParsed: any = [] let usersParsed: any = []
if (users) { if (usersOfCircle) {
Object.keys(users).forEach((key, index) => { console.trace('usersOfCircle',usersOfCircle)
const { fullName } = users[key] Object.keys(usersOfCircle).forEach((userId, index) => {
let avatar = userInfo && userInfo[key] ? userInfo[key].avatar || '' : '' const { fullName } = usersOfCircle[userId]
let avatar = usersOfCircle && usersOfCircle[userId] ? usersOfCircle[userId].avatar || '' : ''
usersParsed.push(<ListItem usersParsed.push(<ListItem
key={`${this.props.id}.${key}`} key={`${this.props.id}.${userId}`}
style={this.styles.userListItem as any} style={this.styles.userListItem as any}
value={2} value={2}
primaryText={fullName} primaryText={fullName}
leftAvatar={<UserAvatar fullName={fullName} fileName={avatar as any} />} leftAvatar={<UserAvatar fullName={fullName!} fileName={avatar} />}
onClick={() => this.props.goTo!(`/${key}`)} onClick={() => this.props.goTo!(`/${userId}`)}
/>) />)
}) })
@@ -176,6 +176,7 @@ export class CircleComponent extends Component<ICircleComponentProps, ICircleCom
*/ */
render () { render () {
const {circle} = this.props
const circleTitle = ( const circleTitle = (
<div> <div>
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}> <div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}>
@@ -203,7 +204,7 @@ export class CircleComponent extends Component<ICircleComponentProps, ICircleCom
style={{ backgroundColor: '#fff', borderBottom: '1px solid rgba(0,0,0,0.12)', height: '72px', padding: '12px 0' }} style={{ backgroundColor: '#fff', borderBottom: '1px solid rgba(0,0,0,0.12)', height: '72px', padding: '12px 0' }}
primaryText={<span style={{ color: 'rgba(0,0,0,0.87)', fontSize: '16px', marginRight: '8px', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }}>{this.props.circle.name}</span>} primaryText={<span style={{ color: 'rgba(0,0,0,0.87)', fontSize: '16px', marginRight: '8px', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }}>{this.props.circle.name}</span>}
leftIcon={<SvgGroup style={{ width: '40px', height: '40px', transform: 'translate(0px, -9px)', fill: '#bdbdbd' }} />} leftIcon={<SvgGroup style={{ width: '40px', height: '40px', transform: 'translate(0px, -9px)', fill: '#bdbdbd' }} />}
rightIconButton={this.rightIconMenu} rightIconButton={!circle.isSystem ? this.rightIconMenu : null}
initiallyOpen={false} initiallyOpen={false}
onClick={this.handleToggleCircle} onClick={this.handleToggleCircle}
open={this.state.open} open={this.state.open}
@@ -259,10 +260,23 @@ const mapDispatchToProps = (dispatch: any, ownProps: ICircleComponentProps) => {
*/ */
const mapStateToProps = (state: any, ownProps: ICircleComponentProps) => { const mapStateToProps = (state: any, ownProps: ICircleComponentProps) => {
const {circle, authorize, server} = state const {circle, authorize, server} = state
const {userTies} = circle
const { uid } = state.authorize const { uid } = state.authorize
const circles: { [circleId: string]: Circle } = circle ? (circle.circleList || {}) : {} const circles: { [circleId: string]: Circle } = circle ? (circle.circleList || {}) : {}
const currentCircle = (circles ? circles[ownProps.id] : {}) as Circle const currentCircle = (circles ? circles[ownProps.id] : {}) as Circle
let usersOfCircle: {[userId: string]: UserTie} = {}
Object.keys(userTies).forEach((userTieId) => {
const theUserTie = userTies[userTieId] as UserTie
if (theUserTie.circleIdList!.indexOf(ownProps.id) > -1) {
usersOfCircle = {
...usersOfCircle,
[userTieId]: theUserTie
}
}
})
return { return {
usersOfCircle,
openSetting: state.circle ? (currentCircle ? (currentCircle.openCircleSettings || false) : false) : false, openSetting: state.circle ? (currentCircle ? (currentCircle.openCircleSettings || false) : false) : false,
userInfo: state.user.info userInfo: state.user.info

View File

@@ -1,6 +1,6 @@
import { Comment } from 'core/domain/comments' import { Comment } from 'core/domain/comments'
import { Profile } from 'core/domain/users' import { Profile } from 'core/domain/users'
import { Circle } from 'core/domain/circles' import { Circle, UserTie } from 'core/domain/circles'
export interface ICircleComponentProps { export interface ICircleComponentProps {
@@ -45,12 +45,9 @@ export interface ICircleComponentProps {
deleteCircle?: Function deleteCircle?: Function
/** /**
* Users profile * Users of current circle
*
* @type {{[userId: string]: Profile}}
* @memberof ICircleComponentProps
*/ */
userInfo?: {[userId: string]: Profile} usersOfCircle?: {[userId: string]: UserTie}
/** /**
* Close setting box of circle * Close setting box of circle

View File

@@ -20,6 +20,6 @@ export interface IFindPeopleComponentProps {
/** /**
* If there are more people {true} or not {false} * If there are more people {true} or not {false}
*/ */
hasMorePeople: boolean hasMorePeople?: boolean
} }

View File

@@ -83,8 +83,9 @@ const mapStateToProps = (state: any,ownProps: IFollowersComponentProps) => {
const {circle, authorize, server} = state const {circle, authorize, server} = state
const { uid } = state.authorize const { uid } = state.authorize
const circles: { [circleId: string]: Circle } = circle ? (circle.circleList || {}) : {} const circles: { [circleId: string]: Circle } = circle ? (circle.circleList || {}) : {}
const followers = circle ? circle.userTieds : {}
return{ return{
followers: circles ? circles.userTieds : {} followers
} }
} }

View File

@@ -6,11 +6,11 @@ import PropTypes from 'prop-types'
// - Import app components // - Import app components
import UserBoxList from 'components/userBoxList' import UserBoxList from 'components/userBoxList'
import { Circle } from 'core/domain/circles'
// - Import API // - Import API
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

View File

@@ -26,7 +26,6 @@ import PostPage from 'components/postPage'
import People from 'components/people' import People from 'components/people'
// - Import API // - Import API
import CircleAPI from 'api/CircleAPI'
// - Import actions // - Import actions
// - Import actions // - Import actions
@@ -130,7 +129,7 @@ export class HomeComponent extends Component<IHomeComponentProps, IHomeComponent
* @memberof Home * @memberof Home
*/ */
render () { render () {
const {loaded, authed, loadDataStream, mergedPosts, hasMorePosts} = this.props const {loaded, authed, loadDataStream, mergedPosts, hasMorePosts, showSendFeedback} = 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} />
@@ -147,7 +146,7 @@ export class HomeComponent extends Component<IHomeComponentProps, IHomeComponent
<NavLink to='/people'><MenuItem primaryText='People' style={{ color: 'rgb(117, 117, 117)' }} leftIcon={<SvgPeople />} /></NavLink> <NavLink to='/people'><MenuItem primaryText='People' style={{ color: 'rgb(117, 117, 117)' }} leftIcon={<SvgPeople />} /></NavLink>
<Divider /> <Divider />
<NavLink to='/settings'><MenuItem primaryText='Settings' style={{ color: 'rgb(117, 117, 117)' }} leftIcon={<SvgSettings />} /></NavLink> <NavLink to='/settings'><MenuItem primaryText='Settings' style={{ color: 'rgb(117, 117, 117)' }} leftIcon={<SvgSettings />} /></NavLink>
<NavLink to='#'><MenuItem primaryText='Send feedback' style={{ color: 'rgb(117, 117, 117)' }} leftIcon={<SvgFeedback />} /></NavLink> <MenuItem primaryText='Send feedback' onClick={() => showSendFeedback()} style={{ color: 'rgb(117, 117, 117)' }} leftIcon={<SvgFeedback />} />
</Menu> </Menu>
</SidebarContent> </SidebarContent>
@@ -175,6 +174,7 @@ const mapDispatchToProps = (dispatch: any, ownProps: IHomeComponentProps) => {
dispatch(notifyActions.dbGetNotifications()) dispatch(notifyActions.dbGetNotifications())
dispatch(circleActions.dbGetCircles()) dispatch(circleActions.dbGetCircles())
dispatch(circleActions.dbGetUserTies()) dispatch(circleActions.dbGetUserTies())
dispatch(circleActions.dbGetFollowers())
}, },
clearData: () => { clearData: () => {
@@ -192,7 +192,10 @@ const mapDispatchToProps = (dispatch: any, ownProps: IHomeComponentProps) => {
defaultDataEnable: () => { defaultDataEnable: () => {
dispatch(globalActions.defaultDataEnable()) dispatch(globalActions.defaultDataEnable())
}, },
goTo: (url: string) => dispatch(push(url)) goTo: (url: string) => dispatch(push(url)),
showSendFeedback: () => dispatch(globalActions.showSendFeedback()),
hideSendFeedback: () => dispatch(globalActions.hideSendFeedback())
} }
} }
@@ -223,7 +226,7 @@ const mapStateToProps = (state: any, ownProps: IHomeComponentProps) => {
mergedPosts, mergedPosts,
global, global,
hasMorePosts, hasMorePosts,
loaded: user.loaded && post.loaded && imageGallery.loaded && notify.loaded && circle.loaded loaded: user.loaded && imageGallery.loaded && notify.loaded && circle.loaded
} }
} }

View File

@@ -98,4 +98,14 @@ export interface IHomeComponentProps {
*/ */
loaded?: boolean loaded?: boolean
/**
* Show send feedback form
*/
showSendFeedback: () => any
/**
* Hide send feedback form
*/
hideSendFeedback: () => any
} }

View File

@@ -1,4 +1,4 @@
import { User } from 'core/domain/users'; import { User } from 'core/domain/users'
export interface IMasterComponentProps { export interface IMasterComponentProps {
/** /**
* Close gloal message * Close gloal message
@@ -98,4 +98,15 @@ export interface IMasterComponentProps {
* @memberof IMasterProps * @memberof IMasterProps
*/ */
uid: string uid: string
/**
* Show master loading
*/
showMasterLoading?: () => any
/**
* Hide master loading
*/
hideMasterLoading?: () => any
} }

View File

@@ -10,6 +10,7 @@ import LinearProgress from 'material-ui/LinearProgress'
// - Import components // - Import components
import MasterLoading from 'components/masterLoading' import MasterLoading from 'components/masterLoading'
import SendFeedback from 'components/sendFeedback'
import MasterRouter from 'routes/MasterRouter' import MasterRouter from 'routes/MasterRouter'
import { IMasterComponentProps } from './IMasterComponentProps' import { IMasterComponentProps } from './IMasterComponentProps'
import { IMasterComponentState } from './IMasterComponentState' import { IMasterComponentState } from './IMasterComponentState'
@@ -79,9 +80,20 @@ export class MasterComponent extends Component<IMasterComponentProps, IMasterCom
componentDidMount () { componentDidMount () {
this._authourizeService.onAuthStateChanged((isVerifide: boolean, user: any) => { this._authourizeService.onAuthStateChanged((isVerifide: boolean, user: any) => {
const {global, clearData, loadDataGuest, defaultDataDisable, defaultDataEnable, login, logout } = this.props const {
global,
clearData,
loadDataGuest,
defaultDataDisable,
defaultDataEnable,
login,
logout,
showMasterLoading,
hideMasterLoading
} = this.props
if (user) { if (user) {
login(user.uid,isVerifide) login(user.uid,isVerifide)
hideMasterLoading!()
this.setState({ this.setState({
loading: false, loading: false,
isVerifide: true isVerifide: true
@@ -89,6 +101,7 @@ export class MasterComponent extends Component<IMasterComponentProps, IMasterCom
} else { } else {
logout() logout()
hideMasterLoading!()
this.setState({ this.setState({
loading: false, loading: false,
isVerifide: false isVerifide: false
@@ -117,14 +130,14 @@ export class MasterComponent extends Component<IMasterComponentProps, IMasterCom
return ( return (
<div id='master'> <div id='master'>
<SendFeedback />
<div className='master__progress' style={{ display: (progress.visible ? 'block' : 'none') }}> <div className='master__progress' style={{ display: (progress.visible ? 'block' : 'none') }}>
<LinearProgress mode='determinate' value={progress.percent} /> <LinearProgress mode='determinate' value={progress.percent} />
</div> </div>
<div className='master__loading animate-fading2' style={{ display: (global.showTopLoading ? 'flex' : 'none') }}> <div className='master__loading animate-fading2' style={{ display: (global.showTopLoading ? 'flex' : 'none') }}>
<div className='title'>Loading ... </div> <div className='title'>Loading ... </div>
</div> </div>
<MasterLoading activeLoading={loading} handleLoading={this.handleLoading} /> <MasterLoading activeLoading={global.showMasterLoading} handleLoading={this.handleLoading} />
<MasterRouter enabled={!loading} data={{uid}} /> <MasterRouter enabled={!loading} data={{uid}} />
<Snackbar <Snackbar
open={this.props.global.messageOpen} open={this.props.global.messageOpen}
@@ -170,7 +183,9 @@ const mapDispatchToProps = (dispatch: any, ownProps: IMasterComponentProps) => {
}, },
loadDataGuest: () => { loadDataGuest: () => {
dispatch(globalActions.loadDataGuest()) dispatch(globalActions.loadDataGuest())
} },
showMasterLoading: () => dispatch(globalActions.showMasterLoading()),
hideMasterLoading: () => dispatch(globalActions.hideMasterLoading())
} }
} }

View File

@@ -2,13 +2,14 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import CircularProgress from 'material-ui/CircularProgress' import CircularProgress from 'material-ui/CircularProgress'
import Dialog from 'material-ui/Dialog' import Dialog from 'material-ui/Dialog'
import RefreshIndicator from 'material-ui/RefreshIndicator'
import { IMasterLoadingComponentProps } from './IMasterLoadingComponentProps' import { IMasterLoadingComponentProps } from './IMasterLoadingComponentProps'
import { IMasterLoadingComponentState } from './IMasterLoadingComponentState' import { IMasterLoadingComponentState } from './IMasterLoadingComponentState'
// - Import app components // - Import app components
// - Create MasterLoading component class // - Create MasterLoading component class
export default class MasterLoadingComponent extends Component<IMasterLoadingComponentProps,IMasterLoadingComponentState> { export default class MasterLoadingComponent extends Component<IMasterLoadingComponentProps, IMasterLoadingComponentState> {
// Constructor // Constructor
constructor (props: IMasterLoadingComponentProps) { constructor (props: IMasterLoadingComponentProps) {
@@ -19,27 +20,19 @@ export default class MasterLoadingComponent extends Component<IMasterLoadingComp
// Render app DOM component // Render app DOM component
render () { render () {
const {activeLoading} = this.props
return ( return (
<Dialog
modal={true}
open={this.props.activeLoading}
autoDetectWindowHeight={false}
overlayStyle={{backgroundColor: 'white'}}
contentClassName='mLoading__content'
bodyStyle={{backgroundColor: ''}}
bodyClassName='mLoading__body'
>
<div>
<div className='mLoading__context'>
<CircularProgress color='white' size={80} thickness={7} />
<h1 style={{float: 'right', color: '#fff'}}>Green</h1>
<div className='mLoading__loading' style={{ display: (activeLoading ? 'flex' : 'none') }}>
<RefreshIndicator
size={50}
left={70}
top={0}
status='loading'
/>
</div> </div>
</div>
</Dialog>
) )
} }

View File

@@ -0,0 +1,30 @@
import { Feed } from 'core/domain/common/feed'
import { ServerRequestModel } from 'models/server/serverRequestModel'
import { Profile } from 'core/domain/users'
export interface ISendFeedbackComponentProps {
/**
* Whether send feedback is diplayed
*/
sendFeedbackStatus?: boolean
/**
* Send feedback
*/
sendFeed?: (feed: Feed) => any
/**
* Hide feedback form
*/
hideFeedback: () => any
/**
* The server request of send feedback
*/
sendFeedbackRequest: ServerRequestModel
/**
* Current user profile
*/
currentUser: Profile
}

View File

@@ -0,0 +1,7 @@
export interface ISendFeedbackComponentState {
/**
* Feedback text
*/
feedText: string
}

View File

@@ -0,0 +1,232 @@
// - Import react components
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import Paper from 'material-ui/Paper'
import TextField from 'material-ui/TextField'
import IconButton from 'material-ui/IconButton'
import SvgHappy from 'material-ui/svg-icons/image/tag-faces'
import SvgSad from 'material-ui/svg-icons/action/face'
import SvgClose from 'material-ui/svg-icons/content/clear'
import RefreshIndicator from 'material-ui/RefreshIndicator'
// - Import app components
// - Import API
// - Import actions
import { globalActions } from 'actions'
import { Feed } from 'core/domain/common'
import { ISendFeedbackComponentProps } from './ISendFeedbackComponentProps'
import { ISendFeedbackComponentState } from './ISendFeedbackComponentState'
import { FeedType } from 'core/domain/common/feedType'
import { ServerRequestModel } from 'models/server'
import { Profile } from 'core/domain/users'
import StringAPI from 'api/StringAPI'
import { ServerRequestType } from 'constants/serverRequestType'
import { User } from 'core/domain/users'
import { ServerRequestStatusType } from 'actions/serverRequestStatusType'
/**
* Create component class
*/
export class SendFeedbackComponent extends Component<ISendFeedbackComponentProps, ISendFeedbackComponentState> {
/**
* Component constructor
* @param {object} props is an object properties of component
*/
constructor(props: ISendFeedbackComponentProps) {
super(props)
// Defaul state
this.state = {
feedText: ''
}
// Binding functions to `this`
this.handleFeedText = this.handleFeedText.bind(this)
this.getFeedbackForm = this.getFeedbackForm.bind(this)
this.mainForm = this.mainForm.bind(this)
this.loadingForm = this.loadingForm.bind(this)
this.successForm = this.successForm.bind(this)
this.errorForm = this.errorForm.bind(this)
}
handleFeedText = (event: any) => {
const target = event ? event.target : {}
const value = target ? target.value : ''
if (value) {
this.setState({
feedText: value
})
}
}
handleSendFeed = (feedType: FeedType) => {
const { sendFeed, currentUser } = this.props
const { feedText } = this.state
sendFeed!(new Feed('', feedText, feedType, currentUser))
}
mainForm = () => {
const { sendFeedbackStatus, hideFeedback, sendFeed, sendFeedbackRequest } = this.props
const { feedText } = this.state
return (
<div>
<TextField
hintText='What do you feel?'
multiLine={true}
onChange={this.handleFeedText}
rows={2}
rowsMax={4}
/><br />
<div className='buttons'>
<IconButton
tooltip='sad'
iconClassName='flaticon-sad-2 icon__svg'
tooltipPosition='bottom-left'
onClick={() => this.handleSendFeed(FeedType.Sad)}
>
</IconButton>
<IconButton
tooltip='acceptable'
iconClassName='flaticon-neutral icon__svg'
tooltipPosition='bottom-left'
onClick={() => this.handleSendFeed(FeedType.Acceptable)}
>
</IconButton>
<IconButton
tooltip='happy'
iconClassName='flaticon-happy-2 icon__svg'
tooltipPosition='bottom-left'
onClick={() => this.handleSendFeed(FeedType.Happy)}
>
</IconButton>
<IconButton
tooltip='awesome'
iconClassName='flaticon-happy icon__svg'
tooltipPosition='bottom-left'
onClick={() => this.handleSendFeed(FeedType.Awesome)}
>
</IconButton>
</div>
</div >)
}
loadingForm = () => {
return (<div className='loading'>
<p>
Your feedback is sending!
</p>
<div className='icon'>
<RefreshIndicator
size={50}
left={70}
top={0}
status='loading'
/>
</div>
</div>)
}
successForm = () => {
return (<div className='success'>We appreciate your kind support as always ;)</div>)
}
errorForm = () => {
return (<div className='error'>Error in sending feedback :(</div>)
}
getFeedbackForm = () => {
const { sendFeedbackStatus, hideFeedback, sendFeed, sendFeedbackRequest } = this.props
const { feedText } = this.state
if (sendFeedbackRequest) {
switch (sendFeedbackRequest.status) {
case ServerRequestStatusType.Sent:
return this.loadingForm()
case ServerRequestStatusType.OK:
return this.successForm()
case ServerRequestStatusType.Error:
return this.errorForm()
default:
return this.mainForm()
}
} else {
return this.mainForm()
}
}
/**
* Reneder component DOM
* @return {react element} return the DOM which rendered by component
*/
render() {
const { sendFeedbackStatus, hideFeedback, sendFeed, sendFeedbackRequest } = this.props
const { feedText } = this.state
return (
<div className='sendFeedback__content animate__up' style={{ display: (sendFeedbackStatus ? 'block' : 'none') }}>
<Paper className='paper' >
<div className='close'>
<IconButton
tooltip='cancel'
tooltipPosition='bottom-left'
onClick={() => hideFeedback()}
>
<SvgClose />
</IconButton>
</div>
{this.getFeedbackForm()}
</Paper>
</div>
)
}
}
/**
* Map dispatch to props
* @param {func} dispatch is the function to dispatch action to reducers
* @param {object} ownProps is the props belong to component
* @return {object} props of component
*/
const mapDispatchToProps = (dispatch: Function, ownProps: ISendFeedbackComponentProps) => {
return {
sendFeed: (feed: Feed) => (dispatch(globalActions.dbSendFeed(feed))),
hideFeedback: () => dispatch(globalActions.hideSendFeedback())
}
}
/**
* Map state to props
* @param {object} state is the obeject from redux store
* @param {object} ownProps is the props belong to component
* @return {object} props of component
*/
const mapStateToProps = (state: any, ownProps: ISendFeedbackComponentProps) => {
const { server, global, authorize, user } = state
const { request } = server
const { uid } = authorize
const currentUser: User = user.info && user.info[uid] ? { ...user.info[uid], userId: uid } : {}
const { sendFeedbackStatus } = global
const sendFeedbackRequest: ServerRequestModel = request ? request[StringAPI.createServerRequestId(ServerRequestType.CommonSendFeedback, uid)] : null
return {
sendFeedbackStatus,
sendFeedbackRequest,
currentUser
}
}
// - Connect component to redux store
export default connect(mapStateToProps, mapDispatchToProps)(SendFeedbackComponent as any)

View File

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

View File

@@ -281,4 +281,4 @@ const mapStateToProps = (state: any,ownProps: ISignupComponentProps) => {
} }
// - Connect component to redux store // - Connect component to redux store
export default withRouter(connect(mapStateToProps,mapDispatchToProps)(SignupComponent as any)) export default withRouter(connect(mapStateToProps,mapDispatchToProps)(SignupComponent as any) as any)

View File

@@ -1,6 +1,8 @@
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 { UserTie } from 'core/domain/circles' import { UserTie } from 'core/domain/circles'
import { ServerRequestStatusType } from 'actions/serverRequestStatusType'
import { ServerRequestModel } from 'models/server/serverRequestModel'
export interface IUserBoxComponentProps { export interface IUserBoxComponentProps {
@@ -73,6 +75,11 @@ export interface IUserBoxComponentProps {
*/ */
fullName?: string fullName?: string
/**
* The `Following` circle identifier of current user
*/
followingCircleId?: string
/** /**
* Create a circle * Create a circle
* *
@@ -85,14 +92,37 @@ export interface IUserBoxComponentProps {
* *
* @memberof IUserBoxComponentProps * @memberof IUserBoxComponentProps
*/ */
addFollowingUser?: (cid: string,user: UserTie) => any addUserToCircle?: (circleIds: string[],user: UserTie) => any
/** /**
* Delete * Add referer user to the `Following` circle of current user
*
* @memberof IUserBoxComponentProps
*/ */
deleteFollowingUser?: (cid: string ,followingId: string) => any followUser?: (circleId: string, userFollowing: UserTie) => any
/**
* Delete following user
*/
deleteFollowingUser?: (followingId: string) => any
/**
* Set current user selected circles for referer user
*/
setSelectedCircles?: (userId: string, circleList: string[]) => any
/**
* Remove current user selected circles for referer user
*/
removeSelectedCircles?: (userId: string, circleList: string[]) => any
/**
* Open select circle box
*/
openSelectCircles?: (userId: string) => any
/**
* Close select circle box
*/
closeSelectCircles?: (userId: string) => any
/** /**
* Redirect page to [url] * Redirect page to [url]
@@ -100,4 +130,29 @@ export interface IUserBoxComponentProps {
* @memberof IUserBoxComponentProps * @memberof IUserBoxComponentProps
*/ */
goTo?: (url: string) => any goTo?: (url: string) => any
/**
* The status of following user server request
*/
followRequest?: ServerRequestModel
/**
* The status of add to circle user server request
*/
addToCircleRequest?: ServerRequestModel
/**
* The status of deleting following user server request
*/
deleteFollowingUserRequest?: ServerRequestModel
/**
* Keep selected circles for refere user
*/
selectedCircles?: string[]
/**
* Whether the select circles box for referer user is open
*/
isSelecteCirclesOpen?: boolean
} }

View File

@@ -33,22 +33,10 @@ export interface IUserBoxComponentState {
*/ */
anchorEl?: any anchorEl?: any
/**
* Circle list popover is open {true} or not {false}
*
* @type {boolean}
* @memberof IUserBoxComponentState
*/
open: boolean
/** /**
* Whether current user changed the selected circles for referer user * Whether current user changed the selected circles for referer user
* *
*/ */
disabledDoneCircles: boolean disabledDoneCircles: boolean
/**
* Keep selected circles for refere user
*/
selectedCircles: string[]
} }

View File

@@ -1,5 +1,6 @@
// - Import react components // - Import react components
import React, { Component } from 'react' import React, { Component } from 'react'
import moment from 'moment'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { push } from 'react-router-redux' import { push } from 'react-router-redux'
@@ -20,7 +21,6 @@ import { grey400, grey800, darkBlack, lightBlack } from 'material-ui/styles/colo
import UserAvatar from 'components/userAvatar' import UserAvatar from 'components/userAvatar'
// - Import API // - Import API
import CircleAPI from 'api/CircleAPI'
import StringAPI from 'api/StringAPI' import StringAPI from 'api/StringAPI'
// - Import actions // - Import actions
@@ -31,12 +31,16 @@ import { IUserBoxComponentState } from './IUserBoxComponentState'
import { User } from 'core/domain/users' import { User } from 'core/domain/users'
import { UserTie, Circle } from 'core/domain/circles' import { UserTie, Circle } from 'core/domain/circles'
import { ServerRequestType } from 'constants/serverRequestType' import { ServerRequestType } from 'constants/serverRequestType'
import { ServerRequestStatusType } from 'actions/serverRequestStatusType'
import { ServerRequestModel } from 'models/server'
/** /**
* Create component class * Create component class
*/ */
export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBoxComponentState> { export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBoxComponentState> {
/**
* Fields
*/
static propTypes = { static propTypes = {
/** /**
* User identifier * User identifier
@@ -69,6 +73,7 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
borderRadius: '4px' borderRadius: '4px'
} }
} }
selectedCircles: string[]
/** /**
* Component constructor * Component constructor
@@ -76,17 +81,13 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
*/ */
constructor (props: IUserBoxComponentProps) { constructor (props: IUserBoxComponentProps) {
super(props) super(props)
const { userBelongCircles, circles } = this.props const { userBelongCircles, circles,userId } = this.props
// Defaul state // Defaul state
this.state = { this.state = {
/**
* It will be true if user follow popover is open
*/
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
*/ */
@@ -95,34 +96,52 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
* The button of add user in a circle is disabled {true} or not {false} * The button of add user in a circle is disabled {true} or not {false}
*/ */
disabledAddToCircle: true, disabledAddToCircle: true,
/**
* Keep selected circles for refere user
*/
selectedCircles: userBelongCircles ? userBelongCircles!.slice() : [],
/** /**
* Whether current user changed the selected circles for referer user * Whether current user changed the selected circles for referer user
*/ */
disabledDoneCircles: true disabledDoneCircles: true
} }
this.selectedCircles = userBelongCircles!.slice()
// Binding functions to `this` // Binding functions to `this`
this.handleChangeName = this.handleChangeName.bind(this) this.handleChangeName = this.handleChangeName.bind(this)
this.onCreateCircle = this.onCreateCircle.bind(this) this.onCreateCircle = this.onCreateCircle.bind(this)
this.handleFollowUser = this.handleFollowUser.bind(this) this.handleDoneAddCircle = this.handleDoneAddCircle.bind(this)
this.handleFollowUser = this.handleFollowUser.bind(this) this.circleList = this.circleList.bind(this)
} }
/** /**
* Handle follow user * Handle follow user
*/ */
handleFollowUser = (checked: boolean, cid: string) => { handleDoneAddCircle = () => {
const { userId, user } = this.props const { userId, user , addUserToCircle, selectedCircles, deleteFollowingUser} = this.props
const { avatar, fullName } = user const { avatar, fullName } = user
if (checked) { const {disabledDoneCircles} = this.state
this.props.addFollowingUser!(cid, { avatar, userId, fullName }) if (!disabledDoneCircles) {
if (selectedCircles!.length > 0) {
addUserToCircle!(selectedCircles!, { avatar, userId, fullName })
} else {
deleteFollowingUser!(userId)
}
}
}
/**
* Handle follow user
*/
onFollowUser = (event: any) => {
// This prevents ghost click
event.preventDefault()
const {isFollowed, followUser, followingCircleId, userId, user, followRequest } = this.props
if (followRequest && followRequest.status === ServerRequestStatusType.Sent) {
return
}
const { avatar, fullName } = user
if (!isFollowed) {
followUser!(followingCircleId!, { avatar, userId, fullName })
} else { } else {
this.props.deleteFollowingUser!(cid, userId) this.onRequestOpenAddCircle()
} }
} }
@@ -130,8 +149,14 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
* Handle request close for add circle box * Handle request close for add circle box
*/ */
onRequestCloseAddCircle = () => { onRequestCloseAddCircle = () => {
const {setSelectedCircles, userId, userBelongCircles, closeSelectCircles} = this.props
setSelectedCircles!(userId, userBelongCircles!)
closeSelectCircles!(userId)
this.setState({ this.setState({
open: false circleName: ``,
disabledCreateCircle: true,
disabledAddToCircle: true,
disabledDoneCircles: true
}) })
} }
@@ -139,9 +164,8 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
* Handle request open for add circle box * Handle request open for add circle box
*/ */
onRequestOpenAddCircle = () => { onRequestOpenAddCircle = () => {
this.setState({ const { openSelectCircles, userId} = this.props
open: true openSelectCircles!(userId)
})
} }
/** /**
@@ -170,54 +194,40 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
} }
handleSelectCircle = (event: object, isInputChecked: boolean, circleId: string) => { handleSelectCircle = (event: object, isInputChecked: boolean, circleId: string) => {
const { userBelongCircles, circles } = this.props const { userBelongCircles, circles, setSelectedCircles, selectedCircles, userId } = this.props
let selectedCircles = this.state.selectedCircles let newSelectedCircles = selectedCircles!.slice()
if (isInputChecked) { if (isInputChecked) {
selectedCircles = [
...selectedCircles, newSelectedCircles = [
...selectedCircles!,
circleId circleId
] ]
} else { } else {
const circleIndex = selectedCircles.indexOf(circleId) const circleIndex = selectedCircles!.indexOf(circleId)
selectedCircles.splice(circleIndex, 1) newSelectedCircles.splice(circleIndex, 1)
} }
setSelectedCircles!(userId, newSelectedCircles)
this.setState({ this.setState({
selectedCircles: selectedCircles, disabledDoneCircles: !this.selectedCircleChange(newSelectedCircles)
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 * Create a circle list of user which belong to
*/ */
circleList = () => { circleList = () => {
let { circles, userId, userBelongCircles } = this.props let { circles, userId, userBelongCircles, selectedCircles } = this.props
if (circles) { if (circles) {
return Object.keys(circles).map((circleId, index) => { const parsedDate = Object.keys(circles).map((circleId, index) => {
const {selectedCircles} = this.state let isBelong = selectedCircles ? selectedCircles!.indexOf(circleId) > -1 : false
let isBelong = selectedCircles!.indexOf(circleId) > -1
// Create checkbox for selected/unselected circle // Create checkbox for selected/unselected circle
return <Checkbox return <Checkbox
key={circleId} key={`${circleId}-${userId}`}
style={{ padding: '10px' }} style={{ padding: '10px' }}
label={circles![circleId].name} label={circles![circleId].name}
labelStyle={{ labelStyle={{
@@ -230,6 +240,8 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
checked={isBelong} checked={isBelong}
/> />
}) })
return parsedDate
} }
} }
@@ -260,6 +272,7 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
*/ */
render () { render () {
const {disabledDoneCircles} = this.state const {disabledDoneCircles} = this.state
const { isFollowed, followRequest, userId, isSelecteCirclesOpen, addToCircleRequest, deleteFollowingUserRequest } = this.props
const writeActions = [ const writeActions = [
<FlatButton <FlatButton
label='Cancel' label='Cancel'
@@ -272,15 +285,14 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
label='Done' label='Done'
primary={true} primary={true}
keyboardFocused={false} keyboardFocused={false}
disabled={disabledDoneCircles} disabled={disabledDoneCircles || (addToCircleRequest ? addToCircleRequest!.status === ServerRequestStatusType.Sent : false)}
onTouchTap={this.onCreateCircle} onTouchTap={this.handleDoneAddCircle}
/> />
] ]
const { isFollowed } = this.props
return ( return (
<Paper style={this.styles.paper} zDepth={1} className='grid-cell'> <Paper key={userId} style={this.styles.paper} zDepth={1} className='grid-cell'>
<div style={{ <div style={{
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
@@ -309,6 +321,10 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
: (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.onFollowUser} onTouchTap={this.onFollowUser}
disabled={
(followRequest ? followRequest.status === ServerRequestStatusType.Sent : false) ||
(deleteFollowingUserRequest ? deleteFollowingUserRequest.status === ServerRequestStatusType.Sent : false)
}
/> />
</div> </div>
</div> </div>
@@ -316,7 +332,7 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
key={this.props.userId || 0} key={this.props.userId || 0}
actions={writeActions} actions={writeActions}
modal={false} modal={false}
open={this.state.open} open={isSelecteCirclesOpen === true}
contentStyle={this.styles.dialog} contentStyle={this.styles.dialog}
onRequestClose={this.onRequestCloseAddCircle} onRequestClose={this.onRequestCloseAddCircle}
overlayStyle={{ background: 'rgba(0,0,0,0.12)' }} overlayStyle={{ background: 'rgba(0,0,0,0.12)' }}
@@ -363,8 +379,13 @@ export class UserBoxComponent extends Component<IUserBoxComponentProps, IUserBox
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: (circleIds: string[], user: UserTie) => dispatch(circleActions.dbUpdateUserInCircles(circleIds, user)), addUserToCircle: (circleIds: string[], user: UserTie) => dispatch(circleActions.dbUpdateUserInCircles(circleIds, user)),
deleteFollowingUser: (cid: string, followingId: string) => dispatch(circleActions.dbDeleteFollowingUser(followingId)), followUser: (circleId: string, userFollowing: UserTie) => dispatch(circleActions.dbFollowUser(circleId, userFollowing)),
deleteFollowingUser: (followingId: string) => dispatch(circleActions.dbDeleteFollowingUser(followingId)),
setSelectedCircles: (userId: string, circleList: string[]) => dispatch(circleActions.setSelectedCircles(userId, circleList)),
removeSelectedCircles: (userId: string, circleList: string[]) => dispatch(circleActions.removeSelectedCircles(userId)),
openSelectCircles: (userId: string) => dispatch(circleActions.openSelectCircleBox(userId)),
closeSelectCircles: (userId: string) => dispatch(circleActions.closeSelectCircleBox(userId)),
goTo: (url: string) => dispatch(push(url)) goTo: (url: string) => dispatch(push(url))
} }
@@ -381,13 +402,26 @@ const mapStateToProps = (state: any, ownProps: IUserBoxComponentProps) => {
const { circle, authorize, server } = state const { circle, authorize, server } = state
const { uid } = authorize const { uid } = authorize
const { request } = server const { request } = server
const circles: { [circleId: string]: Circle } = circle ? (circle.circleList || {}) : {} const circles: { [circleId: string]: Circle } = circle ? (circle.circleList || {}) : {}
const userBelongCircles = circle ? (circle.userTies[ownProps.userId] ? circle.userTies[ownProps.userId].circleIdList : []) : [] const userBelongCircles = circle ? (circle.userTies[ownProps.userId] ? circle.userTies[ownProps.userId].circleIdList : []) : []
const isFollowed = userBelongCircles.length > 0 const isFollowed = userBelongCircles.length > 0
const followingCircleId = circles ? Object.keys(circles)
.filter((circleId) => circles[circleId].isSystem && circles[circleId].name === `Following`)[0] : ''
const followRequest: ServerRequestModel = request ? request[StringAPI.createServerRequestId(ServerRequestType.CircleFollowUser, ownProps.userId)] : null
const addToCircleRequest: ServerRequestModel = request ? request[StringAPI.createServerRequestId(ServerRequestType.CircleAddToCircle, ownProps.userId)] : null
const deleteFollowingUserRequest: ServerRequestModel = request ? request[StringAPI.createServerRequestId(ServerRequestType.CircleDeleteFollowingUser, ownProps.userId)] : null
const selectedCircles = circle.selectedCircles ? circle.selectedCircles[ownProps.userId] : []
const isSelecteCirclesOpen = circle.openSelecteCircles ? circle.openSelecteCircles[ownProps.userId] : []
return { return {
isSelecteCirclesOpen,
isFollowed, isFollowed,
selectedCircles,
circles, circles,
followingCircleId,
userBelongCircles, userBelongCircles,
followRequest,
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

@@ -45,9 +45,7 @@ export class YourCirclesComponent extends Component<IYourCirclesComponentProps,I
if (circles) { if (circles) {
Object.keys(circles).map((key, index) => { Object.keys(circles).map((key, index) => {
if (key.trim() !== '-Followers') {
parsedCircles.push(<CircleComponent key={key} circle={circles![key]} id={key} uid={uid!} />) parsedCircles.push(<CircleComponent key={key} circle={circles![key]} id={key} uid={uid!} />)
}
}) })
} }
return parsedCircles return parsedCircles

View File

@@ -15,6 +15,10 @@ export enum CircleActionType {
DELETE_USER_FROM_CIRCLE = 'DELETE_USER_FROM_CIRCLE', DELETE_USER_FROM_CIRCLE = 'DELETE_USER_FROM_CIRCLE',
SHOW_SELECT_CIRCLE_BOX = 'SHOW_SELECT_CIRCLE_BOX', SHOW_SELECT_CIRCLE_BOX = 'SHOW_SELECT_CIRCLE_BOX',
HIDE_SELECT_CIRCLE_BOX = 'HIDE_SELECT_CIRCLE_BOX', HIDE_SELECT_CIRCLE_BOX = 'HIDE_SELECT_CIRCLE_BOX',
SET_SELECTED_CIRCLES_USER_BOX_COMPONENT = 'SET_SELECTED_CIRCLES_USER_BOX_COMPONENT',
REMOVE_SELECTED_CIRCLES_USER_BOX_COMPONENT = 'REMOVE_SELECTED_CIRCLES_USER_BOX_COMPONENT',
OPEN_SELECT_CIRCLES_USER_BOX_COMPONENT = 'OPEN_SELECT_CIRCLES_USER_BOX_COMPONENT',
CLOSE_SELECT_CIRCLES_USER_BOX_COMPONENT = 'CLOSE_SELECT_CIRCLES_USER_BOX_COMPONENT',
SHOW_FOLLOWING_USER_LOADING = 'SHOW_FOLLOWING_USER_LOADING', SHOW_FOLLOWING_USER_LOADING = 'SHOW_FOLLOWING_USER_LOADING',
HIDE_FOLLOWING_USER_LOADING = 'HIDE_FOLLOWING_USER_LOADING' HIDE_FOLLOWING_USER_LOADING = 'HIDE_FOLLOWING_USER_LOADING'

View File

@@ -12,6 +12,10 @@ export enum GlobalActionType {
SET_HEADER_TITLE = 'SET_HEADER_TITLE', SET_HEADER_TITLE = 'SET_HEADER_TITLE',
SHOW_TOP_LOADING = 'SHOW_TOP_LOADING', SHOW_TOP_LOADING = 'SHOW_TOP_LOADING',
HIDE_TOP_LOADING = 'HIDE_TOP_LOADING', HIDE_TOP_LOADING = 'HIDE_TOP_LOADING',
SHOW_MASTER_LOADING = 'SHOW_MASTER_LOADING',
HIDE_MASTER_LOADING = 'HIDE_MASTER_LOADING',
SHOW_SEND_FEEDBACK = 'SHOW_SEND_FEEDBACK',
HIDE_SEND_FEEDBACK = 'HIDE_SEND_FEEDBACK',
TEMP = 'TEMP', TEMP = 'TEMP',
CLEAR_TEMP = 'CLEAR_TEMP', CLEAR_TEMP = 'CLEAR_TEMP',
OPEN_POST_WRITE = 'OPEN_POST_WRITE', OPEN_POST_WRITE = 'OPEN_POST_WRITE',

View File

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

View File

@@ -35,8 +35,8 @@ export class Circle extends BaseDomain {
public name: string public name: string
/** /**
* Whether circle setting is open * Whether it's configured by system
*/ */
public openCircleSettings?: boolean public isSystem: boolean
} }

View File

@@ -0,0 +1,34 @@
import { BaseDomain } from 'core/domain/common'
import { FeedType } from './feedType'
import { User } from 'core/domain/users'
export class Feed {
/**
* Constructor
*/
constructor (
/**
* Feed identifier
*/
public id?: string,
/**
* Feed text
*/
public text?: string,
/**
* Feed type
*/
public feedType?: FeedType,
/**
* The user who send the feedback
*/
public user?: User
) {
}
}

View File

@@ -0,0 +1,8 @@
export enum FeedType {
Awesome = 'Awesome',
Happy = 'Happey',
Acceptable = 'Acceptable',
Sad = 'Sad',
Bug = 'Bug'
}

View File

@@ -1,7 +1,9 @@
import { SocialError } from './socialError' import { SocialError } from './socialError'
import { BaseDomain } from './baseDomain' import { BaseDomain } from './baseDomain'
import { Feed } from './feed'
export { export {
SocialError, SocialError,
BaseDomain BaseDomain,
} Feed
}

View File

@@ -15,6 +15,12 @@ export interface IUserTieService {
tieUseres: (userTieSenderInfo: UserTie, userTieReceiveInfo: UserTie, circleIds: string[]) tieUseres: (userTieSenderInfo: UserTie, userTieReceiveInfo: UserTie, circleIds: string[])
=> Promise<void> => Promise<void>
/**
* Update users tie
*/
updateUsersTie: (userTieSenderInfo: UserTie, userTieReceiveInfo: UserTie, circleIds: string[])
=> Promise<void>
/** /**
* Remove users' tie * Remove users' tie
*/ */

View File

@@ -1,4 +1,5 @@
import { User } from 'core/domain/users' import { User } from 'core/domain/users'
import { Feed } from 'core/domain/common';
/** /**
* Common service interface * Common service interface
@@ -8,4 +9,8 @@ import { User } from 'core/domain/users'
*/ */
export interface ICommonService { export interface ICommonService {
/**
* Post feedback
*/
addFeed: (feed: Feed) => Promise<string>
} }

View File

@@ -53,6 +53,32 @@ export class UserTieService implements IUserTieService {
}) })
} }
/**
* Update users tie
*/
public updateUsersTie: (userTieSenderInfo: UserTie, userTieReceiveInfo: UserTie, circleIds: string[])
=> Promise<void> = (userTieSenderInfo, userTieReceiveInfo, circleIds) => {
return new Promise<void>((resolve, reject) => {
this._graphService
.updateGraph(
new Graph(
userTieSenderInfo.userId!,
'TIE',
userTieReceiveInfo.userId!,
{...userTieSenderInfo},
{...userTieReceiveInfo},
{creationDate: Date.now(), circleIds}
)
,'users'
).then(() => {
resolve()
})
.catch((error: any) => reject(new SocialError(error.code, 'firestore/updateUsersTie :' + error.message)))
})
}
/** /**
* Remove users' tie * Remove users' tie
*/ */
@@ -89,7 +115,7 @@ export class UserTieService implements IUserTieService {
parsedData = { parsedData = {
...parsedData, ...parsedData,
[rightUserInfo.userId!] : { [rightUserInfo.userId!] : {
...node.rightMetadata, ...rightUserInfo,
circleIdList: metadata ? metadata.circleIds : [] circleIdList: metadata ? metadata.circleIds : []
} }
} }
@@ -123,7 +149,7 @@ export class UserTieService implements IUserTieService {
parsedData = { parsedData = {
...parsedData, ...parsedData,
[leftUserInfo.userId!] : { [leftUserInfo.userId!] : {
...parsedData[leftUserInfo.userId!], ...leftUserInfo,
circleIdList: [] circleIdList: []
} }
} }

View File

@@ -1,7 +1,7 @@
// - Import react components // - Import react components
import { firebaseRef, firebaseAuth } from 'data/firebaseClient' import { firebaseRef, firebaseAuth, db } from 'data/firestoreClient'
import { SocialError } from 'core/domain/common' import { SocialError, Feed } from 'core/domain/common'
import { ICommonService } from 'core/services/common' import { ICommonService } from 'core/services/common'
import { injectable } from 'inversify' import { injectable } from 'inversify'
@@ -15,4 +15,20 @@ import { injectable } from 'inversify'
@injectable() @injectable()
export class CommonService implements ICommonService { export class CommonService implements ICommonService {
/**
* Post feedback
*/
public addFeed: (feed: Feed)
=> Promise<string> = (feed) => {
return new Promise<string>((resolve, reject) => {
let feedRef = db.collection(`feeds`).doc()
feedRef.set({ ...feed, id: feedRef.id })
.then(() => {
resolve(feedRef.id)
})
.catch((error: any) => {
reject(new SocialError(error.code, error.message))
})
})
}
} }

View File

@@ -34,17 +34,19 @@ export class GraphService implements IGraphService {
} }
/** /**
* Add graph * Update graph
*/ */
public updateGraph: (graph: Graph, collection: string) public updateGraph: (graph: Graph, collection: string)
=> Promise<string> = (graph, collection) => { => Promise<string> = (graph, collection) => {
return new Promise<string>((resolve,reject) => { return new Promise<string>((resolve,reject) => {
const graphData = this.getGraphs(collection, graph.leftNode, graph.edgeType, graph.rightNode)
let graphRef = db.collection(`graphs:${collection}`).doc() .then((result) => {
.set({...graph}).then((result) => { graph.nodeId = result[0].nodeId
resolve() let graphRef = db.collection(`graphs:${collection}`).doc(result[0].nodeId)
}) .set({...graph}).then((result) => {
.catch((error: any) => { resolve()
})
}).catch((error: any) => {
reject(new SocialError(error.code,error.message)) reject(new SocialError(error.code,error.message))
}) })
}) })
@@ -114,19 +116,22 @@ export class GraphService implements IGraphService {
=> Promise<firebase.firestore.QuerySnapshot> = (collection, leftNode, edgeType, rightNode) => { => Promise<firebase.firestore.QuerySnapshot> = (collection, leftNode, edgeType, rightNode) => {
return new Promise<firebase.firestore.QuerySnapshot>((resolve,reject) => { return new Promise<firebase.firestore.QuerySnapshot>((resolve,reject) => {
let graphsRef = db.collection(`graphs:${collection}`) let graphsRef = db.collection(`graphs:${collection}`)
let query
if (leftNode) { if (leftNode != null) {
query = graphsRef.where('leftNode', '==', leftNode) graphsRef = graphsRef.where('leftNode', '==', leftNode)
} }
if (rightNode && rightNode != null) { if (rightNode && rightNode != null) {
query = graphsRef.where('rightNode', '==', rightNode) console.trace('getGraphsQuery', {collection, leftNode, edgeType, rightNode})
graphsRef = graphsRef.where('rightNode', '==', rightNode)
} }
if (edgeType) { if (edgeType) {
query = graphsRef.where('edgeType', '==', edgeType) graphsRef = graphsRef.where('edgeType', '==', edgeType)
} }
if (query) { if (graphsRef) {
query.get().then((result) => { graphsRef.get().then((result) => {
resolve(result) resolve(result)
}).catch((error) => reject(error)) }).catch((error) => reject(error))
} else { } else {

View File

@@ -195,7 +195,7 @@ export class PostService implements IPostService {
// a = current item in array // a = current item in array
// b = next item in array // b = next item in array
return b[bKey].creationDate! - a[aKey].creationDate! return b[bKey].creationDate! - a[aKey].creationDate!
}); })
if (lastPostId && lastPostId !== '') { if (lastPostId && lastPostId !== '') {
const lastPostIndex = sortedObjects.findIndex((arg) => { const lastPostIndex = sortedObjects.findIndex((arg) => {
return Object.keys(arg)[0] === lastPostId return Object.keys(arg)[0] === lastPostId

View File

@@ -91,7 +91,7 @@ export class UserService implements IUserService {
} }
] ]
}); })
resolve({ users: parsedData, newLastUserId }) resolve({ users: parsedData, newLastUserId })
}) })
.catch((error: any) => { .catch((error: any) => {
@@ -107,11 +107,15 @@ export class UserService implements IUserService {
return new Promise<UserProvider>((resolve,reject) => { return new Promise<UserProvider>((resolve,reject) => {
let userProviderRef = db.doc(`userProviderInfo/${userId}`) let userProviderRef = db.doc(`userProviderInfo/${userId}`)
userProviderRef.get().then((snapshot) => { userProviderRef.get().then((snapshot) => {
let userProvider: UserProvider = snapshot.data() as UserProvider || {} if (snapshot.exists) {
resolve(userProvider) let userProvider: UserProvider = snapshot.data() as UserProvider || {}
resolve(userProvider)
} else {
throw new SocialError(`firestore/getUserProviderData/notExist `, `document of userProviderRef is not exist `)
}
}) })
.catch((error: any) => { .catch((error: any) => {
reject(new SocialError(error.code, 'firestore/getUserProviderData' + error.message)) reject(new SocialError(error.code, 'firestore/getUserProviderData ' + error.message))
}) })
}) })

View File

@@ -7,39 +7,49 @@ import { Circle, UserTie } from 'core/domain/circles'
* @class CircleState * @class CircleState
*/ */
export class CircleState { export class CircleState {
/** /**
* The list of users belong to users circle * The list of users belong to users circle
* *
* @memberof CircleState * @memberof CircleState
*/ */
userTies: {[userId: string]: UserTie }= {} userTies: { [userId: string]: UserTie } = {}
/** /**
* The list of users belong to users circle * The list of users belong to users circle
* *
* @memberof CircleState * @memberof CircleState
*/ */
userTieds: {[userId: string]: UserTie }= {} userTieds: { [userId: string]: UserTie } = {}
/** /**
* The list of circle of current user * The list of circle of current user
*/ */
circleList: {[circleId: string]: Circle} circleList: { [circleId: string]: Circle }
/** /**
* Whether select circle box is open for the selected user * Whether select circle box is open for the selected user
*/ */
selectCircleStatus: {[userId: string]: boolean} selectCircleStatus: { [userId: string]: boolean }
/** /**
* Whether following loading is shown for the selected user * Whether following loading is shown for the selected user
*/ */
followingLoadingStatus: {[userId: string]: boolean} followingLoadingStatus: { [userId: string]: boolean }
/** /**
* If user circles are loaded {true} or not {false} * Keep selected circles for refere user
* */
* @memberof CircleState selectedCircles: { [userId: string]: string[] }
*/
/**
* Whether the select circles box for referer user is open
*/
openSelecteCircles: { [userId: string]: boolean }
/**
* If user circles are loaded {true} or not {false}
*
* @memberof CircleState
*/
loaded: boolean = false loaded: boolean = false
} }

View File

@@ -75,9 +75,13 @@ export let circleReducer = (state: CircleState = new CircleState(), action: ICir
...state, ...state,
userTies: { userTies: {
...state.userTies, ...state.userTies,
[payload.userTie.user.userId]: { [payload.userTie.userId]: {
...payload.userTie ...payload.userTie
} }
},
selectedCircles: {
...state.selectedCircles,
[payload.userTie.userId]: payload.userTie.circleIdList
} }
} }
@@ -98,7 +102,8 @@ export let circleReducer = (state: CircleState = new CircleState(), action: ICir
userTies: { userTies: {
...state.userTies, ...state.userTies,
...payload.userTies ...payload.userTies
} },
selectedCircles : getSelectedCircles(payload.userTies)
} }
case CircleActionType.ADD_USER_TIED_LIST: case CircleActionType.ADD_USER_TIED_LIST:
@@ -139,9 +144,9 @@ export let circleReducer = (state: CircleState = new CircleState(), action: ICir
return { return {
...state, ...state,
userTies: { userTies: {
...state.userTies,
...filteredUserTies ...filteredUserTies
} },
selectedCircles : getSelectedCircles(filteredUserTies)
} }
/** /**
@@ -208,8 +213,65 @@ export let circleReducer = (state: CircleState = new CircleState(), action: ICir
} }
} }
/**
* User box component
*/
case CircleActionType.SET_SELECTED_CIRCLES_USER_BOX_COMPONENT:
return {
...state,
selectedCircles: {
...state.selectedCircles,
[payload.userId]: payload.circleList
}
}
/**
* User box component
*/
case CircleActionType.REMOVE_SELECTED_CIRCLES_USER_BOX_COMPONENT:
return {
...state,
selectedCircles: {
...state.selectedCircles,
[payload.userId]: []
}
}
/**
* User box component
*/
case CircleActionType.OPEN_SELECT_CIRCLES_USER_BOX_COMPONENT:
return {
...state,
openSelecteCircles: {
...state.openSelecteCircles,
[payload.userId]: true
}
}
case CircleActionType.CLOSE_SELECT_CIRCLES_USER_BOX_COMPONENT:
return {
...state,
openSelecteCircles: {
...state.openSelecteCircles,
[payload.userId]: false
}
}
default: default:
return state return state
} }
} }
/**
* Map user ties selected to selected circles
*/
const getSelectedCircles = (userTies: {[userId: string]: UserTie }) => {
let selectedCircles: {[userId: string]: string[]} = {}
Object.keys(userTies).forEach((userId: string) => {
const userTie = (userTies as {[userId: string]: UserTie })[userId]
selectedCircles = {
...selectedCircles,
[userTie.userId!]: userTie.circleIdList!
}
})
return selectedCircles
}

View File

@@ -6,101 +6,122 @@
*/ */
export class GlobalState { export class GlobalState {
/** /**
* Set percent of loading progress and visibility for Master component * Set percent of loading progress and visibility for Master component
* *
* @type {{ * @type {{
* percent: number, * percent: number,
* visible: Boolean * visible: boolean
* }} * }}
* @memberof IGlobalState * @memberof IGlobalState
*/ */
progress: { progress: {
percent: number percent: number
visible: Boolean visible: boolean
} = { } = {
percent: 0, percent: 0,
visible: false visible: false
} }
/** /**
* If loading is enabled {true} or not false * If loading is enabled {true} or not false
* *
* @type {Boolean} * @type {boolean}
* @memberof IGlobalState * @memberof IGlobalState
*/ */
loadingStatus: Boolean = true loadingStatus: boolean = true
/** /**
* If user date is loaded {true} or not {false} * Whether send feedback is diplayed
* */
* @type {Boolean} sendFeedbackStatus: boolean = false
* @memberof IGlobalState
*/
defaultLoadDataStatus: Boolean = false
/** /**
* If message popup is open {true} or not {false} * If user date is loaded {true} or not {false}
* *
* @type {Boolean} * @type {boolean}
* @memberof IGlobalState * @memberof IGlobalState
*/ */
messageOpen: Boolean = false defaultLoadDataStatus: boolean = false
/** /**
* The text of popup global message * If message popup is open {true} or not {false}
* *
* @type {string} * @type {boolean}
* @memberof IGlobalState * @memberof IGlobalState
*/ */
messageOpen: boolean = false
/**
* The text of popup global message
*
* @type {string}
* @memberof IGlobalState
*/
message: string = '' message: string = ''
/** /**
* Window size * Window size
* *
* @type {number} * @type {number}
* @memberof IGlobalState * @memberof IGlobalState
*/ */
windowWidth: number = 0 windowWidth: number = 0
/** /**
* Window height * Window height
* *
* @type {number} * @type {number}
* @memberof IGlobalState * @memberof IGlobalState
*/ */
windowHeight: number = 0 windowHeight: number = 0
/** /**
* The text of website header * The text of website header
* *
* @type {string} * @type {string}
* @memberof IGlobalState * @memberof IGlobalState
*/ */
headerTitle: string = '' headerTitle: string = ''
/** /**
* Top loading is visible {true} or not {false} * Top loading is visible {true} or not {false}
* *
* @type {Boolean} * @type {boolean}
* @memberof IGlobalState * @memberof IGlobalState
*/ */
showTopLoading: Boolean = false showTopLoading: boolean = false
/** /**
* Top loading message queue * Top loading message queue
* *
* @type {number} * @type {number}
* @memberof IGlobalState * @memberof IGlobalState
*/ */
topLoadingQueue: number = 0 topLoadingQueue: number = 0
/** /**
* Temp date storage * Master loading is visible {true} or not {false}
* *
* @type {*} * @type {boolean}
* @memberof IGlobalState * @memberof IGlobalState
*/ */
showMasterLoading: boolean = true
/**
* Master loading message queue
*
* @type {number}
* @memberof IGlobalState
*/
masterLoadingQueue: number = 0
/**
* Temp date storage
*
* @type {*}
* @memberof IGlobalState
*/
temp: any = { temp: any = {
caller: [] caller: []
} }

View File

@@ -67,12 +67,22 @@ export const globalReducer = (state: GlobalState = new GlobalState(), action: IG
...state, ...state,
headerTitle: action.payload headerTitle: action.payload
} }
case GlobalActionType.HIDE_TOP_LOADING: case GlobalActionType.SHOW_SEND_FEEDBACK:
const queue = state.topLoadingQueue > 0 ? (state.topLoadingQueue - 1) : 0
return { return {
...state, ...state,
topLoadingQueue: queue, sendFeedbackStatus: true
showTopLoading: (queue > 0 ? true : false) }
case GlobalActionType.HIDE_SEND_FEEDBACK:
return {
...state,
sendFeedbackStatus: false
}
case GlobalActionType.HIDE_TOP_LOADING:
const queueTopLoading = state.topLoadingQueue > 0 ? (state.topLoadingQueue - 1) : 0
return {
...state,
topLoadingQueue: queueTopLoading,
showTopLoading: (queueTopLoading > 0 ? true : false)
} }
case GlobalActionType.SHOW_TOP_LOADING: case GlobalActionType.SHOW_TOP_LOADING:
@@ -81,6 +91,20 @@ export const globalReducer = (state: GlobalState = new GlobalState(), action: IG
topLoadingQueue: (state.topLoadingQueue + 1), topLoadingQueue: (state.topLoadingQueue + 1),
showTopLoading: true showTopLoading: true
} }
case GlobalActionType.HIDE_MASTER_LOADING:
const queueMasterLoading = state.masterLoadingQueue > 0 ? (state.masterLoadingQueue - 1) : 0
return {
...state,
masterLoadingQueue: queueMasterLoading,
showMasterLoading: (queueMasterLoading > 0 ? true : false)
}
case GlobalActionType.SHOW_MASTER_LOADING:
return {
...state,
masterLoadingQueue: (state.masterLoadingQueue + 1),
showMasterLoading: true
}
case GlobalActionType.TEMP: case GlobalActionType.TEMP:
return { return {
...state, ...state,

View File

@@ -28,7 +28,7 @@ export let serverReducer = (state: ServerState = new ServerState(), action: ISer
request: { request: {
...state.request, ...state.request,
[request.id]: { [request.id]: {
request ...request
} }
} }
} }

View File

@@ -7,7 +7,7 @@ import CommonAPI from 'api/CommonAPI'
/** /**
* Developer tools * Developer tools
*/ */
(window as any).logger = CommonAPI.logger console.trace = CommonAPI.logger
/** /**
* Initialize container * Initialize container

View File

@@ -2,11 +2,13 @@
@import 'base/grid'; @import 'base/grid';
@import 'base/animate'; @import 'base/animate';
@import 'base/icon'; @import 'base/icon';
@import 'base/flaticon';
// Component styles // Component styles
@import 'components/global'; @import 'components/global';
@import 'components/master'; @import 'components/master';
@import 'components/post'; @import 'components/post';
@import 'components/profile'; @import 'components/profile';
@import 'components/sendFeedback';
@import 'components/userBox'; @import 'components/userBox';
@import 'components/imageGallery'; @import 'components/imageGallery';
@import 'components/postWrite'; @import 'components/postWrite';

View File

@@ -0,0 +1,52 @@
/*
Flaticon icon font: Flaticon
*/
@font-face {
font-family: "Flaticon";
src: url("../assets/fonts/Flaticon.eot");
src: url("../assets/fonts/Flaticon.eot?#iefix") format("embedded-opentype"),
url("../assets/fonts/Flaticon.woff") format("woff"),
url("../assets/fonts/Flaticon.ttf") format("truetype"),
url("../assets/images/Flaticon.svg#Flaticon") format("svg");
font-weight: normal;
font-style: normal;
}
@media screen and (-webkit-min-device-pixel-ratio:0) {
@font-face {
font-family: "Flaticon";
src: url("../assets/images/Flaticon.svg#Flaticon") format("svg");
}
}
.fi:before{
display: inline-block;
font-family: "Flaticon";
font-style: normal;
font-weight: normal;
font-variant: normal;
line-height: 1;
text-decoration: inherit;
text-rendering: optimizeLegibility;
text-transform: none;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
font-smoothing: antialiased;
}
.flaticon-sad-2:before { content: "\f100"; }
.flaticon-sad-1:before { content: "\f101"; }
.flaticon-neutral:before { content: "\f102"; }
.flaticon-happy-2:before { content: "\f103"; }
.flaticon-sad:before { content: "\f104"; }
.flaticon-happy-1:before { content: "\f105"; }
.flaticon-happy:before { content: "\f106"; }
$font-Flaticon-sad-2: "\f100";
$font-Flaticon-sad-1: "\f101";
$font-Flaticon-neutral: "\f102";
$font-Flaticon-happy-2: "\f103";
$font-Flaticon-sad: "\f104";
$font-Flaticon-happy-1: "\f105";
$font-Flaticon-happy: "\f106";

View File

@@ -35,4 +35,14 @@
background-image: icon(facebook, #4267b2); background-image: icon(facebook, #4267b2);
} }
.icon__svg {
padding: 10px;
display: block;
font-family: "Flaticon";
font-size: 64px;
line-height: 1;
transform: translate(-10px, -9px);
}
// .icon-dashstroke { background-image: icon(heart, red, black, 2, 'stroke-dasharray : 2px, 1px;'); } // .icon-dashstroke { background-image: icon(heart, red, black, 2, 'stroke-dasharray : 2px, 1px;'); }

View File

@@ -65,8 +65,7 @@
outline: none !important; outline: none !important;
box-shadow: 0 2px 10px rgba(0, 0, 0, .2) !important; box-shadow: 0 2px 10px rgba(0, 0, 0, .2) !important;
div.container { div.container {
padding: 10px; padding: 10px 0px 10px 0px;
padding: 10px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
@@ -86,16 +85,18 @@
padding: 10px 0px; padding: 10px 0px;
div.item { div.item {
width: 100%; width: 100%;
height: 68px; height: 54px;
background-color: white; background-color: white;
margin-bottom: 10px; margin-bottom: 10px;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
display: flex; display: flex;
padding: 10px; padding: 10px 0px 10px 0px;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
position: relative; position: relative;
div.avatar {} div.avatar {
margin-left: 5px;
}
div.info { div.info {
margin-left: 10px; margin-left: 10px;
height: 100%; height: 100%;

View File

@@ -2,11 +2,37 @@
background-color: rgb(216, 216, 216); background-color: rgb(216, 216, 216);
} }
.mLoading__content { .mLoading__loading {
max-width: 338px !important; position: fixed;
} z-index: 2001;
height: 2em;
.mLoading__context{ width: 2em;
display: flex; overflow: show;
justify-content: space-around; margin: auto;
} top: 0;
left: 0;
bottom: 0;
right: 0;
}
/* Transparent Overlay */
.mLoading__loading:before {
content: '';
display: block;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.65);
}
/* :not(:required) hides these rules from IE9 and below */
.mLoading__loading:not(:required) {
/* hide "mLoading__loading..." text */
font: 0/0 a;
color: transparent;
text-shadow: none;
background-color: transparent;
border: 0;
}

View File

@@ -0,0 +1,37 @@
.sendFeedback__content {
position: fixed;
right: 5px;
bottom: 5px;
z-index: 1;
.paper {
width: 350px;
height: 100%;
margin: 20px;
text-align: center;
}
.buttons {
position: relative;
}
.close {
position: absolute;
top: 13px;
right: 13px;
}
.success {
padding: 30px;
}
.error {
padding: 30px;
}
.loading{
padding: 18px 0px 0px 0px;
.icon {
justify-content: center;
align-items: center;
align-content: center;
align-self: center;
}
}
}