Remove code for rendering public and hashtag timelines outside the web UI (#19257)
parent
e2b561e3a5
commit
02ba9cfa35
21 changed files with 13 additions and 446 deletions
@ -1,32 +0,0 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
class DirectoriesController < ApplicationController |
||||
layout 'public' |
||||
|
||||
before_action :authenticate_user!, if: :whitelist_mode? |
||||
before_action :require_enabled! |
||||
before_action :set_instance_presenter |
||||
before_action :set_accounts |
||||
|
||||
skip_before_action :require_functional!, unless: :whitelist_mode? |
||||
|
||||
def index |
||||
render :index |
||||
end |
||||
|
||||
private |
||||
|
||||
def require_enabled! |
||||
return not_found unless Setting.profile_directory |
||||
end |
||||
|
||||
def set_accounts |
||||
@accounts = Account.local.discoverable.by_recent_status.page(params[:page]).per(20).tap do |query| |
||||
query.merge!(Account.not_excluded_by_account(current_account)) if current_account |
||||
end |
||||
end |
||||
|
||||
def set_instance_presenter |
||||
@instance_presenter = InstancePresenter.new |
||||
end |
||||
end |
@ -1,26 +0,0 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
class PublicTimelinesController < ApplicationController |
||||
layout 'public' |
||||
|
||||
before_action :authenticate_user!, if: :whitelist_mode? |
||||
before_action :require_enabled! |
||||
before_action :set_body_classes |
||||
before_action :set_instance_presenter |
||||
|
||||
def show; end |
||||
|
||||
private |
||||
|
||||
def require_enabled! |
||||
not_found unless Setting.timeline_preview |
||||
end |
||||
|
||||
def set_body_classes |
||||
@body_classes = 'with-modals' |
||||
end |
||||
|
||||
def set_instance_presenter |
||||
@instance_presenter = InstancePresenter.new |
||||
end |
||||
end |
@ -1,90 +0,0 @@ |
||||
import React from 'react'; |
||||
import { connect } from 'react-redux'; |
||||
import PropTypes from 'prop-types'; |
||||
import ImmutablePropTypes from 'react-immutable-proptypes'; |
||||
import { expandHashtagTimeline } from 'mastodon/actions/timelines'; |
||||
import Masonry from 'react-masonry-infinite'; |
||||
import { List as ImmutableList } from 'immutable'; |
||||
import DetailedStatusContainer from 'mastodon/features/status/containers/detailed_status_container'; |
||||
import { debounce } from 'lodash'; |
||||
import LoadingIndicator from 'mastodon/components/loading_indicator'; |
||||
|
||||
const mapStateToProps = (state, { hashtag }) => ({ |
||||
statusIds: state.getIn(['timelines', `hashtag:${hashtag}`, 'items'], ImmutableList()), |
||||
isLoading: state.getIn(['timelines', `hashtag:${hashtag}`, 'isLoading'], false), |
||||
hasMore: state.getIn(['timelines', `hashtag:${hashtag}`, 'hasMore'], false), |
||||
}); |
||||
|
||||
export default @connect(mapStateToProps) |
||||
class HashtagTimeline extends React.PureComponent { |
||||
|
||||
static propTypes = { |
||||
dispatch: PropTypes.func.isRequired, |
||||
statusIds: ImmutablePropTypes.list.isRequired, |
||||
isLoading: PropTypes.bool.isRequired, |
||||
hasMore: PropTypes.bool.isRequired, |
||||
hashtag: PropTypes.string.isRequired, |
||||
local: PropTypes.bool.isRequired, |
||||
}; |
||||
|
||||
static defaultProps = { |
||||
local: false, |
||||
}; |
||||
|
||||
componentDidMount () { |
||||
const { dispatch, hashtag, local } = this.props; |
||||
|
||||
dispatch(expandHashtagTimeline(hashtag, { local })); |
||||
} |
||||
|
||||
handleLoadMore = () => { |
||||
const { dispatch, hashtag, local, statusIds } = this.props; |
||||
const maxId = statusIds.last(); |
||||
|
||||
if (maxId) { |
||||
dispatch(expandHashtagTimeline(hashtag, { maxId, local })); |
||||
} |
||||
} |
||||
|
||||
setRef = c => { |
||||
this.masonry = c; |
||||
} |
||||
|
||||
handleHeightChange = debounce(() => { |
||||
if (!this.masonry) { |
||||
return; |
||||
} |
||||
|
||||
this.masonry.forcePack(); |
||||
}, 50) |
||||
|
||||
render () { |
||||
const { statusIds, hasMore, isLoading } = this.props; |
||||
|
||||
const sizes = [ |
||||
{ columns: 1, gutter: 0 }, |
||||
{ mq: '415px', columns: 1, gutter: 10 }, |
||||
{ mq: '640px', columns: 2, gutter: 10 }, |
||||
{ mq: '960px', columns: 3, gutter: 10 }, |
||||
{ mq: '1255px', columns: 3, gutter: 10 }, |
||||
]; |
||||
|
||||
const loader = (isLoading && statusIds.isEmpty()) ? <LoadingIndicator key={0} /> : undefined; |
||||
|
||||
return ( |
||||
<Masonry ref={this.setRef} className='statuses-grid' hasMore={hasMore} loadMore={this.handleLoadMore} sizes={sizes} loader={loader}> |
||||
{statusIds.map(statusId => ( |
||||
<div className='statuses-grid__item' key={statusId}> |
||||
<DetailedStatusContainer |
||||
id={statusId} |
||||
compact |
||||
measureHeight |
||||
onHeightChange={this.handleHeightChange} |
||||
/> |
||||
</div> |
||||
)).toArray()} |
||||
</Masonry> |
||||
); |
||||
} |
||||
|
||||
} |
@ -1,99 +0,0 @@ |
||||
import React from 'react'; |
||||
import { connect } from 'react-redux'; |
||||
import PropTypes from 'prop-types'; |
||||
import ImmutablePropTypes from 'react-immutable-proptypes'; |
||||
import { expandPublicTimeline, expandCommunityTimeline } from 'mastodon/actions/timelines'; |
||||
import Masonry from 'react-masonry-infinite'; |
||||
import { List as ImmutableList, Map as ImmutableMap } from 'immutable'; |
||||
import DetailedStatusContainer from 'mastodon/features/status/containers/detailed_status_container'; |
||||
import { debounce } from 'lodash'; |
||||
import LoadingIndicator from 'mastodon/components/loading_indicator'; |
||||
|
||||
const mapStateToProps = (state, { local }) => { |
||||
const timeline = state.getIn(['timelines', local ? 'community' : 'public'], ImmutableMap()); |
||||
|
||||
return { |
||||
statusIds: timeline.get('items', ImmutableList()), |
||||
isLoading: timeline.get('isLoading', false), |
||||
hasMore: timeline.get('hasMore', false), |
||||
}; |
||||
}; |
||||
|
||||
export default @connect(mapStateToProps) |
||||
class PublicTimeline extends React.PureComponent { |
||||
|
||||
static propTypes = { |
||||
dispatch: PropTypes.func.isRequired, |
||||
statusIds: ImmutablePropTypes.list.isRequired, |
||||
isLoading: PropTypes.bool.isRequired, |
||||
hasMore: PropTypes.bool.isRequired, |
||||
local: PropTypes.bool, |
||||
}; |
||||
|
||||
componentDidMount () { |
||||
this._connect(); |
||||
} |
||||
|
||||
componentDidUpdate (prevProps) { |
||||
if (prevProps.local !== this.props.local) { |
||||
this._connect(); |
||||
} |
||||
} |
||||
|
||||
_connect () { |
||||
const { dispatch, local } = this.props; |
||||
|
||||
dispatch(local ? expandCommunityTimeline() : expandPublicTimeline()); |
||||
} |
||||
|
||||
handleLoadMore = () => { |
||||
const { dispatch, statusIds, local } = this.props; |
||||
const maxId = statusIds.last(); |
||||
|
||||
if (maxId) { |
||||
dispatch(local ? expandCommunityTimeline({ maxId }) : expandPublicTimeline({ maxId })); |
||||
} |
||||
} |
||||
|
||||
setRef = c => { |
||||
this.masonry = c; |
||||
} |
||||
|
||||
handleHeightChange = debounce(() => { |
||||
if (!this.masonry) { |
||||
return; |
||||
} |
||||
|
||||
this.masonry.forcePack(); |
||||
}, 50) |
||||
|
||||
render () { |
||||
const { statusIds, hasMore, isLoading } = this.props; |
||||
|
||||
const sizes = [ |
||||
{ columns: 1, gutter: 0 }, |
||||
{ mq: '415px', columns: 1, gutter: 10 }, |
||||
{ mq: '640px', columns: 2, gutter: 10 }, |
||||
{ mq: '960px', columns: 3, gutter: 10 }, |
||||
{ mq: '1255px', columns: 3, gutter: 10 }, |
||||
]; |
||||
|
||||
const loader = (isLoading && statusIds.isEmpty()) ? <LoadingIndicator key={0} /> : undefined; |
||||
|
||||
return ( |
||||
<Masonry ref={this.setRef} className='statuses-grid' hasMore={hasMore} loadMore={this.handleLoadMore} sizes={sizes} loader={loader}> |
||||
{statusIds.map(statusId => ( |
||||
<div className='statuses-grid__item' key={statusId}> |
||||
<DetailedStatusContainer |
||||
id={statusId} |
||||
compact |
||||
measureHeight |
||||
onHeightChange={this.handleHeightChange} |
||||
/> |
||||
</div> |
||||
)).toArray()} |
||||
</Masonry> |
||||
); |
||||
} |
||||
|
||||
} |
@ -1,26 +0,0 @@ |
||||
import './public-path'; |
||||
import loadPolyfills from '../mastodon/load_polyfills'; |
||||
import { start } from '../mastodon/common'; |
||||
|
||||
start(); |
||||
|
||||
function loaded() { |
||||
const TimelineContainer = require('../mastodon/containers/timeline_container').default; |
||||
const React = require('react'); |
||||
const ReactDOM = require('react-dom'); |
||||
const mountNode = document.getElementById('mastodon-timeline'); |
||||
|
||||
if (mountNode !== null) { |
||||
const props = JSON.parse(mountNode.getAttribute('data-props')); |
||||
ReactDOM.render(<TimelineContainer {...props} />, mountNode); |
||||
} |
||||
} |
||||
|
||||
function main() { |
||||
const ready = require('../mastodon/ready').default; |
||||
ready(loaded); |
||||
} |
||||
|
||||
loadPolyfills().then(main).catch(error => { |
||||
console.error(error); |
||||
}); |
@ -1,54 +0,0 @@ |
||||
- content_for :page_title do |
||||
= t('directories.explore_mastodon', title: site_title) |
||||
|
||||
- content_for :header_tags do |
||||
%meta{ name: 'description', content: t('directories.explanation') } |
||||
|
||||
= opengraph 'og:site_name', t('about.hosted_on', domain: site_hostname) |
||||
= opengraph 'og:type', 'website' |
||||
= opengraph 'og:title', t('directories.explore_mastodon', title: site_title) |
||||
= opengraph 'og:description', t('directories.explanation') |
||||
= opengraph 'og:image', File.join(root_url, 'android-chrome-192x192.png') |
||||
|
||||
.page-header |
||||
%h1= t('directories.explore_mastodon', title: site_title) |
||||
%p= t('directories.explanation') |
||||
|
||||
- if @accounts.empty? |
||||
= nothing_here |
||||
- else |
||||
.directory__list |
||||
- @accounts.each do |account| |
||||
.account-card |
||||
= link_to TagManager.instance.url_for(account), class: 'account-card__permalink' do |
||||
.account-card__header |
||||
= image_tag account.header.url, alt: '' |
||||
.account-card__title |
||||
.account-card__title__avatar |
||||
= image_tag account.avatar.url, alt: '' |
||||
.display-name |
||||
%bdi |
||||
%strong.emojify.p-name= display_name(account, custom_emojify: true) |
||||
%span |
||||
= acct(account) |
||||
= fa_icon('lock') if account.locked? |
||||
- if account.note.present? |
||||
.account-card__bio.emojify |
||||
= prerender_custom_emojis(account_bio_format(account), account.emojis) |
||||
- else |
||||
.flex-spacer |
||||
.account-card__actions |
||||
.account-card__counters |
||||
.account-card__counters__item |
||||
= friendly_number_to_human account.statuses_count |
||||
%small= t('accounts.posts', count: account.statuses_count).downcase |
||||
.account-card__counters__item |
||||
= friendly_number_to_human account.followers_count |
||||
%small= t('accounts.followers', count: account.followers_count).downcase |
||||
.account-card__counters__item |
||||
= friendly_number_to_human account.following_count |
||||
%small= t('accounts.following', count: account.following_count).downcase |
||||
.account-card__actions__button |
||||
= account_action_button(account) |
||||
|
||||
= paginate @accounts |
@ -1,17 +0,0 @@ |
||||
- content_for :page_title do |
||||
= t('about.see_whats_happening') |
||||
|
||||
- content_for :header_tags do |
||||
%meta{ name: 'robots', content: 'noindex' }/ |
||||
= javascript_pack_tag 'about', crossorigin: 'anonymous' |
||||
|
||||
.page-header |
||||
%h1= t('about.see_whats_happening') |
||||
|
||||
- if Setting.show_known_fediverse_at_about_page |
||||
%p= t('about.browse_public_posts') |
||||
- else |
||||
%p= t('about.browse_local_posts') |
||||
|
||||
#mastodon-timeline{ data: { props: Oj.dump(default_props.merge(local: !Setting.show_known_fediverse_at_about_page)) }} |
||||
.notranslate#modal-container |
@ -1,6 +0,0 @@ |
||||
= opengraph 'og:site_name', t('about.hosted_on', domain: site_hostname) |
||||
= opengraph 'og:url', tag_url(@tag) |
||||
= opengraph 'og:type', 'website' |
||||
= opengraph 'og:title', "##{@tag.display_name}" |
||||
= opengraph 'og:description', strip_tags(t('about.about_hashtag_html', hashtag: @tag.display_name)) |
||||
= opengraph 'twitter:card', 'summary' |
@ -1,16 +0,0 @@ |
||||
- content_for :page_title do |
||||
= "##{@tag.display_name}" |
||||
|
||||
- content_for :header_tags do |
||||
%meta{ name: 'robots', content: 'noindex' }/ |
||||
%link{ rel: 'alternate', type: 'application/rss+xml', href: tag_url(@tag, format: 'rss') }/ |
||||
|
||||
= javascript_pack_tag 'about', crossorigin: 'anonymous' |
||||
= render 'og' |
||||
|
||||
.page-header |
||||
%h1= "##{@tag.display_name}" |
||||
%p= t('about.about_hashtag_html', hashtag: @tag.display_name) |
||||
|
||||
#mastodon-timeline{ data: { props: Oj.dump(default_props.merge(hashtag: @tag.name, local: @local)) }} |
||||
.notranslate#modal-container |
Loading…
Reference in new issue