Normalized data in Redux, fix for asset URLs when rendered outside request
This commit is contained in:
		@@ -7,9 +7,15 @@ const DisplayName = React.createClass({
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render () {
 | 
					  render () {
 | 
				
			||||||
 | 
					    let displayName = this.props.account.get('display_name');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (displayName.length === 0) {
 | 
				
			||||||
 | 
					      displayName = this.props.account.get('username');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <span style={{ display: 'block', maxWidth: '100%', overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis' }}>
 | 
					      <span style={{ display: 'block', maxWidth: '100%', overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis' }}>
 | 
				
			||||||
        <strong style={{ fontWeight: 'bold' }}>{this.props.account.get('display_name')}</strong> <span style={{ fontSize: '14px' }}>@{this.props.account.get('acct')}</span>
 | 
					        <strong style={{ fontWeight: 'bold' }}>{displayName}</strong> <span style={{ fontSize: '14px' }}>@{this.props.account.get('acct')}</span>
 | 
				
			||||||
      </span>
 | 
					      </span>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,9 +3,21 @@ import StatusList            from '../components/status_list';
 | 
				
			|||||||
import { replyCompose }      from '../actions/compose';
 | 
					import { replyCompose }      from '../actions/compose';
 | 
				
			||||||
import { reblog, favourite } from '../actions/interactions';
 | 
					import { reblog, favourite } from '../actions/interactions';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function selectStatus(state, id) {
 | 
				
			||||||
 | 
					  let status = state.getIn(['timelines', 'statuses', id]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  status = status.set('account', state.getIn(['timelines', 'accounts', status.get('account')]));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (status.get('reblog') !== null) {
 | 
				
			||||||
 | 
					    status = status.set('reblog', selectStatus(state, status.get('reblog')));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return status;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const mapStateToProps = function (state, props) {
 | 
					const mapStateToProps = function (state, props) {
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    statuses: state.getIn(['timelines', props.type])
 | 
					    statuses: state.getIn(['timelines', props.type]).map(id => selectStatus(state, id))
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,31 +2,57 @@ import { TIMELINE_SET, TIMELINE_UPDATE }    from '../actions/timelines';
 | 
				
			|||||||
import { REBLOG_SUCCESS, FAVOURITE_SUCCESS } from '../actions/interactions';
 | 
					import { REBLOG_SUCCESS, FAVOURITE_SUCCESS } from '../actions/interactions';
 | 
				
			||||||
import Immutable                            from 'immutable';
 | 
					import Immutable                            from 'immutable';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const initialState = Immutable.Map();
 | 
					const initialState = Immutable.Map({
 | 
				
			||||||
 | 
					  home: Immutable.List(),
 | 
				
			||||||
 | 
					  mentions: Immutable.List(),
 | 
				
			||||||
 | 
					  statuses: Immutable.Map(),
 | 
				
			||||||
 | 
					  accounts: Immutable.Map()
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function updateMatchingStatuses(state, needle, callback) {
 | 
					function statusToMaps(state, status) {
 | 
				
			||||||
  return state.map(function (list) {
 | 
					  // Separate account
 | 
				
			||||||
    return list.map(function (status) {
 | 
					  let account = status.get('account');
 | 
				
			||||||
      if (status.get('id') === needle.get('id')) {
 | 
					  status = status.set('account', account.get('id'));
 | 
				
			||||||
        return callback(status);
 | 
					 | 
				
			||||||
      } else if (status.getIn(['reblog', 'id'], null) === needle.get('id')) {
 | 
					 | 
				
			||||||
        return status.set('reblog', callback(status.get('reblog')));
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return status;
 | 
					  // Separate reblog, repeat for reblog
 | 
				
			||||||
    });
 | 
					  let reblog = status.get('reblog');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (reblog !== null) {
 | 
				
			||||||
 | 
					    status = status.set('reblog', reblog.get('id'));
 | 
				
			||||||
 | 
					    state  = statusToMaps(state, reblog);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return state.withMutations(map => {
 | 
				
			||||||
 | 
					    map.setIn(['accounts', account.get('id')], account);
 | 
				
			||||||
 | 
					    map.setIn(['statuses', status.get('id')], status);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function timelineToMaps(state, timeline, statuses) {
 | 
				
			||||||
 | 
					  statuses.forEach((status, i) => {
 | 
				
			||||||
 | 
					    state = statusToMaps(state, status);
 | 
				
			||||||
 | 
					    state = state.setIn([timeline, i], status.get('id'));
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return state;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function updateTimelineWithMaps(state, timeline, status) {
 | 
				
			||||||
 | 
					  state = statusToMaps(state, status);
 | 
				
			||||||
 | 
					  state = state.update(timeline, list => list.unshift(status.get('id')));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return state;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function timelines(state = initialState, action) {
 | 
					export default function timelines(state = initialState, action) {
 | 
				
			||||||
  switch(action.type) {
 | 
					  switch(action.type) {
 | 
				
			||||||
    case TIMELINE_SET:
 | 
					    case TIMELINE_SET:
 | 
				
			||||||
      return state.set(action.timeline, Immutable.fromJS(action.statuses));
 | 
					      return timelineToMaps(state, action.timeline, Immutable.fromJS(action.statuses));
 | 
				
			||||||
    case TIMELINE_UPDATE:
 | 
					    case TIMELINE_UPDATE:
 | 
				
			||||||
      return state.update(action.timeline, list => list.unshift(Immutable.fromJS(action.status)));
 | 
					      return updateTimelineWithMaps(state, action.timeline,Immutable.fromJS(action.status));
 | 
				
			||||||
    case REBLOG_SUCCESS:
 | 
					    case REBLOG_SUCCESS:
 | 
				
			||||||
    case FAVOURITE_SUCCESS:
 | 
					    case FAVOURITE_SUCCESS:
 | 
				
			||||||
      return updateMatchingStatuses(state, action.status, () => Immutable.fromJS(action.response));
 | 
					      return statusToMaps(state, Immutable.fromJS(action.response));
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      return state;
 | 
					      return state;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -214,6 +214,6 @@ module AtomBuilderHelper
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def single_link_avatar(xml, account, size, px)
 | 
					  def single_link_avatar(xml, account, size, px)
 | 
				
			||||||
    xml.link('rel' => 'avatar', 'type' => account.avatar_content_type, 'media:width' => px, 'media:height' =>px, 'href' => asset_url(account.avatar.url(size, false)))
 | 
					    xml.link('rel' => 'avatar', 'type' => account.avatar_content_type, 'media:width' => px, 'media:height' =>px, 'href' => full_asset_url(account.avatar.url(size, false)))
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,15 @@
 | 
				
			|||||||
module RoutingHelper
 | 
					module RoutingHelper
 | 
				
			||||||
  extend ActiveSupport::Concern
 | 
					  extend ActiveSupport::Concern
 | 
				
			||||||
  include Rails.application.routes.url_helpers
 | 
					  include Rails.application.routes.url_helpers
 | 
				
			||||||
  include ActionView::Helpers::AssetUrlHelper
 | 
					  include ActionView::Helpers::AssetTagHelper
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  included do
 | 
					  included do
 | 
				
			||||||
    def default_url_options
 | 
					    def default_url_options
 | 
				
			||||||
      ActionMailer::Base.default_url_options
 | 
					      ActionMailer::Base.default_url_options
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def full_asset_url(source)
 | 
				
			||||||
 | 
					    File.join(root_url, ActionController::Base.helpers.asset_url(source))
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@ Nokogiri::XML::Builder.new do |xml|
 | 
				
			|||||||
    title      xml, @account.display_name
 | 
					    title      xml, @account.display_name
 | 
				
			||||||
    subtitle   xml, @account.note
 | 
					    subtitle   xml, @account.note
 | 
				
			||||||
    updated_at xml, stream_updated_at
 | 
					    updated_at xml, stream_updated_at
 | 
				
			||||||
    logo       xml, asset_url(@account.avatar.url(:medium, false))
 | 
					    logo       xml, full_asset_url(@account.avatar.url(:medium, false))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    author(xml) do
 | 
					    author(xml) do
 | 
				
			||||||
      include_author xml, @account
 | 
					      include_author xml, @account
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,7 @@ object @account
 | 
				
			|||||||
attributes :id, :username, :acct, :display_name, :note
 | 
					attributes :id, :username, :acct, :display_name, :note
 | 
				
			||||||
 | 
					
 | 
				
			||||||
node(:url)             { |account| url_for_target(account) }
 | 
					node(:url)             { |account| url_for_target(account) }
 | 
				
			||||||
node(:avatar)          { |account| asset_url(account.avatar.url(:large, false)) }
 | 
					node(:avatar)          { |account| full_asset_url(account.avatar.url(:large, false)) }
 | 
				
			||||||
node(:followers_count) { |account| account.followers.count }
 | 
					node(:followers_count) { |account| account.followers.count }
 | 
				
			||||||
node(:following_count) { |account| account.following.count }
 | 
					node(:following_count) { |account| account.following.count }
 | 
				
			||||||
node(:statuses_count)  { |account| account.statuses.count  }
 | 
					node(:statuses_count)  { |account| account.statuses.count  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,9 @@
 | 
				
			|||||||
Rails.application.configure do
 | 
					Rails.application.configure do
 | 
				
			||||||
  config.x.local_domain = ENV['LOCAL_DOMAIN'] || 'localhost'
 | 
					  config.x.local_domain = ENV['LOCAL_DOMAIN'] || "localhost:#{ENV['PORT'] || 3000}"
 | 
				
			||||||
  config.x.hub_url      = ENV['HUB_URL']      || 'https://pubsubhubbub.superfeedr.com'
 | 
					  config.x.hub_url      = ENV['HUB_URL']      || 'https://pubsubhubbub.superfeedr.com'
 | 
				
			||||||
  config.x.use_https    = ENV['LOCAL_HTTPS'] == 'true'
 | 
					  config.x.use_https    = ENV['LOCAL_HTTPS'] == 'true'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  config.action_mailer.default_url_options = { host: config.x.local_domain, protocol: config.x.use_https ? 'https://' : 'http://' }
 | 
					  config.action_mailer.default_url_options = { host: config.x.local_domain, protocol: config.x.use_https ? 'https://' : 'http://', trailing_slash: false }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if Rails.env.production?
 | 
					  if Rails.env.production?
 | 
				
			||||||
    config.action_cable.allowed_request_origins = ["http#{config.x.use_https ? 's' : ''}://#{config.x.local_domain}"]
 | 
					    config.action_cable.allowed_request_origins = ["http#{config.x.use_https ? 's' : ''}://#{config.x.local_domain}"]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,7 @@
 | 
				
			|||||||
require 'rails_helper'
 | 
					require 'rails_helper'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RSpec.describe ApplicationHelper, type: :helper do
 | 
					RSpec.describe ApplicationHelper, type: :helper do
 | 
				
			||||||
  let(:local_domain) { 'local.tld' }
 | 
					  let(:local_domain) { Rails.configuration.x.local_domain }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  before do
 | 
					 | 
				
			||||||
    Rails.configuration.x.local_domain = local_domain
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe '#unique_tag' do
 | 
					  describe '#unique_tag' do
 | 
				
			||||||
    it 'returns a string' do
 | 
					    it 'returns a string' do
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user