mirror of
https://github.com/fluencelabs/fluid
synced 2025-06-29 04:51:33 +00:00
init frontend
This commit is contained in:
36
frontend/src/actions/messages.js
Normal file
36
frontend/src/actions/messages.js
Normal file
@ -0,0 +1,36 @@
|
||||
import { saveMessage } from "../utils/api";
|
||||
|
||||
//importing loading bar to show when we submit a tweet
|
||||
import { showLoading, hideLoading } from "react-redux-loading";
|
||||
|
||||
export const ADD_MESSAGE = "ADD_MESSAGE";
|
||||
export const RECEIVE_MESSAGES = "RECEIVE_MESSAGES";
|
||||
|
||||
function addMessage(message) {
|
||||
return {
|
||||
type: ADD_MESSAGE,
|
||||
message
|
||||
};
|
||||
}
|
||||
|
||||
//args: message text and the message that the newTweet is replying to, if any
|
||||
export function handleAddMessage(text, name) {
|
||||
//using getState to get the current state of our store
|
||||
return (dispatch) => {
|
||||
dispatch(showLoading());
|
||||
return saveMessage({
|
||||
text: text,
|
||||
name: name
|
||||
})
|
||||
.then(message => dispatch(addMessage(message)))
|
||||
.then(() => dispatch(hideLoading()));
|
||||
};
|
||||
}
|
||||
|
||||
//action creator
|
||||
export function receiveMessages(messages) {
|
||||
return {
|
||||
type: RECEIVE_MESSAGES,
|
||||
messages
|
||||
};
|
||||
}
|
21
frontend/src/actions/shared.js
Normal file
21
frontend/src/actions/shared.js
Normal file
@ -0,0 +1,21 @@
|
||||
import { getMessages } from "../utils/api";
|
||||
|
||||
//importing action creators
|
||||
import { receiveMessages } from "./messages";
|
||||
|
||||
//importing action creators of loading bar
|
||||
import { showLoading, hideLoading } from "react-redux-loading";
|
||||
|
||||
export function handleInitialData() {
|
||||
return dispatch => {
|
||||
//before retrieving info, show loading bar
|
||||
dispatch(showLoading());
|
||||
|
||||
return getMessages().then((messages) => {
|
||||
dispatch(receiveMessages(messages));
|
||||
|
||||
//after everything has loaded, hide loading bar
|
||||
dispatch(hideLoading());
|
||||
});
|
||||
};
|
||||
}
|
42
frontend/src/components/App.js
Normal file
42
frontend/src/components/App.js
Normal file
@ -0,0 +1,42 @@
|
||||
import React, { Component, Fragment } from "react";
|
||||
import { BrowserRouter as Router, Route } from "react-router-dom";
|
||||
import { connect } from "react-redux";
|
||||
import { handleInitialData } from "../actions/shared";
|
||||
|
||||
import LoadingBar from "react-redux-loading"; //importing the loading bar given by react-redux-loading
|
||||
|
||||
import Dashboard from "./Dashboard";
|
||||
import NewMessage from "./NewMessage";
|
||||
|
||||
class App extends Component {
|
||||
componentDidMount() {
|
||||
this.props.dispatch(handleInitialData());
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Router>
|
||||
{/* using a fragment so we don't add another element (div) to the DOM */}
|
||||
<Fragment>
|
||||
<LoadingBar />
|
||||
<div className="container">
|
||||
{this.props.loading === true ? null : (
|
||||
<div>
|
||||
<NewMessage />
|
||||
<Dashboard />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Fragment>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps({ }) {
|
||||
return {
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(App);
|
30
frontend/src/components/Dashboard.js
Normal file
30
frontend/src/components/Dashboard.js
Normal file
@ -0,0 +1,30 @@
|
||||
import React, { Component } from "react";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import Message from "./Message";
|
||||
|
||||
class Dashboard extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h3 className="center">Your Timeline</h3>
|
||||
<ul className="dashbord-list">
|
||||
{this.props.messages.map((m, i) => {
|
||||
return (
|
||||
<li key={i}>
|
||||
<Message message={m} />
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//destructuring messages from state
|
||||
function mapStateToProps(state) {
|
||||
return state;
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(Dashboard);
|
34
frontend/src/components/Message.js
Normal file
34
frontend/src/components/Message.js
Normal file
@ -0,0 +1,34 @@
|
||||
import React, { Component } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { withRouter } from "react-router-dom";
|
||||
|
||||
class Message extends Component {
|
||||
render() {
|
||||
const { message } = this.props;
|
||||
|
||||
if (message === null) {
|
||||
return <p>This message doesn't exist</p>;
|
||||
}
|
||||
|
||||
const {
|
||||
name,
|
||||
text
|
||||
} = message;
|
||||
|
||||
return (
|
||||
<div className="message-info">
|
||||
<div>
|
||||
<span>{name}</span>
|
||||
<p>{text}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(message) {
|
||||
return message;
|
||||
}
|
||||
|
||||
//using withRouter because this component is not being rendered by react router, so to have access to history props, we need to use withRouter
|
||||
export default withRouter(connect(mapStateToProps)(Message));
|
71
frontend/src/components/NewMessage.js
Normal file
71
frontend/src/components/NewMessage.js
Normal file
@ -0,0 +1,71 @@
|
||||
import React, { Component } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { handleAddMessage } from "../actions/messages";
|
||||
|
||||
class NewMessage extends Component {
|
||||
state = {
|
||||
text: "",
|
||||
name: ""
|
||||
};
|
||||
|
||||
handleChangeText = e => {
|
||||
const text = e.target.value;
|
||||
|
||||
this.setState(() => ({
|
||||
text
|
||||
}));
|
||||
};
|
||||
|
||||
handleChangeName = e => {
|
||||
const name = e.target.value;
|
||||
|
||||
this.setState(() => ({
|
||||
name
|
||||
}));
|
||||
};
|
||||
|
||||
handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
const { text, name } = this.state;
|
||||
|
||||
const { dispatch } = this.props;
|
||||
|
||||
dispatch(handleAddMessage(text, name));
|
||||
|
||||
//reset state to default
|
||||
this.setState(() => ({
|
||||
text: "",
|
||||
name: ""
|
||||
}));
|
||||
};
|
||||
|
||||
render() {
|
||||
const { text, name } = this.state;
|
||||
const messageLeft = 280 - text.length;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3 className="center">Compose new Tweet </h3>
|
||||
<form className="new-message" onSubmit={this.handleSubmit}>
|
||||
<textarea
|
||||
placeholder="Message"
|
||||
value={text}
|
||||
onChange={this.handleChangeText}
|
||||
className="textarea"
|
||||
maxLength={280}
|
||||
/>
|
||||
<input type="text" placeholder="Name" value={name} onChange={this.handleChangeName}/>
|
||||
{/* show how many characters are left */}
|
||||
{messageLeft <= 100 && <div className="message-length">{messageLeft}</div>}
|
||||
|
||||
{/* button is disabled if it's an empty string */}
|
||||
<button className="btn" type="submit" disabled={text === ""}>
|
||||
Submit
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect()(NewMessage);
|
6
frontend/src/middleware/index.js
Normal file
6
frontend/src/middleware/index.js
Normal file
@ -0,0 +1,6 @@
|
||||
import thunk from "redux-thunk";
|
||||
import logger from "./logger";
|
||||
|
||||
import { applyMiddleware } from "redux";
|
||||
|
||||
export default applyMiddleware(thunk, logger);
|
11
frontend/src/middleware/logger.js
Normal file
11
frontend/src/middleware/logger.js
Normal file
@ -0,0 +1,11 @@
|
||||
const logger = store => next => action => {
|
||||
console.group(action.type);
|
||||
console.log("The action: ", action);
|
||||
const returnValue = next(action);
|
||||
console.log("The new state: ", store.getState());
|
||||
console.groupEnd();
|
||||
|
||||
return returnValue;
|
||||
};
|
||||
|
||||
export default logger;
|
10
frontend/src/reducers/index.js
Normal file
10
frontend/src/reducers/index.js
Normal file
@ -0,0 +1,10 @@
|
||||
import { combineReducers } from "redux";
|
||||
|
||||
import messages from "./messages";
|
||||
|
||||
import { loadingBarReducer } from "react-redux-loading";
|
||||
|
||||
export default combineReducers({
|
||||
messages,
|
||||
loadingBar: loadingBarReducer
|
||||
});
|
20
frontend/src/reducers/messages.js
Normal file
20
frontend/src/reducers/messages.js
Normal file
@ -0,0 +1,20 @@
|
||||
import { RECEIVE_MESSAGES, ADD_MESSAGE } from "../actions/messages";
|
||||
|
||||
export default function messages(state = [], action) {
|
||||
switch (action.type) {
|
||||
case RECEIVE_MESSAGES:
|
||||
return action.messages;
|
||||
|
||||
case ADD_MESSAGE:
|
||||
console.log("reducer message");
|
||||
console.log(state);
|
||||
console.log(action);
|
||||
const { message } = action; //getting the newly added message from action
|
||||
state.push(message);
|
||||
|
||||
return state;
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
48
frontend/src/utils/_DATA.js
Normal file
48
frontend/src/utils/_DATA.js
Normal file
@ -0,0 +1,48 @@
|
||||
let messages = [
|
||||
{
|
||||
text: "Shoutout to all the speakers I know for whom English is not a first language, but can STILL explain a concept well. It's hard enough to give a good talk in your mother tongue!",
|
||||
name: "sarah_edo"
|
||||
},
|
||||
{
|
||||
text: "I hope one day the propTypes pendulum swings back. Such a simple yet effective API. Was one of my favorite parts of React.",
|
||||
name: "tylermcginnis"
|
||||
},
|
||||
{
|
||||
text: "Want to work at Facebook/Google/:BigCompany? Start contributing code long before you ever interview there.",
|
||||
name: "tylermcginnis"
|
||||
},
|
||||
{
|
||||
text: "Puppies 101: buy a hamper with a lid on it.",
|
||||
name: "sarah_edo"
|
||||
},
|
||||
{
|
||||
text: "Is there a metric like code coverage, but that shows lines that, if changed (in a syntactically correct way), wouldn’t cause tests to fail?",
|
||||
name: "dan_abramov",
|
||||
},
|
||||
{
|
||||
text: "React came out 'rethinking best practices'. It has since accumulated 'best practices' of its own. Let’s see if we can do better.",
|
||||
name: "dan_abramov",
|
||||
},
|
||||
{
|
||||
text: "I think I realized I like dogs so much because I can really relate to being motivated by snacks",
|
||||
name: "sarah_edo",
|
||||
},
|
||||
{
|
||||
text: "Maybe the real benefit of open source was the friendships we made along the way?",
|
||||
name: "tylermcginnis",
|
||||
}
|
||||
];
|
||||
|
||||
export function _getMessages() {
|
||||
return new Promise((res, rej) => {
|
||||
setTimeout(() => res(messages), 1000)
|
||||
})
|
||||
}
|
||||
|
||||
export function _saveMessage(message) {
|
||||
return new Promise((res, rej) => {
|
||||
setTimeout(() => {
|
||||
res(message)
|
||||
}, 1000)
|
||||
})
|
||||
}
|
38
frontend/src/utils/api.js
Normal file
38
frontend/src/utils/api.js
Normal file
@ -0,0 +1,38 @@
|
||||
import {
|
||||
_getMessages,
|
||||
_saveMessage,
|
||||
} from './_DATA.js'
|
||||
|
||||
import * as fluence from "fluence";
|
||||
|
||||
let session = fluence.directConnect("localhost", 30000, 1);
|
||||
|
||||
export function getMessages () {
|
||||
let request = {
|
||||
action: "Fetch"
|
||||
};
|
||||
return session.request(JSON.stringify(request))
|
||||
.then((r) => r.asString())
|
||||
.then((r) => {
|
||||
let posts = JSON.parse(r);
|
||||
console.log("posts");
|
||||
console.log(posts);
|
||||
return {
|
||||
messages: posts.map((p) => {
|
||||
return {
|
||||
text: p.msg,
|
||||
name: p.username
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function saveMessage(message) {
|
||||
let request = {
|
||||
action: "Post",
|
||||
msg: message.text,
|
||||
username: message.name
|
||||
};
|
||||
return session.requestAsync(request).then(() => message);
|
||||
}
|
Reference in New Issue
Block a user