Normalized data in Redux, fix for asset URLs when rendered outside request
This commit is contained in:
		
					parent
					
						
							
								7939a216ff
							
						
					
				
			
			
				commit
				
					
						1022d682dc
					
				
			
		
					 9 changed files with 71 additions and 27 deletions
				
			
		|  | @ -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')) { |   // Separate reblog, repeat for reblog | ||||||
|         return status.set('reblog', callback(status.get('reblog'))); |   let reblog = status.get('reblog'); | ||||||
|  | 
 | ||||||
|  |   if (reblog !== null) { | ||||||
|  |     status = status.set('reblog', reblog.get('id')); | ||||||
|  |     state  = statusToMaps(state, reblog); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|       return status; |   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 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue