From 1022d682dc915bcbf3076c0280f29472068830bb Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 4 Sep 2016 14:04:26 +0200 Subject: [PATCH] Normalized data in Redux, fix for asset URLs when rendered outside request --- .../components/components/display_name.jsx | 8 ++- .../containers/status_list_container.jsx | 14 ++++- .../components/reducers/timelines.jsx | 54 ++++++++++++++----- app/helpers/atom_builder_helper.rb | 2 +- app/helpers/routing_helper.rb | 6 ++- app/views/accounts/show.atom.ruby | 2 +- app/views/api/accounts/show.rabl | 2 +- config/initializers/ostatus.rb | 4 +- spec/helpers/application_helper_spec.rb | 6 +-- 9 files changed, 71 insertions(+), 27 deletions(-) diff --git a/app/assets/javascripts/components/components/display_name.jsx b/app/assets/javascripts/components/components/display_name.jsx index 1d579731b..f8d821bce 100644 --- 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 ( - {this.props.account.get('display_name')} @{this.props.account.get('acct')} + {displayName} @{this.props.account.get('acct')} ); } diff --git a/app/assets/javascripts/components/containers/status_list_container.jsx b/app/assets/javascripts/components/containers/status_list_container.jsx index cc6333a81..743e56c67 100644 --- 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 index 616411186..9900489df 100644 --- 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(); +const initialState = Immutable.Map({ + home: Immutable.List(), + mentions: Immutable.List(), + statuses: Immutable.Map(), + accounts: 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'))); - } +function statusToMaps(state, status) { + // Separate account + let account = status.get('account'); + status = status.set('account', account.get('id')); - 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) { 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 index f71f0e83e..a98cb7b3b 100644 --- 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 index cd8132f07..101382604 100644 --- 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 index b7e3d2590..8a7bc1b92 100644 --- 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 index 05c92c99d..4f7cee680 100644 --- 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 index 6aada2c32..72009334f 100644 --- 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 index d294b9587..9f68a504a 100644 --- 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