Cleaning up action names and compose drawer
This commit is contained in:
		
							
								
								
									
										242
									
								
								.eslintrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										242
									
								
								.eslintrc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,242 @@ | ||||
| { | ||||
|   "env": { | ||||
|     "browser": true, | ||||
|     "node": false, | ||||
|     "es6": true | ||||
|   }, | ||||
|  | ||||
|   "plugins": ["react"], | ||||
|  | ||||
|   "ecmaFeatures": { | ||||
|     "arrowFunctions": true, | ||||
|     "binaryLiterals": true, | ||||
|     "blockBindings": true, | ||||
|     "classes": true, | ||||
|     "defaultParams": true, | ||||
|     "destructuring": true, | ||||
|     "forOf": true, | ||||
|     "generators": true, | ||||
|     "modules": true, | ||||
|     "objectLiteralComputedProperties": true, | ||||
|     "objectLiteralDuplicateProperties": true, | ||||
|     "objectLiteralShorthandMethods": true, | ||||
|     "objectLiteralShorthandProperties": true, | ||||
|     "octalLiterals": true, | ||||
|     "regexUFlag": true, | ||||
|     "regexYFlag": true, | ||||
|     "spread": true, | ||||
|     "superInFunctions": true, | ||||
|     "templateStrings": true, | ||||
|     "unicodeCodePointEscapes": true, | ||||
|     "globalReturn": true, | ||||
|     "jsx": true | ||||
|   }, | ||||
|  | ||||
|   "rules": { | ||||
|  | ||||
|     // | ||||
|     //Possible Errors | ||||
|     // | ||||
|     // The following rules point out areas where you might have made mistakes. | ||||
|     // | ||||
|     "comma-dangle": 2, // disallow or enforce trailing commas | ||||
|     "no-cond-assign": 2, // disallow assignment in conditional expressions | ||||
|     "no-console": 1, // disallow use of console (off by default in the node environment) | ||||
|     "no-constant-condition": 2, // disallow use of constant expressions in conditions | ||||
|     "no-control-regex": 2, // disallow control characters in regular expressions | ||||
|     "no-debugger": 2, // disallow use of debugger | ||||
|     "no-dupe-args": 2, // disallow duplicate arguments in functions | ||||
|     "no-dupe-keys": 2, // disallow duplicate keys when creating object literals | ||||
|     "no-duplicate-case": 2, // disallow a duplicate case label. | ||||
|     "no-empty": 2, // disallow empty statements | ||||
|     "no-empty-class": 2, // disallow the use of empty character classes in regular expressions | ||||
|     "no-ex-assign": 2, // disallow assigning to the exception in a catch block | ||||
|     "no-extra-boolean-cast": 2, // disallow double-negation boolean casts in a boolean context | ||||
|     "no-extra-parens": 0, // disallow unnecessary parentheses (off by default) | ||||
|     "no-extra-semi": 2, // disallow unnecessary semicolons | ||||
|     "no-func-assign": 2, // disallow overwriting functions written as function declarations | ||||
|     "no-inner-declarations": 2, // disallow function or variable declarations in nested blocks | ||||
|     "no-invalid-regexp": 2, // disallow invalid regular expression strings in the RegExp constructor | ||||
|     "no-irregular-whitespace": 2, // disallow irregular whitespace outside of strings and comments | ||||
|     "no-negated-in-lhs": 2, // disallow negation of the left operand of an in expression | ||||
|     "no-obj-calls": 2, // disallow the use of object properties of the global object (Math and JSON) as functions | ||||
|     "no-regex-spaces": 2, // disallow multiple spaces in a regular expression literal | ||||
|     "no-reserved-keys": 2, // disallow reserved words being used as object literal keys (off by default) | ||||
|     "no-sparse-arrays": 2, // disallow sparse arrays | ||||
|     "no-unreachable": 2, // disallow unreachable statements after a return, throw, continue, or break statement | ||||
|     "use-isnan": 2, // disallow comparisons with the value NaN | ||||
|     "valid-jsdoc": 2, // Ensure JSDoc comments are valid (off by default) | ||||
|     "valid-typeof": 2, // Ensure that the results of typeof are compared against a valid string | ||||
|  | ||||
|     // | ||||
|     // Best Practices | ||||
|     // | ||||
|     // These are rules designed to prevent you from making mistakes. | ||||
|     // They either prescribe a better way of doing something or help you avoid footguns. | ||||
|     // | ||||
|     "block-scoped-var": 0, // treat var statements as if they were block scoped (off by default). 0: deep destructuring is not compatible https://github.com/eslint/eslint/issues/1863 | ||||
|     "complexity": 0, // specify the maximum cyclomatic complexity allowed in a program (off by default) | ||||
|     "consistent-return": 2, // require return statements to either always or never specify values | ||||
|     "curly": 2, // specify curly brace conventions for all control statements | ||||
|     "default-case": 2, // require default case in switch statements (off by default) | ||||
|     "dot-notation": 2, // encourages use of dot notation whenever possible | ||||
|     "eqeqeq": 2, // require the use of === and !== | ||||
|     "guard-for-in": 2, // make sure for-in loops have an if statement (off by default) | ||||
|     "no-alert": 2, // disallow the use of alert, confirm, and prompt | ||||
|     "no-caller": 2, // disallow use of arguments.caller or arguments.callee | ||||
|     "no-div-regex": 2, // disallow division operators explicitly at beginning of regular expression (off by default) | ||||
|     "no-else-return": 2, // disallow else after a return in an if (off by default) | ||||
|     "no-empty-label": 2, // disallow use of labels for anything other then loops and switches | ||||
|     "no-eq-null": 2, // disallow comparisons to null without a type-checking operator (off by default) | ||||
|     "no-eval": 2, // disallow use of eval() | ||||
|     "no-extend-native": 2, // disallow adding to native types | ||||
|     "no-extra-bind": 2, // disallow unnecessary function binding | ||||
|     "no-fallthrough": 2, // disallow fallthrough of case statements | ||||
|     "no-floating-decimal": 2, // disallow the use of leading or trailing decimal points in numeric literals (off by default) | ||||
|     "no-implied-eval": 2, // disallow use of eval()-like methods | ||||
|     "no-iterator": 2, // disallow usage of __iterator__ property | ||||
|     "no-labels": 2, // disallow use of labeled statements | ||||
|     "no-lone-blocks": 2, // disallow unnecessary nested blocks | ||||
|     "no-loop-func": 2, // disallow creation of functions within loops | ||||
|     "no-multi-spaces": 2, // disallow use of multiple spaces | ||||
|     "no-multi-str": 2, // disallow use of multiline strings | ||||
|     "no-native-reassign": 2, // disallow reassignments of native objects | ||||
|     "no-new": 2, // disallow use of new operator when not part of the assignment or comparison | ||||
|     "no-new-func": 2, // disallow use of new operator for Function object | ||||
|     "no-new-wrappers": 2, // disallows creating new instances of String,Number, and Boolean | ||||
|     "no-octal": 2, // disallow use of octal literals | ||||
|     "no-octal-escape": 2, // disallow use of octal escape sequences in string literals, such as var foo = "Copyright \251"; | ||||
|     "no-param-reassign": 2, // disallow reassignment of function parameters (off by default) | ||||
|     "no-process-env": 2, // disallow use of process.env (off by default) | ||||
|     "no-proto": 2, // disallow usage of __proto__ property | ||||
|     "no-redeclare": 2, // disallow declaring the same variable more then once | ||||
|     "no-return-assign": 2, // disallow use of assignment in return statement | ||||
|     "no-script-url": 2, // disallow use of javascript: urls. | ||||
|     "no-self-compare": 2, // disallow comparisons where both sides are exactly the same (off by default) | ||||
|     "no-sequences": 2, // disallow use of comma operator | ||||
|     "no-throw-literal": 2, // restrict what can be thrown as an exception (off by default) | ||||
|     "no-unused-expressions": 2, // disallow usage of expressions in statement position | ||||
|     "no-void": 2, // disallow use of void operator (off by default) | ||||
|     "no-warning-comments": [0, {"terms": ["todo", "fixme"], "location": "start"}], // disallow usage of configurable warning terms in comments": 2, // e.g. TODO or FIXME (off by default) | ||||
|     "no-with": 2, // disallow use of the with statement | ||||
|     "radix": 2, // require use of the second argument for parseInt() (off by default) | ||||
|     "vars-on-top": 2, // requires to declare all vars on top of their containing scope (off by default) | ||||
|     "wrap-iife": 2, // require immediate function invocation to be wrapped in parentheses (off by default) | ||||
|     "yoda": 2, // require or disallow Yoda conditions | ||||
|  | ||||
|     // | ||||
|     // Strict Mode | ||||
|     // | ||||
|     // These rules relate to using strict mode. | ||||
|     // | ||||
|     "strict": 0, // controls location of Use Strict Directives. 0: required by `babel-eslint` | ||||
|  | ||||
|     // | ||||
|     // Variables | ||||
|     // | ||||
|     // These rules have to do with variable declarations. | ||||
|     // | ||||
|     "no-catch-shadow": 2, // disallow the catch clause parameter name being the same as a variable in the outer scope (off by default in the node environment) | ||||
|     "no-delete-var": 2, // disallow deletion of variables | ||||
|     "no-label-var": 2, // disallow labels that share a name with a variable | ||||
|     "no-shadow": 2, // disallow declaration of variables already declared in the outer scope | ||||
|     "no-shadow-restricted-names": 2, // disallow shadowing of names such as arguments | ||||
|     "no-undef": 2, // disallow use of undeclared variables unless mentioned in a /*global */ block | ||||
|     "no-undef-init": 2, // disallow use of undefined when initializing variables | ||||
|     "no-undefined": 2, // disallow use of undefined variable (off by default) | ||||
|     "no-unused-vars": 2, // disallow declaration of variables that are not used in the code | ||||
|     "no-use-before-define": 2, // disallow use of variables before they are defined | ||||
|  | ||||
|     // | ||||
|     //Stylistic Issues | ||||
|     // | ||||
|     // These rules are purely matters of style and are quite subjective. | ||||
|     // | ||||
|     "indent": [1, 2], // this option sets a specific tab width for your code (off by default) | ||||
|     "brace-style": 1, // enforce one true brace style (off by default) | ||||
|     "camelcase": 1, // require camel case names | ||||
|     "comma-spacing": [1, {"before": false, "after": true}], // enforce spacing before and after comma | ||||
|     "comma-style": [1, "last"], // enforce one true comma style (off by default) | ||||
|     "consistent-this": [1, "_this"], // enforces consistent naming when capturing the current execution context (off by default) | ||||
|     "eol-last": 1, // enforce newline at the end of file, with no multiple empty lines | ||||
|     "func-names": 0, // require function expressions to have a name (off by default) | ||||
|     "func-style": 0, // enforces use of function declarations or expressions (off by default) | ||||
|     "key-spacing": [1, {"beforeColon": false, "afterColon": true}], // enforces spacing between keys and values in object literal properties | ||||
|     "max-nested-callbacks": [1, 3], // specify the maximum depth callbacks can be nested (off by default) | ||||
|     "new-cap": [1, {newIsCap: true, capIsNew: false}], // require a capital letter for constructors | ||||
|     "new-parens": 1, // disallow the omission of parentheses when invoking a constructor with no arguments | ||||
|     "newline-after-var": 0, // allow/disallow an empty newline after var statement (off by default) | ||||
|     "no-array-constructor": 1, // disallow use of the Array constructor | ||||
|     "no-inline-comments": 1, // disallow comments inline after code (off by default) | ||||
|     "no-lonely-if": 1, // disallow if as the only statement in an else block (off by default) | ||||
|     "no-mixed-spaces-and-tabs": 1, // disallow mixed spaces and tabs for indentation | ||||
|     "no-multiple-empty-lines": [1, {"max": 2}], // disallow multiple empty lines (off by default) | ||||
|     "no-nested-ternary": 1, // disallow nested ternary expressions (off by default) | ||||
|     "no-new-object": 1, // disallow use of the Object constructor | ||||
|     "no-spaced-func": 1, // disallow space between function identifier and application | ||||
|     "no-ternary": 0, // disallow the use of ternary operators (off by default) | ||||
|     "no-trailing-spaces": 1, // disallow trailing whitespace at the end of lines | ||||
|     "no-underscore-dangle": 1, // disallow dangling underscores in identifiers | ||||
|     "no-wrap-func": 1, // disallow wrapping of non-IIFE statements in parens | ||||
|     "one-var": [1, "never"], // allow just one var statement per function (off by default) | ||||
|     "operator-assignment": [1, "never"], // require assignment operator shorthand where possible or prohibit it entirely (off by default) | ||||
|     "padded-blocks": [1, "never"], // enforce padding within blocks (off by default) | ||||
|     "quote-props": [1, "as-needed"], // require quotes around object literal property names (off by default) | ||||
|     "quotes": [1, "single"], // specify whether double or single quotes should be used | ||||
|     "semi": [1, "always"], // require or disallow use of semicolons instead of ASI | ||||
|     "semi-spacing": [1, {"before": false, "after": true}], // enforce spacing before and after semicolons | ||||
|     "sort-vars": 0, // sort variables within the same declaration block (off by default) | ||||
|     "space-after-keywords": [1, "always"], // require a space after certain keywords (off by default) | ||||
|     "space-before-blocks": [1, "always"], // require or disallow space before blocks (off by default) | ||||
|     "space-before-function-paren": [1, {"anonymous": "always", "named": "never"}], // require or disallow space before function opening parenthesis (off by default) | ||||
|     "space-in-brackets": [1, "never"], // require or disallow spaces inside brackets (off by default) | ||||
|     "space-in-parens": [1, "never"], // require or disallow spaces inside parentheses (off by default) | ||||
|     "space-infix-ops": [1, "always"], // require spaces around operators | ||||
|     "space-return-throw-case": [1, "always"], // require a space after return, throw, and case | ||||
|     "space-unary-ops": [1, {"words": true, "nonwords": false}], // Require or disallow spaces before/after unary operators (words on by default, nonwords off by default) | ||||
|     "spaced-line-comment": [1, "always"], // require or disallow a space immediately following the // in a line comment (off by default) | ||||
|     "wrap-regex": 0, // require regex literals to be wrapped in parentheses (off by default) | ||||
|  | ||||
|     // | ||||
|     // ECMAScript 6 | ||||
|     // | ||||
|     // These rules are only relevant to ES6 environments and are off by default. | ||||
|     // | ||||
|     "no-var": 2, // require let or const instead of var (off by default) | ||||
|     "generator-star-spacing": [2, "before"], // enforce the spacing around the * in generator functions (off by default) | ||||
|  | ||||
|     // | ||||
|     // Legacy | ||||
|     // | ||||
|     // The following rules are included for compatibility with JSHint and JSLint. | ||||
|     // While the names of the rules may not match up with the JSHint/JSLint counterpart, | ||||
|     // the functionality is the same. | ||||
|     // | ||||
|     "max-depth": [2, 3], // specify the maximum depth that blocks can be nested (off by default) | ||||
|     "max-len": [2, 100, 2], // specify the maximum length of a line in your program (off by default) | ||||
|     "max-params": [2, 5], // limits the number of parameters that can be used in the function declaration. (off by default) | ||||
|     "max-statements": 0, // specify the maximum number of statement allowed in a function (off by default) | ||||
|     "no-bitwise": 0, // disallow use of bitwise operators (off by default) | ||||
|     "no-plusplus": 2, // disallow use of unary operators, ++ and -- (off by default) | ||||
|  | ||||
|     // | ||||
|     // eslint-plugin-react | ||||
|     // | ||||
|     // React specific linting rules for ESLint | ||||
|     // | ||||
|     "react/display-name": 0, // Prevent missing displayName in a React component definition | ||||
|     "react/jsx-quotes": [2, "double", "avoid-escape"], // Enforce quote style for JSX attributes | ||||
|     "react/jsx-no-undef": 2, // Disallow undeclared variables in JSX | ||||
|     "react/jsx-sort-props": 0, // Enforce props alphabetical sorting | ||||
|     "react/jsx-uses-react": 2, // Prevent React to be incorrectly marked as unused | ||||
|     "react/jsx-uses-vars": 2, // Prevent variables used in JSX to be incorrectly marked as unused | ||||
|     "react/no-did-mount-set-state": 2, // Prevent usage of setState in componentDidMount | ||||
|     "react/no-did-update-set-state": 2, // Prevent usage of setState in componentDidUpdate | ||||
|     "react/no-multi-comp": 0, // Prevent multiple component definition per file | ||||
|     "react/no-unknown-property": 2, // Prevent usage of unknown DOM property | ||||
|     "react/prop-types": 2, // Prevent missing props validation in a React component definition | ||||
|     "react/react-in-jsx-scope": 2, // Prevent missing React when using JSX | ||||
|     "react/self-closing-comp": 2, // Prevent extra closing tags for components without children | ||||
|     "react/wrap-multilines": 2, // Prevent missing parentheses around multilines JSX | ||||
|   } | ||||
| } | ||||
							
								
								
									
										49
									
								
								app/assets/javascripts/components/actions/compose.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								app/assets/javascripts/components/actions/compose.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| import api from '../api' | ||||
|  | ||||
| export const COMPOSE_CHANGE         = 'COMPOSE_CHANGE'; | ||||
| export const COMPOSE_SUBMIT         = 'COMPOSE_SUBMIT'; | ||||
| export const COMPOSE_SUBMIT_REQUEST = 'COMPOSE_SUBMIT_REQUEST'; | ||||
| export const COMPOSE_SUBMIT_SUCCESS = 'COMPOSE_SUBMIT_SUCCESS'; | ||||
| export const COMPOSE_SUBMIT_FAIL    = 'COMPOSE_SUBMIT_FAIL'; | ||||
|  | ||||
| export function changeCompose(text) { | ||||
|   return { | ||||
|     type: COMPOSE_CHANGE, | ||||
|     text: text | ||||
|   }; | ||||
| } | ||||
|  | ||||
| export function submitCompose() { | ||||
|   return function (dispatch, getState) { | ||||
|     dispatch(submitComposeRequest()); | ||||
|  | ||||
|     api(getState).post('/api/statuses', { | ||||
|       status: getState().getIn(['compose', 'text'], ''), | ||||
|       in_reply_to_id: getState().getIn(['compose', 'in_reply_to_id'], null) | ||||
|     }).then(function (response) { | ||||
|       dispatch(submitComposeSuccess(response.data)); | ||||
|     }).catch(function (error) { | ||||
|       dispatch(submitComposeFail(error)); | ||||
|     }); | ||||
|   }; | ||||
| } | ||||
|  | ||||
| export function submitComposeRequest() { | ||||
|   return { | ||||
|     type: COMPOSE_SUBMIT_REQUEST | ||||
|   }; | ||||
| } | ||||
|  | ||||
| export function submitComposeSuccess(response) { | ||||
|   return { | ||||
|     type: COMPOSE_SUBMIT_SUCCESS | ||||
|   }; | ||||
| } | ||||
|  | ||||
| export function submitComposeFail(error) { | ||||
|   return { | ||||
|     type: COMPOSE_SUBMIT_FAIL, | ||||
|     error: error | ||||
|   }; | ||||
| } | ||||
|  | ||||
| @@ -1,8 +1,8 @@ | ||||
| export const SET_ACCESS_TOKEN = 'SET_ACCESS_TOKEN'; | ||||
| export const ACCESS_TOKEN_SET = 'ACCESS_TOKEN_SET'; | ||||
|  | ||||
| export function setAccessToken(token) { | ||||
|   return { | ||||
|     type: SET_ACCESS_TOKEN, | ||||
|     type: ACCESS_TOKEN_SET, | ||||
|     token: token | ||||
|   }; | ||||
| } | ||||
|   | ||||
| @@ -1,81 +0,0 @@ | ||||
| import fetch from 'isomorphic-fetch' | ||||
|  | ||||
| export const SET_TIMELINE = 'SET_TIMELINE'; | ||||
| export const ADD_STATUS   = 'ADD_STATUS'; | ||||
|  | ||||
| export const PUBLISH       = 'PUBLISH'; | ||||
| export const PUBLISH_START = 'PUBLISH_START'; | ||||
| export const PUBLISH_SUCC  = 'PUBLISH_SUCC'; | ||||
| export const PUBLISH_ERROR = 'PUBLISH_ERROR'; | ||||
|  | ||||
| export function setTimeline(timeline, statuses) { | ||||
|   return { | ||||
|     type: SET_TIMELINE, | ||||
|     timeline: timeline, | ||||
|     statuses: statuses | ||||
|   }; | ||||
| } | ||||
|  | ||||
| export function addStatus(timeline, status) { | ||||
|   return { | ||||
|     type: ADD_STATUS, | ||||
|     timeline: timeline, | ||||
|     status: status | ||||
|   }; | ||||
| } | ||||
|  | ||||
| export function publishStart() { | ||||
|   return { | ||||
|     type: PUBLISH_START | ||||
|   }; | ||||
| } | ||||
|  | ||||
| export function publishError(error) { | ||||
|   return { | ||||
|     type: PUBLISH_ERROR, | ||||
|     error: error | ||||
|   }; | ||||
| } | ||||
|  | ||||
| export function publishSucc(status) { | ||||
|   return { | ||||
|     type: PUBLISH_SUCC, | ||||
|     status: status | ||||
|   }; | ||||
| } | ||||
|  | ||||
| export function publish(text, in_reply_to_id) { | ||||
|   return function (dispatch, getState) { | ||||
|     const access_token = getState().getIn(['meta', 'access_token']); | ||||
|  | ||||
|     var data = new FormData(); | ||||
|  | ||||
|     data.append('status', text); | ||||
|  | ||||
|     if (in_reply_to_id !== null) { | ||||
|       data.append('in_reply_to_id', in_reply_to_id); | ||||
|     } | ||||
|  | ||||
|     dispatch(publishStart()); | ||||
|  | ||||
|     return fetch('/api/statuses', { | ||||
|       method: 'POST', | ||||
|  | ||||
|       headers: { | ||||
|         'Authorization': `Bearer ${access_token}` | ||||
|       }, | ||||
|  | ||||
|       body: data | ||||
|     }).then(function (response) { | ||||
|       return response.json(); | ||||
|     }).then(function (json) { | ||||
|       if (json.error) { | ||||
|         dispatch(publishError(json.error)); | ||||
|       } else { | ||||
|         dispatch(publishSucc(json)); | ||||
|       } | ||||
|     }).catch(function (error) { | ||||
|       dispatch(publishError(error)); | ||||
|     }); | ||||
|   }; | ||||
| } | ||||
							
								
								
									
										19
									
								
								app/assets/javascripts/components/actions/timelines.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								app/assets/javascripts/components/actions/timelines.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| export const TIMELINE_SET    = 'TIMELINE_SET'; | ||||
| export const TIMELINE_UPDATE = 'TIMELINE_UPDATE'; | ||||
|  | ||||
|  | ||||
| export function setTimeline(timeline, statuses) { | ||||
|   return { | ||||
|     type: TIMELINE_SET, | ||||
|     timeline: timeline, | ||||
|     statuses: statuses | ||||
|   }; | ||||
| } | ||||
|  | ||||
| export function updateTimeline(timeline, status) { | ||||
|   return { | ||||
|     type: TIMELINE_UPDATE, | ||||
|     timeline: timeline, | ||||
|     status: status | ||||
|   }; | ||||
| } | ||||
							
								
								
									
										11
									
								
								app/assets/javascripts/components/api.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								app/assets/javascripts/components/api.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| import axios from 'axios'; | ||||
|  | ||||
| export default getState => axios.create({ | ||||
|   headers: { | ||||
|     'Authorization': `Bearer ${getState().getIn(['meta', 'access_token'], '')}` | ||||
|   }, | ||||
|  | ||||
|   transformResponse: [function (data) { | ||||
|     return JSON.parse(data); | ||||
|   }] | ||||
| }); | ||||
| @@ -1,9 +1,13 @@ | ||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
|  | ||||
| const Avatar = React.createClass({ | ||||
|  | ||||
|   propTypes: { | ||||
|     src: React.PropTypes.string.isRequired | ||||
|   }, | ||||
|  | ||||
|   mixins: [PureRenderMixin], | ||||
|  | ||||
|   render () { | ||||
|     return ( | ||||
|       <div style={{ width: '48px', height: '48px', flex: '0 0 auto' }}> | ||||
|   | ||||
| @@ -1,12 +1,21 @@ | ||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
|  | ||||
| const Button = React.createClass({ | ||||
|  | ||||
|   propTypes: { | ||||
|     text: React.PropTypes.string.isRequired, | ||||
|     onClick: React.PropTypes.func | ||||
|     onClick: React.PropTypes.func, | ||||
|     disabled: React.PropTypes.boolean | ||||
|   }, | ||||
|  | ||||
|   mixins: [PureRenderMixin], | ||||
|  | ||||
|   handleClick (e) { | ||||
|     e.preventDefault(); | ||||
|     this.props.onClick(); | ||||
|  | ||||
|     if (!this.props.disabled) { | ||||
|       this.props.onClick(); | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   render () { | ||||
|   | ||||
| @@ -1,8 +1,13 @@ | ||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
|  | ||||
| const CharacterCounter = React.createClass({ | ||||
|  | ||||
|   propTypes: { | ||||
|     text: React.PropTypes.string.isRequired | ||||
|   }, | ||||
|  | ||||
|   mixins: [PureRenderMixin], | ||||
|  | ||||
|   render () { | ||||
|     return ( | ||||
|       <span style={{ fontSize: '16px', cursor: 'default' }}> | ||||
|   | ||||
| @@ -1,12 +1,16 @@ | ||||
| import StatusListContainer from '../containers/status_list_container'; | ||||
| import ColumnHeader        from './column_header'; | ||||
| import PureRenderMixin     from 'react-addons-pure-render-mixin'; | ||||
|  | ||||
| const Column = React.createClass({ | ||||
|  | ||||
|   propTypes: { | ||||
|     type: React.PropTypes.string | ||||
|   }, | ||||
|  | ||||
|   render: function() { | ||||
|   mixins: [PureRenderMixin], | ||||
|  | ||||
|   render () { | ||||
|     return ( | ||||
|       <div style={{ width: '380px', flex: '0 0 auto', background: '#282c37', margin: '10px', marginRight: '0', display: 'flex', flexDirection: 'column' }}> | ||||
|         <ColumnHeader type={this.props.type} /> | ||||
| @@ -14,6 +18,7 @@ const Column = React.createClass({ | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| }); | ||||
|  | ||||
| export default Column; | ||||
|   | ||||
| @@ -1,15 +1,21 @@ | ||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
|  | ||||
| const ColumnHeader = React.createClass({ | ||||
|  | ||||
|   propTypes: { | ||||
|     type: React.PropTypes.string | ||||
|   }, | ||||
|  | ||||
|   render: function() { | ||||
|   mixins: [PureRenderMixin], | ||||
|  | ||||
|   render () { | ||||
|     return ( | ||||
|       <div style={{ padding: '15px', fontSize: '16px', background: '#2f3441', flex: '0 0 auto' }}> | ||||
|         {this.props.type} | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| }); | ||||
|  | ||||
| export default ColumnHeader; | ||||
|   | ||||
| @@ -1,8 +1,11 @@ | ||||
| import Column from './column'; | ||||
| import Column          from './column'; | ||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
|  | ||||
| const ColumnsArea = React.createClass({ | ||||
|  | ||||
|   render: function() { | ||||
|   mixins: [PureRenderMixin], | ||||
|  | ||||
|   render () { | ||||
|     return ( | ||||
|       <div style={{ display: 'flex', flexDirection: 'row', flex: '1' }}> | ||||
|         <Column type='home' /> | ||||
| @@ -10,6 +13,7 @@ const ColumnsArea = React.createClass({ | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| }); | ||||
|  | ||||
| export default ColumnsArea; | ||||
|   | ||||
| @@ -1,42 +1,40 @@ | ||||
| import CharacterCounter from './character_counter'; | ||||
| import Button           from './button'; | ||||
| import { publish }      from '../actions/statuses'; | ||||
| import PureRenderMixin  from 'react-addons-pure-render-mixin'; | ||||
|  | ||||
| const ComposerDrawer = React.createClass({ | ||||
|  | ||||
|   propTypes: { | ||||
|     text: React.PropTypes.string.isRequired, | ||||
|     isSubmitting: React.PropTypes.boolean, | ||||
|     onChange: React.PropTypes.func.isRequired, | ||||
|     onSubmit: React.PropTypes.func.isRequired | ||||
|   }, | ||||
|  | ||||
|   getInitialState() { | ||||
|     return { | ||||
|       text: '' | ||||
|     }; | ||||
|   }, | ||||
|   mixins: [PureRenderMixin], | ||||
|  | ||||
|   handleChange (e) { | ||||
|     this.setState({ text: e.target.value }); | ||||
|     this.props.onChange(e.target.value); | ||||
|   }, | ||||
|  | ||||
|   handleKeyUp (e) { | ||||
|     if (e.keyCode === 13 && e.ctrlKey) { | ||||
|       this.handleSubmit(); | ||||
|       this.props.onSubmit(); | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   handleSubmit () { | ||||
|     this.props.onSubmit(this.state.text, null); | ||||
|     this.setState({ text: '' }); | ||||
|     this.props.onSubmit(); | ||||
|   }, | ||||
|  | ||||
|   render () { | ||||
|     return ( | ||||
|       <div style={{ width: '230px', background: '#454b5e', margin: '10px 0', padding: '10px' }}> | ||||
|         <textarea placeholder='What is on your mind?' value={this.state.text} onKeyUp={this.handleKeyUp} onChange={this.handleChange} style={{ display: 'block', boxSizing: 'border-box', width: '100%', height: '100px', background: '#fff', resize: 'none', border: 'none', color: '#282c37', padding: '10px', fontFamily: 'Roboto', fontSize: '14px' }} /> | ||||
|       <div style={{ width: '380px', boxSizing: 'border-box', background: '#454b5e', margin: '10px', marginRight: '0', padding: '10px' }}> | ||||
|         <textarea disabled={this.props.isSubmitting} placeholder='What is on your mind?' value={this.props.text} onKeyUp={this.handleKeyUp} onChange={this.handleChange} style={{ display: 'block', boxSizing: 'border-box', width: '100%', height: '100px', background: '#fff', resize: 'none', border: 'none', color: '#282c37', padding: '10px', fontFamily: 'Roboto', fontSize: '14px' }} /> | ||||
|  | ||||
|         <div style={{ marginTop: '10px', overflow: 'hidden' }}> | ||||
|           <div style={{ float: 'right' }}><Button text='Publish' onClick={this.handleSubmit} /></div> | ||||
|           <div style={{ float: 'right', marginRight: '16px', lineHeight: '36px' }}><CharacterCounter text={this.state.text} /></div> | ||||
|           <div style={{ float: 'right' }}><Button text='Publish' onClick={this.handleSubmit} disabled={this.props.isSubmitting} /></div> | ||||
|           <div style={{ float: 'right', marginRight: '16px', lineHeight: '36px' }}><CharacterCounter text={this.props.text} /></div> | ||||
|         </div> | ||||
|       </div> | ||||
|     ); | ||||
|   | ||||
| @@ -1,10 +1,14 @@ | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import PureRenderMixin    from 'react-addons-pure-render-mixin'; | ||||
|  | ||||
| const DisplayName = React.createClass({ | ||||
|  | ||||
|   propTypes: { | ||||
|     account: ImmutablePropTypes.map.isRequired | ||||
|   }, | ||||
|  | ||||
|   mixins: [PureRenderMixin], | ||||
|  | ||||
|   render () { | ||||
|     var displayName = this.props.account.get('display_name', this.props.account.get('username')); | ||||
|     var acct        = this.props.account.get('acct'); | ||||
|   | ||||
| @@ -1,18 +1,20 @@ | ||||
| import NavBar                  from './nav_bar'; | ||||
| import ColumnsArea             from './columns_area'; | ||||
| import ComposerDrawerContainer from '../containers/composer_drawer_container'; | ||||
| import PureRenderMixin         from 'react-addons-pure-render-mixin'; | ||||
|  | ||||
| const Frontend = React.createClass({ | ||||
|  | ||||
|   render: function() { | ||||
|   mixins: [PureRenderMixin], | ||||
|  | ||||
|   render () { | ||||
|     return ( | ||||
|       <div style={{ flex: '0 0 auto', display: 'flex', width: '100%', height: '100%', background: '#1a1c23' }}> | ||||
|         <NavBar /> | ||||
|         <ComposerDrawerContainer /> | ||||
|         <ColumnsArea /> | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| }); | ||||
|  | ||||
| export default Frontend; | ||||
|   | ||||
| @@ -1,12 +0,0 @@ | ||||
| const NavBar = React.createClass({ | ||||
|  | ||||
|   render: function() { | ||||
|     return ( | ||||
|       <div style={{ background: '#2f3441', width: '60px', margin: '10px', marginRight: '0' }}> | ||||
|  | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
| }); | ||||
|  | ||||
| export default NavBar; | ||||
| @@ -1,4 +1,5 @@ | ||||
| import moment from 'moment'; | ||||
| import moment          from 'moment'; | ||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
|  | ||||
| moment.updateLocale('en', { | ||||
|   relativeTime : { | ||||
| @@ -19,6 +20,7 @@ moment.updateLocale('en', { | ||||
| }); | ||||
|  | ||||
| const RelativeTimestamp = React.createClass({ | ||||
|  | ||||
|   getInitialState () { | ||||
|     return { | ||||
|       text: '' | ||||
| @@ -29,6 +31,8 @@ const RelativeTimestamp = React.createClass({ | ||||
|     timestamp: React.PropTypes.string.isRequired | ||||
|   }, | ||||
|  | ||||
|   mixins: [PureRenderMixin], | ||||
|  | ||||
|   componentWillMount () { | ||||
|     this._updateMomentText(); | ||||
|     this.interval = setInterval(this._updateMomentText, 6000); | ||||
|   | ||||
| @@ -2,12 +2,16 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import Avatar             from './avatar'; | ||||
| import DisplayName        from './display_name'; | ||||
| import RelativeTimestamp  from './relative_timestamp'; | ||||
| import PureRenderMixin    from 'react-addons-pure-render-mixin'; | ||||
|  | ||||
| const Status = React.createClass({ | ||||
|  | ||||
|   propTypes: { | ||||
|     status: ImmutablePropTypes.map.isRequired | ||||
|   }, | ||||
|  | ||||
|   mixins: [PureRenderMixin], | ||||
|  | ||||
|   render () { | ||||
|     var content = { __html: this.props.status.get('content') }; | ||||
|     var status  = this.props.status; | ||||
| @@ -30,6 +34,7 @@ const Status = React.createClass({ | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| }); | ||||
|  | ||||
| export default Status; | ||||
|   | ||||
| @@ -1,12 +1,16 @@ | ||||
| import Status             from './status'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import PureRenderMixin    from 'react-addons-pure-render-mixin'; | ||||
|  | ||||
| const StatusList = React.createClass({ | ||||
|  | ||||
|   propTypes: { | ||||
|     statuses: ImmutablePropTypes.list.isRequired | ||||
|   }, | ||||
|  | ||||
|   render: function() { | ||||
|   mixins: [PureRenderMixin], | ||||
|  | ||||
|   render () { | ||||
|     return ( | ||||
|       <div style={{ overflowY: 'scroll', flex: '1 1 auto' }}> | ||||
|         <div> | ||||
| @@ -17,6 +21,7 @@ const StatusList = React.createClass({ | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| }); | ||||
|  | ||||
| export default StatusList; | ||||
|   | ||||
| @@ -1,15 +1,22 @@ | ||||
| import { connect }    from 'react-redux'; | ||||
| import ComposerDrawer from '../components/composer_drawer'; | ||||
| import { publish }    from '../actions/statuses'; | ||||
| import { connect }                      from 'react-redux'; | ||||
| import ComposerDrawer                   from '../components/composer_drawer'; | ||||
| import { changeCompose, submitCompose } from '../actions/compose'; | ||||
|  | ||||
| const mapStateToProps = function (state, props) { | ||||
|   return {}; | ||||
|   return { | ||||
|     text: state.getIn(['compose', 'text']), | ||||
|     isSubmitting: state.getIn(['compose', 'isSubmitting']) | ||||
|   }; | ||||
| }; | ||||
|  | ||||
| const mapDispatchToProps = function (dispatch) { | ||||
|   return { | ||||
|     onSubmit: function (text, in_reply_to_id) { | ||||
|       dispatch(publish(text, in_reply_to_id)); | ||||
|     onChange: function (text) { | ||||
|       dispatch(changeCompose(text)); | ||||
|     }, | ||||
|  | ||||
|     onSubmit: function () { | ||||
|       dispatch(submitCompose()); | ||||
|     } | ||||
|   } | ||||
| }; | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| import { Provider }               from 'react-redux'; | ||||
| import configureStore             from '../store/configureStore'; | ||||
| import Frontend                   from '../components/frontend'; | ||||
| import { setTimeline, addStatus } from '../actions/statuses'; | ||||
| import { setAccessToken }         from '../actions/meta'; | ||||
| import PureRenderMixin            from 'react-addons-pure-render-mixin'; | ||||
| import { Provider }                    from 'react-redux'; | ||||
| import configureStore                  from '../store/configureStore'; | ||||
| import Frontend                        from '../components/frontend'; | ||||
| import { setTimeline, updateTimeline } from '../actions/timelines'; | ||||
| import { setAccessToken }              from '../actions/meta'; | ||||
| import PureRenderMixin                 from 'react-addons-pure-render-mixin'; | ||||
|  | ||||
| const store = configureStore(); | ||||
|  | ||||
| @@ -11,7 +11,7 @@ const Root = React.createClass({ | ||||
|  | ||||
|   propTypes: { | ||||
|     token: React.PropTypes.string.isRequired, | ||||
|     timelines: React.PropTypes.array | ||||
|     timelines: React.PropTypes.object | ||||
|   }, | ||||
|  | ||||
|   mixins: [PureRenderMixin], | ||||
| @@ -32,13 +32,13 @@ const Root = React.createClass({ | ||||
|         disconnected: function() {}, | ||||
|  | ||||
|         received: function(data) { | ||||
|           return store.dispatch(addStatus(data.timeline, JSON.parse(data.message))); | ||||
|           return store.dispatch(updateTimeline(data.timeline, JSON.parse(data.message))); | ||||
|         } | ||||
|       }); | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   render() { | ||||
|   render () { | ||||
|     return ( | ||||
|       <Provider store={store}> | ||||
|         <Frontend /> | ||||
|   | ||||
| @@ -3,7 +3,7 @@ import StatusList  from '../components/status_list'; | ||||
|  | ||||
| const mapStateToProps = function (state, props) { | ||||
|   return { | ||||
|     statuses: state.getIn(['statuses', props.type]) | ||||
|     statuses: state.getIn(['timelines', props.type]) | ||||
|   }; | ||||
| }; | ||||
|  | ||||
|   | ||||
							
								
								
									
										25
									
								
								app/assets/javascripts/components/reducers/compose.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								app/assets/javascripts/components/reducers/compose.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| import { COMPOSE_CHANGE, COMPOSE_SUBMIT_REQUEST, COMPOSE_SUBMIT_SUCCESS, COMPOSE_SUBMIT_FAIL } from '../actions/compose'; | ||||
| import Immutable                                                                               from 'immutable'; | ||||
|  | ||||
| const initialState = Immutable.Map({ | ||||
|   text: '', | ||||
|   in_reply_to_id: null, | ||||
|   isSubmitting: false | ||||
| }); | ||||
|  | ||||
| export default function compose(state = initialState, action) { | ||||
|   switch(action.type) { | ||||
|     case COMPOSE_CHANGE: | ||||
|       return state.set('text', action.text); | ||||
|     case COMPOSE_SUBMIT_REQUEST: | ||||
|       return state.set('isSubmitting', true); | ||||
|     case COMPOSE_SUBMIT_SUCCESS: | ||||
|       return state.withMutations(map => { | ||||
|         map.set('text', '').set('isSubmitting', false); | ||||
|       }); | ||||
|     case COMPOSE_SUBMIT_FAIL: | ||||
|       return state.set('isSubmitting', false); | ||||
|     default: | ||||
|       return state; | ||||
|   } | ||||
| } | ||||
| @@ -1,8 +1,10 @@ | ||||
| import { combineReducers } from 'redux-immutable'; | ||||
| import statuses            from './statuses'; | ||||
| import timelines           from './timelines'; | ||||
| import meta                from './meta'; | ||||
| import compose             from './compose'; | ||||
|  | ||||
| export default combineReducers({ | ||||
|   statuses, | ||||
|   meta | ||||
|   timelines, | ||||
|   meta, | ||||
|   compose | ||||
| }); | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| import { SET_ACCESS_TOKEN }         from '../actions/meta'; | ||||
| import { ACCESS_TOKEN_SET }         from '../actions/meta'; | ||||
| import Immutable                    from 'immutable'; | ||||
|  | ||||
| const initialState = Immutable.Map(); | ||||
|  | ||||
| export default function meta(state = initialState, action) { | ||||
|   switch(action.type) { | ||||
|     case SET_ACCESS_TOKEN: | ||||
|     case ACCESS_TOKEN_SET: | ||||
|       return state.set('access_token', action.token); | ||||
|     default: | ||||
|       return state; | ||||
|   | ||||
| @@ -1,13 +1,13 @@ | ||||
| import { SET_TIMELINE, ADD_STATUS } from '../actions/statuses'; | ||||
| import Immutable                    from 'immutable'; | ||||
| import { TIMELINE_SET, TIMELINE_UPDATE } from '../actions/timelines'; | ||||
| import Immutable                         from 'immutable'; | ||||
| 
 | ||||
| const initialState = Immutable.Map(); | ||||
| 
 | ||||
| export default function statuses(state = initialState, action) { | ||||
| export default function timelines(state = initialState, action) { | ||||
|   switch(action.type) { | ||||
|     case SET_TIMELINE: | ||||
|     case TIMELINE_SET: | ||||
|       return state.set(action.timeline, Immutable.fromJS(action.statuses)); | ||||
|     case ADD_STATUS: | ||||
|     case TIMELINE_UPDATE: | ||||
|       return state.update(action.timeline, function (list) { | ||||
|         return list.unshift(Immutable.fromJS(action.status)); | ||||
|       }); | ||||
| @@ -1,7 +1,7 @@ | ||||
| import { createStore, applyMiddleware } from 'redux'; | ||||
| import { createStore, applyMiddleware, compose } from 'redux'; | ||||
| import thunk from 'redux-thunk'; | ||||
| import appReducer from '../reducers'; | ||||
|  | ||||
| export default function configureStore() { | ||||
|   return createStore(appReducer, applyMiddleware(thunk)); | ||||
| export default function configureStore(initialState) { | ||||
|   return createStore(appReducer, initialState, compose(applyMiddleware(thunk), window.devToolsExtension ? window.devToolsExtension() : f => f)); | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| { | ||||
|   "name": "mastodon", | ||||
|   "devDependencies": { | ||||
|     "babel-plugin-react-transform": "^2.0.2", | ||||
|     "babel-preset-es2015": "^6.13.2", | ||||
|     "babel-preset-react": "^6.11.1", | ||||
|     "babelify": "^7.3.0", | ||||
| @@ -8,12 +9,12 @@ | ||||
|     "browserify-incremental": "^3.1.1", | ||||
|     "react": "^15.3.0", | ||||
|     "react-dom": "^15.3.0", | ||||
|     "redux-devtools": "^3.3.1" | ||||
|     "react-proxy": "^1.1.8" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "axios": "^0.14.0", | ||||
|     "es6-promise": "^3.2.1", | ||||
|     "immutable": "^3.8.1", | ||||
|     "isomorphic-fetch": "^2.2.1", | ||||
|     "moment": "^2.14.1", | ||||
|     "react-addons-pure-render-mixin": "^15.3.1", | ||||
|     "react-immutable-proptypes": "^2.1.0", | ||||
|   | ||||
		Reference in New Issue
	
	Block a user