Adding React.js, Redux, revamping dashboard
This commit is contained in:
		
							parent
							
								
									68c93f8b85
								
							
						
					
					
						commit
						49520d6e62
					
				
					 34 changed files with 297 additions and 75 deletions
				
			
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| 
						 | 
					@ -20,3 +20,4 @@ public/system
 | 
				
			||||||
public/assets
 | 
					public/assets
 | 
				
			||||||
.env
 | 
					.env
 | 
				
			||||||
.env.*
 | 
					.env.*
 | 
				
			||||||
 | 
					node_modules/
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								Gemfile
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								Gemfile
									
									
									
									
									
								
							| 
						 | 
					@ -38,6 +38,9 @@ gem 'rack-attack'
 | 
				
			||||||
gem 'sidekiq'
 | 
					gem 'sidekiq'
 | 
				
			||||||
gem 'sinatra', require: nil, github: 'sinatra'
 | 
					gem 'sinatra', require: nil, github: 'sinatra'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					gem 'react-rails'
 | 
				
			||||||
 | 
					gem 'browserify-rails'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
group :development, :test do
 | 
					group :development, :test do
 | 
				
			||||||
  gem 'rspec-rails'
 | 
					  gem 'rspec-rails'
 | 
				
			||||||
  gem 'pry-rails'
 | 
					  gem 'pry-rails'
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										16
									
								
								Gemfile.lock
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								Gemfile.lock
									
									
									
									
									
								
							| 
						 | 
					@ -53,6 +53,10 @@ GEM
 | 
				
			||||||
    addressable (2.4.0)
 | 
					    addressable (2.4.0)
 | 
				
			||||||
    arel (7.1.1)
 | 
					    arel (7.1.1)
 | 
				
			||||||
    ast (2.3.0)
 | 
					    ast (2.3.0)
 | 
				
			||||||
 | 
					    babel-source (5.8.35)
 | 
				
			||||||
 | 
					    babel-transpiler (0.7.0)
 | 
				
			||||||
 | 
					      babel-source (>= 4.0, < 6)
 | 
				
			||||||
 | 
					      execjs (~> 2.0)
 | 
				
			||||||
    bcrypt (3.1.11)
 | 
					    bcrypt (3.1.11)
 | 
				
			||||||
    better_errors (2.1.1)
 | 
					    better_errors (2.1.1)
 | 
				
			||||||
      coderay (>= 1.0.0)
 | 
					      coderay (>= 1.0.0)
 | 
				
			||||||
| 
						 | 
					@ -60,6 +64,9 @@ GEM
 | 
				
			||||||
      rack (>= 0.9.0)
 | 
					      rack (>= 0.9.0)
 | 
				
			||||||
    binding_of_caller (0.7.2)
 | 
					    binding_of_caller (0.7.2)
 | 
				
			||||||
      debug_inspector (>= 0.0.1)
 | 
					      debug_inspector (>= 0.0.1)
 | 
				
			||||||
 | 
					    browserify-rails (3.1.0)
 | 
				
			||||||
 | 
					      railties (>= 4.0.0, < 5.1)
 | 
				
			||||||
 | 
					      sprockets (>= 3.5.2)
 | 
				
			||||||
    builder (3.2.2)
 | 
					    builder (3.2.2)
 | 
				
			||||||
    bullet (5.3.0)
 | 
					    bullet (5.3.0)
 | 
				
			||||||
      activesupport (>= 3.0.0)
 | 
					      activesupport (>= 3.0.0)
 | 
				
			||||||
| 
						 | 
					@ -245,6 +252,13 @@ GEM
 | 
				
			||||||
    rake (11.2.2)
 | 
					    rake (11.2.2)
 | 
				
			||||||
    rdoc (4.2.2)
 | 
					    rdoc (4.2.2)
 | 
				
			||||||
      json (~> 1.4)
 | 
					      json (~> 1.4)
 | 
				
			||||||
 | 
					    react-rails (1.8.2)
 | 
				
			||||||
 | 
					      babel-transpiler (>= 0.7.0)
 | 
				
			||||||
 | 
					      coffee-script-source (~> 1.8)
 | 
				
			||||||
 | 
					      connection_pool
 | 
				
			||||||
 | 
					      execjs
 | 
				
			||||||
 | 
					      railties (>= 3.2)
 | 
				
			||||||
 | 
					      tilt
 | 
				
			||||||
    redis (3.3.1)
 | 
					    redis (3.3.1)
 | 
				
			||||||
    ref (2.0.0)
 | 
					    ref (2.0.0)
 | 
				
			||||||
    responders (2.3.0)
 | 
					    responders (2.3.0)
 | 
				
			||||||
| 
						 | 
					@ -348,6 +362,7 @@ DEPENDENCIES
 | 
				
			||||||
  addressable
 | 
					  addressable
 | 
				
			||||||
  better_errors
 | 
					  better_errors
 | 
				
			||||||
  binding_of_caller
 | 
					  binding_of_caller
 | 
				
			||||||
 | 
					  browserify-rails
 | 
				
			||||||
  bullet
 | 
					  bullet
 | 
				
			||||||
  coffee-rails (~> 4.1.0)
 | 
					  coffee-rails (~> 4.1.0)
 | 
				
			||||||
  devise
 | 
					  devise
 | 
				
			||||||
| 
						 | 
					@ -380,6 +395,7 @@ DEPENDENCIES
 | 
				
			||||||
  rails (= 5.0.0.1)
 | 
					  rails (= 5.0.0.1)
 | 
				
			||||||
  rails_12factor
 | 
					  rails_12factor
 | 
				
			||||||
  rails_autolink
 | 
					  rails_autolink
 | 
				
			||||||
 | 
					  react-rails
 | 
				
			||||||
  redis (~> 3.2)
 | 
					  redis (~> 3.2)
 | 
				
			||||||
  rspec-rails
 | 
					  rspec-rails
 | 
				
			||||||
  rspec-sidekiq
 | 
					  rspec-sidekiq
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,4 +12,6 @@
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//= require jquery
 | 
					//= require jquery
 | 
				
			||||||
//= require jquery_ujs
 | 
					//= require jquery_ujs
 | 
				
			||||||
//= require_tree .
 | 
					//= require components
 | 
				
			||||||
 | 
					//= require cable
 | 
				
			||||||
 | 
					//= require mastodon-logo
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,13 +0,0 @@
 | 
				
			||||||
App.timeline = App.cable.subscriptions.create("TimelineChannel", {
 | 
					 | 
				
			||||||
  connected: function() {
 | 
					 | 
				
			||||||
    console.log('Connected');
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  disconnected: function() {
 | 
					 | 
				
			||||||
    console.log('Disconnected');
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  received: function(data) {
 | 
					 | 
				
			||||||
    console.log(JSON.parse(data.message));
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
							
								
								
									
										9
									
								
								app/assets/javascripts/components.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								app/assets/javascripts/components.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,9 @@
 | 
				
			||||||
 | 
					//= require_self
 | 
				
			||||||
 | 
					//= require react_ujs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					window.React    = require('react');
 | 
				
			||||||
 | 
					window.ReactDOM = require('react-dom');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//= require_tree ./components
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					window.Root = require('./components/containers/root');
 | 
				
			||||||
							
								
								
									
										0
									
								
								app/assets/javascripts/components/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								app/assets/javascripts/components/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										18
									
								
								app/assets/javascripts/components/actions/statuses.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								app/assets/javascripts/components/actions/statuses.jsx
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					export const SET_TIMELINE = 'SET_TIMELINE';
 | 
				
			||||||
 | 
					export const ADD_STATUS   = 'ADD_STATUS';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										19
									
								
								app/assets/javascripts/components/components/column.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								app/assets/javascripts/components/components/column.jsx
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,19 @@
 | 
				
			||||||
 | 
					import StatusListContainer from '../containers/status_list_container';
 | 
				
			||||||
 | 
					import ColumnHeader        from './column_header';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Column = React.createClass({
 | 
				
			||||||
 | 
					  propTypes: {
 | 
				
			||||||
 | 
					    type: React.PropTypes.string
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render: function() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div style={{ width: '350px', flex: '0 0 auto', background: '#282c37', margin: '10px', marginRight: '0', display: 'flex', flexDirection: 'column' }}>
 | 
				
			||||||
 | 
					        <ColumnHeader type={this.props.type} />
 | 
				
			||||||
 | 
					        <StatusListContainer type={this.props.type} />
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Column;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,15 @@
 | 
				
			||||||
 | 
					const ColumnHeader = React.createClass({
 | 
				
			||||||
 | 
					  propTypes: {
 | 
				
			||||||
 | 
					    type: React.PropTypes.string
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render: function() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div style={{ padding: '15px', fontSize: '16px', background: '#2f3441', flex: '0 0 auto' }}>
 | 
				
			||||||
 | 
					        {this.props.type}
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default ColumnHeader;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,15 @@
 | 
				
			||||||
 | 
					import Column from './column';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ColumnsArea = React.createClass({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render: function() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div style={{ display: 'flex', flexDirection: 'row', flex: '1' }}>
 | 
				
			||||||
 | 
					        <Column type='home' />
 | 
				
			||||||
 | 
					        <Column type='mentions' />
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default ColumnsArea;
 | 
				
			||||||
							
								
								
									
										16
									
								
								app/assets/javascripts/components/components/frontend.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								app/assets/javascripts/components/components/frontend.jsx
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,16 @@
 | 
				
			||||||
 | 
					import NavBar      from './nav_bar';
 | 
				
			||||||
 | 
					import ColumnsArea from './columns_area';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Frontend = React.createClass({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render: function() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div style={{ flex: '0 0 auto', display: 'flex', width: '100%', height: '100%', background: '#1a1c23' }}>
 | 
				
			||||||
 | 
					        <NavBar />
 | 
				
			||||||
 | 
					        <ColumnsArea />
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Frontend;
 | 
				
			||||||
							
								
								
									
										8
									
								
								app/assets/javascripts/components/components/nav_bar.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								app/assets/javascripts/components/components/nav_bar.jsx
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					const NavBar = React.createClass({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render: function() {
 | 
				
			||||||
 | 
					    return <div style={{ background: '#2f3441', width: '60px', margin: '10px', marginRight: '0' }} />;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default NavBar;
 | 
				
			||||||
							
								
								
									
										19
									
								
								app/assets/javascripts/components/components/status.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								app/assets/javascripts/components/components/status.jsx
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,19 @@
 | 
				
			||||||
 | 
					import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Status = React.createClass({
 | 
				
			||||||
 | 
					  propTypes: {
 | 
				
			||||||
 | 
					    status: ImmutablePropTypes.map.isRequired
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render: function() {
 | 
				
			||||||
 | 
					    console.log(this.props.status.toJS());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div style={{ height: '100px' }}>
 | 
				
			||||||
 | 
					        {this.props.status.getIn(['account', 'username'])}: {this.props.status.get('content')}
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Status;
 | 
				
			||||||
							
								
								
									
										22
									
								
								app/assets/javascripts/components/components/status_list.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								app/assets/javascripts/components/components/status_list.jsx
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,22 @@
 | 
				
			||||||
 | 
					import Status             from './status';
 | 
				
			||||||
 | 
					import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const StatusList = React.createClass({
 | 
				
			||||||
 | 
					  propTypes: {
 | 
				
			||||||
 | 
					    statuses: ImmutablePropTypes.list.isRequired
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render: function() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div style={{ overflowY: 'scroll', flex: '1 1 auto' }}>
 | 
				
			||||||
 | 
					        <div>
 | 
				
			||||||
 | 
					          {this.props.statuses.map((status) => {
 | 
				
			||||||
 | 
					            return <Status key={status.get('id')} status={status} />;
 | 
				
			||||||
 | 
					          })}
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default StatusList;
 | 
				
			||||||
							
								
								
									
										40
									
								
								app/assets/javascripts/components/containers/root.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								app/assets/javascripts/components/containers/root.jsx
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,40 @@
 | 
				
			||||||
 | 
					import { Provider }               from 'react-redux';
 | 
				
			||||||
 | 
					import configureStore             from '../store/configureStore';
 | 
				
			||||||
 | 
					import Frontend                   from '../components/frontend';
 | 
				
			||||||
 | 
					import { setTimeline, addStatus } from '../actions/statuses';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const store = configureStore();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Root = React.createClass({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  componentWillMount() {
 | 
				
			||||||
 | 
					    for (var timelineType in this.props.timelines) {
 | 
				
			||||||
 | 
					      if (this.props.timelines.hasOwnProperty(timelineType)) {
 | 
				
			||||||
 | 
					        store.dispatch(setTimeline(timelineType, JSON.parse(this.props.timelines[timelineType])));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (typeof App !== 'undefined') {
 | 
				
			||||||
 | 
					      App.timeline = App.cable.subscriptions.create("TimelineChannel", {
 | 
				
			||||||
 | 
					        connected: function() {},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        disconnected: function() {},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        received: function(data) {
 | 
				
			||||||
 | 
					          return store.dispatch(addStatus(data.timeline, JSON.parse(data.message)));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Provider store={store}>
 | 
				
			||||||
 | 
					        <Frontend />
 | 
				
			||||||
 | 
					      </Provider>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Root;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					import { connect } from 'react-redux';
 | 
				
			||||||
 | 
					import StatusList  from '../components/status_list';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const mapStateToProps = function (state, props) {
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    statuses: state.getIn(['statuses', props.type])
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default connect(mapStateToProps)(StatusList);
 | 
				
			||||||
							
								
								
									
										6
									
								
								app/assets/javascripts/components/reducers/index.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								app/assets/javascripts/components/reducers/index.jsx
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					import { combineReducers } from 'redux-immutable';
 | 
				
			||||||
 | 
					import statuses            from './statuses';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default combineReducers({
 | 
				
			||||||
 | 
					  statuses
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										17
									
								
								app/assets/javascripts/components/reducers/statuses.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								app/assets/javascripts/components/reducers/statuses.jsx
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,17 @@
 | 
				
			||||||
 | 
					import { SET_TIMELINE, ADD_STATUS } from '../actions/statuses';
 | 
				
			||||||
 | 
					import Immutable                    from 'immutable';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const initialState = Immutable.Map();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function statuses(state = initialState, action) {
 | 
				
			||||||
 | 
					  switch(action.type) {
 | 
				
			||||||
 | 
					    case SET_TIMELINE:
 | 
				
			||||||
 | 
					      return state.set(action.timeline, Immutable.fromJS(action.statuses));
 | 
				
			||||||
 | 
					    case ADD_STATUS:
 | 
				
			||||||
 | 
					      return state.update(action.timeline, function (list) {
 | 
				
			||||||
 | 
					        list.unshift(Immutable.fromJS(action.status));
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      return state;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					import { createStore } from 'redux';
 | 
				
			||||||
 | 
					import appReducer from '../reducers';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function configureStore(initialState) {
 | 
				
			||||||
 | 
					  return createStore(appReducer, initialState);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -67,6 +67,23 @@ body {
 | 
				
			||||||
  font-weight: 400;
 | 
					  font-weight: 400;
 | 
				
			||||||
  color: #fff;
 | 
					  color: #fff;
 | 
				
			||||||
  padding-bottom: 140px;
 | 
					  padding-bottom: 140px;
 | 
				
			||||||
 | 
					  text-rendering: optimizelegibility;
 | 
				
			||||||
 | 
					  font-feature-settings: "kern";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &.app-body {
 | 
				
			||||||
 | 
					    position: fixed;
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    height: 100%;
 | 
				
			||||||
 | 
					    padding: 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.app-holder {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  height: 100%;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.container {
 | 
					.container {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,6 +12,8 @@ class ApplicationController < ActionController::Base
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  helper_method :current_account
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  protected
 | 
					  protected
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def current_account
 | 
					  def current_account
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,9 @@
 | 
				
			||||||
class HomeController < ApplicationController
 | 
					class HomeController < ApplicationController
 | 
				
			||||||
  layout 'dashboard'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  before_action :authenticate_user!
 | 
					  before_action :authenticate_user!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def index
 | 
					  def index
 | 
				
			||||||
    @timeline = Feed.new(:home, current_user.account).get(10, params[:max_id])
 | 
					    @body_classes = 'app-body'
 | 
				
			||||||
 | 
					    @home         = Feed.new(:home, current_user.account).get(20)
 | 
				
			||||||
 | 
					    @mentions     = Feed.new(:mentions, current_user.account).get(20)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,4 @@
 | 
				
			||||||
class SettingsController < ApplicationController
 | 
					class SettingsController < ApplicationController
 | 
				
			||||||
  layout 'dashboard'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  before_action :authenticate_user!
 | 
					  before_action :authenticate_user!
 | 
				
			||||||
  before_action :set_account
 | 
					  before_action :set_account
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,4 @@
 | 
				
			||||||
class StatusesController < ApplicationController
 | 
					class StatusesController < ApplicationController
 | 
				
			||||||
  layout 'dashboard'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  before_action :authenticate_user!
 | 
					  before_action :authenticate_user!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def create
 | 
					  def create
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ class Feed
 | 
				
			||||||
    @account = account
 | 
					    @account = account
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def get(limit, max_id)
 | 
					  def get(limit, max_id = nil)
 | 
				
			||||||
    max_id     = '+inf' if max_id.nil?
 | 
					    max_id     = '+inf' if max_id.nil?
 | 
				
			||||||
    unhydrated = redis.zrevrangebyscore(key, "(#{max_id}", '-inf', limit: [0, limit])
 | 
					    unhydrated = redis.zrevrangebyscore(key, "(#{max_id}", '-inf', limit: [0, limit])
 | 
				
			||||||
    status_map = Hash.new
 | 
					    status_map = Hash.new
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,7 +31,7 @@ class FanOutOnWriteService < BaseService
 | 
				
			||||||
  def push(type, receiver, status)
 | 
					  def push(type, receiver, status)
 | 
				
			||||||
    redis.zadd(FeedManager.key(type, receiver.id), status.id, status.id)
 | 
					    redis.zadd(FeedManager.key(type, receiver.id), status.id, status.id)
 | 
				
			||||||
    trim(type, receiver)
 | 
					    trim(type, receiver)
 | 
				
			||||||
    ActionCable.server.broadcast("timeline:#{receiver.id}", message: inline_render(receiver, status))
 | 
					    ActionCable.server.broadcast("timeline:#{receiver.id}", timeline: type, message: inline_render(receiver, status))
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def trim(type, receiver)
 | 
					  def trim(type, receiver)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1 @@
 | 
				
			||||||
= simple_form_for Status.new, url: statuses_path, method: :post do |f|
 | 
					= react_component 'Root', { timelines: { home: render(file: 'api/statuses/home', locals: { statuses: @home }, formats: :json), mentions: render(file: 'api/statuses/mentions', locals: { statuses: @mentions }, formats: :json) }}, class: 'app-holder', prerender: false
 | 
				
			||||||
  = f.input :text, required: true, autofocus: true, label: false, placeholder: 'What are you up to?'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .form-actions
 | 
					 | 
				
			||||||
    = f.button :submit, 'Post update'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- content_for :raw_content do
 | 
					 | 
				
			||||||
  .activity-stream.activity-stream-embedded
 | 
					 | 
				
			||||||
    - @timeline.each do |status|
 | 
					 | 
				
			||||||
      = render partial: 'stream_entries/status', locals: { status: status }
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,5 +9,5 @@
 | 
				
			||||||
    = javascript_include_tag 'application'
 | 
					    = javascript_include_tag 'application'
 | 
				
			||||||
    = csrf_meta_tags
 | 
					    = csrf_meta_tags
 | 
				
			||||||
    = yield :header_tags
 | 
					    = yield :header_tags
 | 
				
			||||||
  %body
 | 
					  %body{ class: @body_classes }
 | 
				
			||||||
    = content_for?(:content) ? yield(:content) : yield
 | 
					    = content_for?(:content) ? yield(:content) : yield
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,39 +0,0 @@
 | 
				
			||||||
- content_for :content do
 | 
					 | 
				
			||||||
  .dashboard-wrapper
 | 
					 | 
				
			||||||
    .dashboard__sidebar
 | 
					 | 
				
			||||||
      .dashboard__top-bar.alternate
 | 
					 | 
				
			||||||
         
 | 
					 | 
				
			||||||
      .dashboard__current-user
 | 
					 | 
				
			||||||
        = link_to account_path(current_user.account) do
 | 
					 | 
				
			||||||
          = image_tag current_user.account.avatar.url(:medium), class: 'dashboard__current-user__avatar'
 | 
					 | 
				
			||||||
          %strong.dashboard__current-user__display-name= display_name(current_user.account)
 | 
					 | 
				
			||||||
          %span.dashboard__current-user__username= "@#{current_user.account.username}"
 | 
					 | 
				
			||||||
      %ul
 | 
					 | 
				
			||||||
        %li{ class: active_nav_class(root_path) }
 | 
					 | 
				
			||||||
          = link_to root_path do
 | 
					 | 
				
			||||||
            = fa_icon 'home'
 | 
					 | 
				
			||||||
            Home
 | 
					 | 
				
			||||||
        %li{ class: active_nav_class(oauth_authorized_applications_path) }
 | 
					 | 
				
			||||||
          = link_to oauth_authorized_applications_path do
 | 
					 | 
				
			||||||
            = fa_icon 'shield'
 | 
					 | 
				
			||||||
            Authorized apps
 | 
					 | 
				
			||||||
        %li{ class: active_nav_class(settings_path) }
 | 
					 | 
				
			||||||
          = link_to settings_path do
 | 
					 | 
				
			||||||
            = fa_icon 'user'
 | 
					 | 
				
			||||||
            Edit profile
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .dashboard__content
 | 
					 | 
				
			||||||
      .dashboard__top-bar
 | 
					 | 
				
			||||||
        = content_for?(:page_title) ? yield(:page_title) : 'Mastodon'
 | 
					 | 
				
			||||||
        %ul
 | 
					 | 
				
			||||||
          %li= link_to fa_icon('gear'), edit_registration_path(current_user), title: 'Change password'
 | 
					 | 
				
			||||||
          %li= link_to fa_icon('sign-out'), destroy_user_session_path, method: :delete, title: 'Sign out'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      .dashboard__content__content= yield
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      = yield(:raw_content)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .footer
 | 
					 | 
				
			||||||
    .domain= Rails.configuration.x.local_domain
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
= render template: "layouts/application"
 | 
					 | 
				
			||||||
| 
						 | 
					@ -28,12 +28,14 @@ module Mastodon
 | 
				
			||||||
    config.active_job.queue_adapter = :sidekiq
 | 
					    config.active_job.queue_adapter = :sidekiq
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    config.to_prepare do
 | 
					    config.to_prepare do
 | 
				
			||||||
      Doorkeeper::ApplicationsController.layout           'dashboard'
 | 
					      # Doorkeeper::ApplicationsController.layout           'dashboard'
 | 
				
			||||||
      Doorkeeper::AuthorizedApplicationsController.layout 'dashboard'
 | 
					      # Doorkeeper::AuthorizedApplicationsController.layout 'dashboard'
 | 
				
			||||||
      Doorkeeper::AuthorizationsController.layout         'auth'
 | 
					      Doorkeeper::AuthorizationsController.layout         'auth'
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    config.middleware.use Rack::Attack
 | 
					    config.middleware.use Rack::Attack
 | 
				
			||||||
    config.middleware.use Rack::Deflater
 | 
					    config.middleware.use Rack::Deflater
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    config.browserify_rails.commandline_options = "--transform [ babelify --presets [ es2015 react ] ] --extension=\".jsx\""
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -63,6 +63,8 @@ Rails.application.configure do
 | 
				
			||||||
    Bullet.bullet_logger = true
 | 
					    Bullet.bullet_logger = true
 | 
				
			||||||
    Bullet.rails_logger = true
 | 
					    Bullet.rails_logger = true
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  config.react.variant = :development
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require 'sidekiq/testing'
 | 
					require 'sidekiq/testing'
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -80,4 +80,6 @@ Rails.application.configure do
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  config.action_mailer.delivery_method = :smtp
 | 
					  config.action_mailer.delivery_method = :smtp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  config.react.variant = :production
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										20
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,20 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "name": "mastodon",
 | 
				
			||||||
 | 
					  "devDependencies": {
 | 
				
			||||||
 | 
					    "babel-preset-es2015": "^6.13.2",
 | 
				
			||||||
 | 
					    "babel-preset-react": "^6.11.1",
 | 
				
			||||||
 | 
					    "babelify": "^7.3.0",
 | 
				
			||||||
 | 
					    "browserify": "^13.1.0",
 | 
				
			||||||
 | 
					    "browserify-incremental": "^3.1.1",
 | 
				
			||||||
 | 
					    "react": "^15.3.0",
 | 
				
			||||||
 | 
					    "react-dom": "^15.3.0",
 | 
				
			||||||
 | 
					    "redux-devtools": "^3.3.1"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
					    "immutable": "^3.8.1",
 | 
				
			||||||
 | 
					    "react-immutable-proptypes": "^2.1.0",
 | 
				
			||||||
 | 
					    "react-redux": "^4.4.5",
 | 
				
			||||||
 | 
					    "redux": "^3.5.2",
 | 
				
			||||||
 | 
					    "redux-immutable": "^3.0.8"
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in a new issue