Skip to content
This repository was archived by the owner on Jun 28, 2022. It is now read-only.

Create Actions #5

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11,139 changes: 11,139 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 8 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@
"dependencies": {
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-scripts": "1.1.1"
"react-icons": "^4.3.1",
"react-redux": "^7.2.5",
"react-redux-loading": "^4.6.1",
"react-router-dom": "^5.3.0",
"react-scripts": "1.1.1",
"redux": "^4.1.1",
"redux-thunk": "^2.3.0"
},
"scripts": {
"start": "react-scripts start",
Expand All @@ -16,6 +22,6 @@
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/udacity/reactnd-chirper-app.git"
"url": "https://github.com/Omar7amed/reactnd-chirper-app"
}
}
8 changes: 8 additions & 0 deletions src/actions/authedUser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const SET_AUTHED_USER = 'SET_AUTHED_USER'

export function setAuthedUser (id) {
return {
type: SET_AUTHED_USER,
id
}
}
20 changes: 20 additions & 0 deletions src/actions/shared.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { getInitialData } from '../utils/api'
import { receiveTweets } from './tweets'
import { receiveUsers } from './users'
import { setAuthedUser } from './authedUser'
import { showLoading, hideLoading } from 'react-redux-loading'

const AUTHED_ID = 'dan_abramov'

export function handleInitialData() {
return (dispatch) => {
dispatch(showLoading())
return getInitialData()
.then(({ users, tweets }) => {
dispatch(receiveTweets(tweets))
dispatch(receiveUsers(users))
dispatch(setAuthedUser(AUTHED_ID))
dispatch(hideLoading())
})
}
}
59 changes: 59 additions & 0 deletions src/actions/tweets.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { saveLikeToggle , saveTweet } from "../utils/api"
import { showLoading, hideLoading } from "react-redux-loading"


export const RECEIVE_TWEETS = 'RECEIVE_TWEETS'
export const TOGGLE_TWEET = 'TOGGLE_TWEET'
export const ADD_TWEET = 'ADD_TWEET'

function addTweet (tweet) {
return{
type: ADD_TWEET,
tweet
}
}

export function handleAddTweet (text, replyingTo) {
return (dispatch , getState) => {
const { authedUser } = getState()

dispatch(showLoading())

return saveTweet({
text,
author: authedUser,
replyingTo
})
.then((tweet) => dispatch(addTweet(tweet)))
.then(() => dispatch(hideLoading()))
}
}

export function receiveTweets (tweets) {
return {
type: RECEIVE_TWEETS,
tweets,
}
}

function toggleTweet ({id, authedUser, hasLiked}) {
return {
type: TOGGLE_TWEET,
id,
authedUser,
hasLiked
}
}

export function handleToggleTweet(info) {
return (dispatch) => {
dispatch(toggleTweet(info))

return saveLikeToggle(info)
.catch ((e) => {
console.warn("Error in handleToggleTweet: ", e)
dispatch(toggleTweet(info))
alert("There was an error liking the tweet. Try again!")
})
}
}
8 changes: 8 additions & 0 deletions src/actions/users.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const RECEIVE_USERS = 'RECEIVE_USERS'

export function receiveUsers (users) {
return {
type: RECEIVE_USERS,
users,
}
}
38 changes: 33 additions & 5 deletions src/components/App.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,41 @@
import React, { Component } from 'react'
import React, { Component} from 'react'
import { BrowserRouter as Router, Route } from 'react-router-dom'
import { handleInitialData } from '../actions/shared'
import { connect } from 'react-redux'
import Dashboard from './Dashboard'
import LoadingBar from 'react-redux-loading'
import NewTweet from './NewTweet'
import TweetPage from './TweetPage'
import Nav from './Nav'

class App extends Component {
componentDidMount() {
this.props.dispatch(handleInitialData())
}

render() {
return (
<div>
Starter Code
</div>
<Router>
<LoadingBar />
<div className='container'>
<Nav />
{this.props.loading
? null
: <div>
<Route path='/' exact component={Dashboard} />
<Route path='/tweet/:id' exact component={TweetPage} />
<Route path='/new' exact component={NewTweet} />
</div>}
</div>
</Router>
)
}
}

export default App
function mapStateToProps({authedUser}) {
return {
loading: authedUser === null
}
}

export default connect(mapStateToProps)(App)
29 changes: 29 additions & 0 deletions src/components/Dashboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React, { Component } from "react"
import { connect } from "react-redux"
import Tweet from "./Tweet"

class Dashboard extends Component {
render() {
return (
<div>
<h3 className='center'>Your Timeline</h3>
<ul className='dashboard-list'>
{this.props.tweetIds.map((id) => (
<li key={id}>
<Tweet id={id}/>
</li>
))}
</ul>
</div>
)
}
}

function mapStateToProps ({ tweets }) {
return {
tweetIds: Object.keys(tweets)
.sort( (a,b) => tweets[b].timestamp - tweets[a].timestamp)
}
}

export default connect(mapStateToProps)(Dashboard)
25 changes: 25 additions & 0 deletions src/components/Nav.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React, { Component } from "react"
import { NavLink } from "react-router-dom"

class Nav extends Component {
render () {
return (
<nav className='nav'>
<ul>
<li>
<NavLink to='/' exact activeClassName='active'>
Home
</NavLink>
</li>
<li>
<NavLink to='/new' activeClassName='active'>
New Tweet
</NavLink>
</li>
</ul>
</nav>
)
}
}

export default Nav
72 changes: 72 additions & 0 deletions src/components/NewTweet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React, { Component } from "react"
import { connect } from "react-redux"
import { handleAddTweet } from "../actions/tweets"
import { Redirect } from "react-router-dom"

class NewTweet extends Component {

state = {
text: '',
toHome: false
}

handleChange = (e) => {
const text = e.target.value

this.setState(()=> ({
text
}))
}

handleSubmit = (e) => {
e.preventDefault()

const { text } = this.state
const { dispatch, id } = this.props

dispatch(handleAddTweet(text, id))

this.setState(() => ({
text: '',
toHome: id ? false : true
}))
}

render () {
const {text, toHome} = this.state

if (toHome === true) {
return <Redirect to='/'/>
}

const tweetLeft = 280 - text.length

return (
<div>
<h3 className='center'>Compose New Tweet</h3>
<form className='new-tweet' onSubmit={this.handleSubmit}>
<textarea
onChange={this.handleChange}
placeholder="What's happening?"
value={text}
className='textarea'
maxLength={280}
/>
{tweetLeft <= 100 && (
<div className='tweet-length'>
{ tweetLeft }
</div>
)}
<button
className='btn'
type='submit'
disabled={text === ''} >
Submit
</button>
</form>
</div>
)
}
}

export default connect()(NewTweet)
83 changes: 83 additions & 0 deletions src/components/Tweet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React, { Component } from "react"
import { connect } from "react-redux"
import { formatTweet, formatDate } from "../utils/helpers"
import { TiArrowBackOutline, TiHeartOutline, TiHeartFullOutline} from "react-icons/ti/index"
import { handleToggleTweet } from "../actions/tweets"
import { Link, withRouter } from "react-router-dom"

class Tweet extends Component {
toParent = (e, id) => {
e.preventDefault()
this.props.history.push(`/tweet/${id}`)
}

handleLike = (e) => {
e.preventDefault()

const { dispatch, authedUser, tweet } = this.props

dispatch(handleToggleTweet({
id: tweet.id,
hasLiked: tweet.hasLiked,
authedUser
}))
}

render(){
const { tweet } = this.props

if (tweet === null) {
return <p>This Tweet Does Not Exist.</p>
}

const {
name, avatar, likes, replies, parent, text, timestamp, id,hasLiked
} = tweet

return (
<Link to={`/tweet/${id}`} className='tweet'>
<img
className='avatar'
src={avatar}
alt={`Avatar of ${name}`}
/>
<div className='tweet-info'>
<div>
<span> {name} </span>
<div> {formatDate(timestamp)} </div>
{parent && (
<button className='replying-to' onClick={ (e) => this.toParent(e, parent.id)}>
Replying to @{parent.author}
</button>
)}
<p>{text}</p>
</div>
<div className='tweet-icons'>
<TiArrowBackOutline className='tweet-icon'/>
<span>{replies !== 0 && replies}</span>
<button className='heart-button' onClick={this.handleLike}>
{ hasLiked === true
? <TiHeartFullOutline className='tweet-icon' color='#e0245e'/>
: <TiHeartOutline className='tweet-icon' /> }
</button>
<span>{likes !== 0 && likes}</span>
</div>
</div>
</Link>
)
}
}

function mapStateToProps({authedUser, tweets, users} , { id }) {
const tweet = tweets[id]
const parentTweet = tweet ? tweets[tweet.replyingTo] : null

return {
authedUser,
tweet : tweet
? formatTweet(tweet, users[tweet.author], authedUser , parentTweet)
: null
}
}

export default withRouter(connect(mapStateToProps)(Tweet))
Loading