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