diff --git a/frontend/src/actions/messages.js b/frontend/src/actions/messages.js
new file mode 100644
index 0000000..ab3fda5
--- /dev/null
+++ b/frontend/src/actions/messages.js
@@ -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
+ };
+}
diff --git a/frontend/src/actions/shared.js b/frontend/src/actions/shared.js
new file mode 100644
index 0000000..47b3503
--- /dev/null
+++ b/frontend/src/actions/shared.js
@@ -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());
+ });
+ };
+}
diff --git a/frontend/src/components/App.js b/frontend/src/components/App.js
new file mode 100644
index 0000000..4de2b95
--- /dev/null
+++ b/frontend/src/components/App.js
@@ -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 (
+
+ {/* using a fragment so we don't add another element (div) to the DOM */}
+
+
+
+ {this.props.loading === true ? null : (
+
+
+
+
+ )}
+
+
+
+ );
+ }
+}
+
+function mapStateToProps({ }) {
+ return {
+
+ };
+}
+
+export default connect(mapStateToProps)(App);
diff --git a/frontend/src/components/Dashboard.js b/frontend/src/components/Dashboard.js
new file mode 100644
index 0000000..bd6867f
--- /dev/null
+++ b/frontend/src/components/Dashboard.js
@@ -0,0 +1,30 @@
+import React, { Component } from "react";
+import { connect } from "react-redux";
+
+import Message from "./Message";
+
+class Dashboard extends Component {
+ render() {
+ return (
+
+
Your Timeline
+
+ {this.props.messages.map((m, i) => {
+ return (
+ -
+
+
+ )
+ })}
+
+
+ );
+ }
+}
+
+//destructuring messages from state
+function mapStateToProps(state) {
+ return state;
+}
+
+export default connect(mapStateToProps)(Dashboard);
diff --git a/frontend/src/components/Message.js b/frontend/src/components/Message.js
new file mode 100644
index 0000000..3c8fd1e
--- /dev/null
+++ b/frontend/src/components/Message.js
@@ -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 This message doesn't exist
;
+ }
+
+ const {
+ name,
+ text
+ } = message;
+
+ return (
+
+ );
+ }
+}
+
+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));
diff --git a/frontend/src/components/NewMessage.js b/frontend/src/components/NewMessage.js
new file mode 100644
index 0000000..9ba45ad
--- /dev/null
+++ b/frontend/src/components/NewMessage.js
@@ -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 (
+
+ );
+ }
+}
+
+export default connect()(NewMessage);
diff --git a/frontend/src/middleware/index.js b/frontend/src/middleware/index.js
new file mode 100644
index 0000000..90c5f66
--- /dev/null
+++ b/frontend/src/middleware/index.js
@@ -0,0 +1,6 @@
+import thunk from "redux-thunk";
+import logger from "./logger";
+
+import { applyMiddleware } from "redux";
+
+export default applyMiddleware(thunk, logger);
diff --git a/frontend/src/middleware/logger.js b/frontend/src/middleware/logger.js
new file mode 100644
index 0000000..dbecb0e
--- /dev/null
+++ b/frontend/src/middleware/logger.js
@@ -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;
diff --git a/frontend/src/reducers/index.js b/frontend/src/reducers/index.js
new file mode 100644
index 0000000..913c203
--- /dev/null
+++ b/frontend/src/reducers/index.js
@@ -0,0 +1,10 @@
+import { combineReducers } from "redux";
+
+import messages from "./messages";
+
+import { loadingBarReducer } from "react-redux-loading";
+
+export default combineReducers({
+ messages,
+ loadingBar: loadingBarReducer
+});
diff --git a/frontend/src/reducers/messages.js b/frontend/src/reducers/messages.js
new file mode 100644
index 0000000..44173be
--- /dev/null
+++ b/frontend/src/reducers/messages.js
@@ -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;
+ }
+}
diff --git a/frontend/src/utils/_DATA.js b/frontend/src/utils/_DATA.js
new file mode 100644
index 0000000..37f8e8b
--- /dev/null
+++ b/frontend/src/utils/_DATA.js
@@ -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)
+ })
+}
diff --git a/frontend/src/utils/api.js b/frontend/src/utils/api.js
new file mode 100644
index 0000000..34ef950
--- /dev/null
+++ b/frontend/src/utils/api.js
@@ -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);
+}