commit: 1022d682dc915bcbf3076c0280f29472068830bb
parent: 7939a216ff5cc7ac6bb30e850a21355f04fdebe5
Author: Eugen Rochko <eugen@zeonfederated.com>
Date: Sun, 4 Sep 2016 14:04:26 +0200
Normalized data in Redux, fix for asset URLs when rendered outside request
Diffstat:
9 files changed, 73 insertions(+), 29 deletions(-)
diff --git a/app/assets/javascripts/components/components/display_name.jsx b/app/assets/javascripts/components/components/display_name.jsx
@@ -7,9 +7,15 @@ const DisplayName = React.createClass({
},
render () {
+ let displayName = this.props.account.get('display_name');
+
+ if (displayName.length === 0) {
+ displayName = this.props.account.get('username');
+ }
+
return (
<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>
);
}
diff --git a/app/assets/javascripts/components/containers/status_list_container.jsx b/app/assets/javascripts/components/containers/status_list_container.jsx
@@ -3,9 +3,21 @@ import StatusList from '../components/status_list';
import { replyCompose } from '../actions/compose';
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) {
return {
- statuses: state.getIn(['timelines', props.type])
+ statuses: state.getIn(['timelines', props.type]).map(id => selectStatus(state, id))
};
};
diff --git a/app/assets/javascripts/components/reducers/timelines.jsx b/app/assets/javascripts/components/reducers/timelines.jsx
@@ -2,31 +2,57 @@ import { TIMELINE_SET, TIMELINE_UPDATE } from '../actions/timelines';
import { REBLOG_SUCCESS, FAVOURITE_SUCCESS } from '../actions/interactions';
import Immutable from 'immutable';
-const initialState = Immutable.Map();
-
-function updateMatchingStatuses(state, needle, callback) {
- return state.map(function (list) {
- return list.map(function (status) {
- if (status.get('id') === needle.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;
- });
+const initialState = Immutable.Map({
+ home: Immutable.List(),
+ mentions: Immutable.List(),
+ statuses: Immutable.Map(),
+ accounts: Immutable.Map()
+});
+
+function statusToMaps(state, status) {
+ // Separate account
+ let account = status.get('account');
+ status = status.set('account', account.get('id'));
+
+ // 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) {
switch(action.type) {
case TIMELINE_SET:
- return state.set(action.timeline, Immutable.fromJS(action.statuses));
+ return timelineToMaps(state, action.timeline, Immutable.fromJS(action.statuses));
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 FAVOURITE_SUCCESS:
- return updateMatchingStatuses(state, action.status, () => Immutable.fromJS(action.response));
+ return statusToMaps(state, Immutable.fromJS(action.response));
default:
return state;
}
diff --git a/app/helpers/atom_builder_helper.rb b/app/helpers/atom_builder_helper.rb
@@ -214,6 +214,6 @@ module AtomBuilderHelper
end
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
diff --git a/app/helpers/routing_helper.rb b/app/helpers/routing_helper.rb
@@ -1,11 +1,15 @@
module RoutingHelper
extend ActiveSupport::Concern
include Rails.application.routes.url_helpers
- include ActionView::Helpers::AssetUrlHelper
+ include ActionView::Helpers::AssetTagHelper
included do
def default_url_options
ActionMailer::Base.default_url_options
end
end
+
+ def full_asset_url(source)
+ File.join(root_url, ActionController::Base.helpers.asset_url(source))
+ end
end
diff --git a/app/views/accounts/show.atom.ruby b/app/views/accounts/show.atom.ruby
@@ -4,7 +4,7 @@ Nokogiri::XML::Builder.new do |xml|
title xml, @account.display_name
subtitle xml, @account.note
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
include_author xml, @account
diff --git a/app/views/api/accounts/show.rabl b/app/views/api/accounts/show.rabl
@@ -3,7 +3,7 @@ object @account
attributes :id, :username, :acct, :display_name, :note
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(:following_count) { |account| account.following.count }
node(:statuses_count) { |account| account.statuses.count }
diff --git a/config/initializers/ostatus.rb b/config/initializers/ostatus.rb
@@ -1,9 +1,9 @@
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.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?
config.action_cable.allowed_request_origins = ["http#{config.x.use_https ? 's' : ''}://#{config.x.local_domain}"]
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
@@ -1,11 +1,7 @@
require 'rails_helper'
RSpec.describe ApplicationHelper, type: :helper do
- let(:local_domain) { 'local.tld' }
-
- before do
- Rails.configuration.x.local_domain = local_domain
- end
+ let(:local_domain) { Rails.configuration.x.local_domain }
describe '#unique_tag' do
it 'returns a string' do