sforkowany z mirror/soapbox
				
			Display federation restrictions on remote timelines
							rodzic
							
								
									567c4ce093
								
							
						
					
					
						commit
						f4ba9b9b2e
					
				|  | @ -0,0 +1,191 @@ | |||
| 'use strict'; | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { FormattedMessage } from 'react-intl'; | ||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| import Icon from 'soapbox/components/icon'; | ||||
| import { makeGetRemoteInstance } from 'soapbox/selectors'; | ||||
| 
 | ||||
| const hasRestrictions = remoteInstance => { | ||||
|   return remoteInstance | ||||
|     .get('federation') | ||||
|     .deleteAll(['accept', 'reject_deletes', 'report_removal']) | ||||
|     .reduce((acc, value) => acc || value, false); | ||||
| }; | ||||
| 
 | ||||
| const getRemoteInstance = makeGetRemoteInstance(); | ||||
| 
 | ||||
| const mapStateToProps = (state, { host }) => { | ||||
|   return { | ||||
|     instance: state.get('instance'), | ||||
|     remoteInstance: getRemoteInstance(state, host), | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export default @connect(mapStateToProps, null, null, { forwardRef: true }) | ||||
| class InstanceInfoPanel extends ImmutablePureComponent { | ||||
| 
 | ||||
|   static propTypes = { | ||||
|     intl: PropTypes.object.isRequired, | ||||
|     host: PropTypes.string.isRequired, | ||||
|     instance: ImmutablePropTypes.map, | ||||
|     remoteInstance: ImmutablePropTypes.map, | ||||
|   }; | ||||
| 
 | ||||
|   renderRestrictions = () => { | ||||
|     const { remoteInstance } = this.props; | ||||
|     const items = []; | ||||
| 
 | ||||
|     const { | ||||
|       avatar_removal, | ||||
|       banner_removal, | ||||
|       federated_timeline_removal, | ||||
|       followers_only, | ||||
|       media_nsfw, | ||||
|       media_removal, | ||||
|     } = remoteInstance.get('federation').toJS(); | ||||
| 
 | ||||
|     const fullMediaRemoval = media_removal && avatar_removal && banner_removal; | ||||
|     const partialMediaRemoval = media_removal || avatar_removal || banner_removal; | ||||
| 
 | ||||
|     if (followers_only) { | ||||
|       items.push(( | ||||
|         <div className='federation-restriction' key='followers_only'> | ||||
|           <div className='federation-restriction__icon'> | ||||
|             <Icon id='lock' /> | ||||
|           </div> | ||||
|           <div className='federation-restriction__message'> | ||||
|             <FormattedMessage | ||||
|               id='federation_restriction.followers_only' | ||||
|               defaultMessage='Hidden except to followers' | ||||
|             /> | ||||
|           </div> | ||||
|         </div> | ||||
|       )); | ||||
|     } else if (federated_timeline_removal) { | ||||
|       items.push(( | ||||
|         <div className='federation-restriction' key='federated_timeline_removal'> | ||||
|           <div className='federation-restriction__icon'> | ||||
|             <Icon id='unlock' /> | ||||
|           </div> | ||||
|           <div className='federation-restriction__message'> | ||||
|             <FormattedMessage | ||||
|               id='federation_restriction.federated_timeline_removal' | ||||
|               defaultMessage='Fediverse timeline removal' | ||||
|             /> | ||||
|           </div> | ||||
|         </div> | ||||
|       )); | ||||
|     } | ||||
| 
 | ||||
|     if (fullMediaRemoval) { | ||||
|       items.push(( | ||||
|         <div className='federation-restriction' key='full_media_removal'> | ||||
|           <div className='federation-restriction__icon'> | ||||
|             <Icon id='photo' /> | ||||
|           </div> | ||||
|           <div className='federation-restriction__message'> | ||||
|             <FormattedMessage | ||||
|               id='federation_restriction.full_media_removal' | ||||
|               defaultMessage='Full media removal' | ||||
|             /> | ||||
|           </div> | ||||
|         </div> | ||||
|       )); | ||||
|     } else if (partialMediaRemoval) { | ||||
|       items.push(( | ||||
|         <div className='federation-restriction' key='partial_media_removal'> | ||||
|           <div className='federation-restriction__icon'> | ||||
|             <Icon id='photo' /> | ||||
|           </div> | ||||
|           <div className='federation-restriction__message'> | ||||
|             <FormattedMessage | ||||
|               id='federation_restriction.partial_media_removal' | ||||
|               defaultMessage='Partial media removal' | ||||
|             /> | ||||
|           </div> | ||||
|         </div> | ||||
|       )); | ||||
|     } | ||||
| 
 | ||||
|     if (!fullMediaRemoval && media_nsfw) { | ||||
|       items.push(( | ||||
|         <div className='federation-restriction' key='media_nsfw'> | ||||
|           <div className='federation-restriction__icon'> | ||||
|             <Icon id='eye-slash' /> | ||||
|           </div> | ||||
|           <div className='federation-restriction__message'> | ||||
|             <FormattedMessage | ||||
|               id='federation_restriction.media_nsfw' | ||||
|               defaultMessage='Attachments marked NSFW' | ||||
|             /> | ||||
|           </div> | ||||
|         </div> | ||||
|       )); | ||||
|     } | ||||
| 
 | ||||
|     return items; | ||||
|   } | ||||
| 
 | ||||
|   renderContent = () => { | ||||
|     const { host, instance, remoteInstance } = this.props; | ||||
|     if (!instance || !remoteInstance) return null; | ||||
| 
 | ||||
|     if (remoteInstance.getIn(['federation', 'reject']) === true) { | ||||
|       return ( | ||||
|         <div className='instance-federation-panel__message'> | ||||
|           <Icon id='close' /> | ||||
|           <FormattedMessage | ||||
|             id='remote_instance.federation_panel.restricted_message' | ||||
|             defaultMessage='{siteTitle} blocks all activities from {host}.' | ||||
|             values={{ host, siteTitle: instance.get('title') }} | ||||
|           /> | ||||
|         </div> | ||||
|       ); | ||||
|     } else if (hasRestrictions(remoteInstance)) { | ||||
|       return [ | ||||
|         ( | ||||
|           <div className='instance-federation-panel__message'> | ||||
|             <FormattedMessage | ||||
|               id='remote_instance.federation_panel.some_restrictions_message' | ||||
|               defaultMessage='{siteTitle} has placed some restrictions on {host}.' | ||||
|               values={{ host, siteTitle: instance.get('title') }} | ||||
|             /> | ||||
|           </div> | ||||
|         ), | ||||
|         this.renderRestrictions(), | ||||
|       ]; | ||||
|     } else { | ||||
|       return ( | ||||
|         <div className='instance-federation-panel__message'> | ||||
|           <Icon id='check' /> | ||||
|           <FormattedMessage | ||||
|             id='remote_instance.federation_panel.no_restrictions_message' | ||||
|             defaultMessage='{siteTitle} has placed no restrictions on {host}.' | ||||
|             values={{ host, siteTitle: instance.get('title') }} | ||||
|           /> | ||||
|         </div> | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   render() { | ||||
|     return ( | ||||
|       <div className='wtf-panel instance-federation-panel'> | ||||
|         <div className='wtf-panel-header'> | ||||
|           <i role='img' alt='gavel' className='fa fa-gavel wtf-panel-header__icon' /> | ||||
|           <span className='wtf-panel-header__label'> | ||||
|             <span><FormattedMessage id='remote_instance.federation_panel.heading' defaultMessage='Federation Restrictions' /></span> | ||||
|           </span> | ||||
|         </div> | ||||
|         <div className='wtf-panel__content'> | ||||
|           {this.renderContent()} | ||||
|         </div> | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
|  | @ -34,6 +34,7 @@ import HomePage from 'soapbox/pages/home_page'; | |||
| import DefaultPage from 'soapbox/pages/default_page'; | ||||
| import EmptyPage from 'soapbox/pages/default_page'; | ||||
| import AdminPage from 'soapbox/pages/admin_page'; | ||||
| import RemoteInstancePage from 'soapbox/pages/remote_instance_page'; | ||||
| import SidebarMenu from '../../components/sidebar_menu'; | ||||
| import { connectUserStream } from '../../actions/streaming'; | ||||
| import { Redirect } from 'react-router-dom'; | ||||
|  | @ -194,7 +195,7 @@ class SwitchingColumnsArea extends React.PureComponent { | |||
|         <WrappedRoute path='/' exact page={HomePage} component={HomeTimeline} content={children} /> | ||||
|         <WrappedRoute path='/timeline/local' exact page={HomePage} component={CommunityTimeline} content={children} publicRoute /> | ||||
|         <WrappedRoute path='/timeline/fediverse' exact page={HomePage} component={PublicTimeline} content={children} publicRoute /> | ||||
|         <WrappedRoute path='/timeline/:instance' exact page={HomePage} component={RemoteTimeline} content={children} /> | ||||
|         <WrappedRoute path='/timeline/:instance' exact page={RemoteInstancePage} component={RemoteTimeline} content={children} /> | ||||
|         <WrappedRoute path='/messages' page={DefaultPage} component={DirectTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} /> | ||||
| 
 | ||||
|         {/* | ||||
|  |  | |||
|  | @ -0,0 +1,59 @@ | |||
| import React from 'react'; | ||||
| import { connect } from 'react-redux'; | ||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| import WhoToFollowPanel from 'soapbox/features/ui/components/who_to_follow_panel'; | ||||
| import TrendsPanel from 'soapbox/features/ui/components/trends_panel'; | ||||
| import PromoPanel from 'soapbox/features/ui/components/promo_panel'; | ||||
| import FeaturesPanel from 'soapbox/features/ui/components/features_panel'; | ||||
| import LinkFooter from 'soapbox/features/ui/components/link_footer'; | ||||
| import { getFeatures } from 'soapbox/utils/features'; | ||||
| import InstanceInfoPanel from 'soapbox/features/ui/components/instance_info_panel'; | ||||
| 
 | ||||
| const mapStateToProps = state => { | ||||
|   const features = getFeatures(state.get('instance')); | ||||
| 
 | ||||
|   return { | ||||
|     showTrendsPanel: features.trends, | ||||
|     showWhoToFollowPanel: features.suggestions, | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export default @connect(mapStateToProps) | ||||
| class RemoteInstancePage extends ImmutablePureComponent { | ||||
| 
 | ||||
|   render() { | ||||
|     const { children, showTrendsPanel, showWhoToFollowPanel, params: { instance: host } } = this.props; | ||||
| 
 | ||||
|     return ( | ||||
|       <div className='page'> | ||||
|         <div className='page__columns'> | ||||
|           <div className='columns-area__panels'> | ||||
| 
 | ||||
|             <div className='columns-area__panels__pane columns-area__panels__pane--left'> | ||||
|               <div className='columns-area__panels__pane__inner'> | ||||
|                 <InstanceInfoPanel host={host} /> | ||||
|               </div> | ||||
|             </div> | ||||
| 
 | ||||
|             <div className='columns-area__panels__main'> | ||||
|               <div className='columns-area columns-area--mobile'> | ||||
|                 {children} | ||||
|               </div> | ||||
|             </div> | ||||
| 
 | ||||
|             <div className='columns-area__panels__pane columns-area__panels__pane--right'> | ||||
|               <div className='columns-area__panels__pane__inner'> | ||||
|                 {showTrendsPanel && <TrendsPanel limit={3} key='trends-panel' />} | ||||
|                 {showWhoToFollowPanel && <WhoToFollowPanel limit={5} key='wtf-panel' />} | ||||
|                 <FeaturesPanel key='features-panel' /> | ||||
|                 <PromoPanel key='promo-panel' /> | ||||
|                 <LinkFooter key='link-footer' /> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
|  | @ -1,5 +1,6 @@ | |||
| import { createSelector } from 'reselect'; | ||||
| import { List as ImmutableList } from 'immutable'; | ||||
| import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; | ||||
| import { getDomain } from 'soapbox/utils/accounts'; | ||||
| 
 | ||||
| const getAccountBase         = (state, id) => state.getIn(['accounts', id], null); | ||||
| const getAccountCounters     = (state, id) => state.getIn(['accounts_counters', id], null); | ||||
|  | @ -215,3 +216,26 @@ export const makeGetOtherAccounts = () => { | |||
|       }, ImmutableList()); | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| const getRemoteInstanceFavicon = (state, host) => ( | ||||
|   state.get('accounts') | ||||
|     .find(account => getDomain(account) === host, null, ImmutableMap()) | ||||
|     .getIn(['pleroma', 'favicon']) | ||||
| ); | ||||
| 
 | ||||
| const getSimplePolicy = (state, host) => ( | ||||
|   state.getIn(['instance', 'pleroma', 'metadata', 'federation', 'mrf_simple'], ImmutableMap()) | ||||
|     .map(hosts => hosts.includes(host)) | ||||
| ); | ||||
| 
 | ||||
| export const makeGetRemoteInstance = () => { | ||||
|   return createSelector([ | ||||
|     getRemoteInstanceFavicon, | ||||
|     getSimplePolicy, | ||||
|   ], (favicon, federation) => { | ||||
|     return ImmutableMap({ | ||||
|       favicon, | ||||
|       federation, | ||||
|     }); | ||||
|   }); | ||||
| }; | ||||
|  |  | |||
|  | @ -82,6 +82,7 @@ | |||
| @import 'components/backups'; | ||||
| @import 'components/crypto-donate'; | ||||
| @import 'components/datepicker'; | ||||
| @import 'components/remote-timeline'; | ||||
| 
 | ||||
| // Holiday | ||||
| @import 'holiday/halloween'; | ||||
|  |  | |||
|  | @ -0,0 +1,27 @@ | |||
| .instance-federation-panel { | ||||
|   &__message { | ||||
|     margin-bottom: 15px; | ||||
| 
 | ||||
|     i.fa { | ||||
|       padding-right: 10px; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   .wtf-panel__content { | ||||
|     box-sizing: border-box; | ||||
|     padding: 15px; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .federation-restriction { | ||||
|   display: flex; | ||||
|   padding: 15px 0; | ||||
| 
 | ||||
|   &__icon { | ||||
|     width: 16px; | ||||
|     display: flex; | ||||
|     align-items: flex-start; | ||||
|     justify-content: center; | ||||
|     padding-right: 10px; | ||||
|   } | ||||
| } | ||||
		Ładowanie…
	
		Reference in New Issue
	
	 Alex Gleason
						Alex Gleason