about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEugen Rochko <eugen@zeonfederated.com>2022-10-04 20:13:46 +0200
committerGitHub <noreply@github.com>2022-10-04 20:13:46 +0200
commit02ba9cfa35c7b2285950955619ae3431391e9625 (patch)
tree0e1ccc086ba1efde94851f998418d21ac4e278a6
parente2b561e3a521ff893943c0e9e32952e35934ca54 (diff)
Remove code for rendering public and hashtag timelines outside the web UI (#19257)
-rw-r--r--app/controllers/directories_controller.rb32
-rw-r--r--app/controllers/public_timelines_controller.rb26
-rw-r--r--app/controllers/tags_controller.rb2
-rw-r--r--app/helpers/application_helper.rb5
-rw-r--r--app/javascript/mastodon/features/standalone/hashtag_timeline/index.js90
-rw-r--r--app/javascript/mastodon/features/standalone/public_timeline/index.js99
-rw-r--r--app/javascript/packs/about.js26
-rw-r--r--app/models/form/admin_settings.rb2
-rw-r--r--app/views/about/show.html.haml25
-rw-r--r--app/views/admin/settings/edit.html.haml3
-rw-r--r--app/views/directories/index.html.haml54
-rw-r--r--app/views/layouts/public.html.haml1
-rw-r--r--app/views/public_timelines/show.html.haml17
-rw-r--r--app/views/tags/_og.html.haml6
-rw-r--r--app/views/tags/show.html.haml16
-rw-r--r--config/locales/en.yml11
-rw-r--r--config/routes.rb5
-rw-r--r--config/settings.yml1
-rw-r--r--package.json1
-rw-r--r--spec/controllers/tags_controller_spec.rb9
-rw-r--r--yarn.lock28
21 files changed, 13 insertions, 446 deletions
diff --git a/app/controllers/directories_controller.rb b/app/controllers/directories_controller.rb
deleted file mode 100644
index f28c5b2af..000000000
--- a/app/controllers/directories_controller.rb
+++ /dev/null
@@ -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
diff --git a/app/controllers/public_timelines_controller.rb b/app/controllers/public_timelines_controller.rb
deleted file mode 100644
index 1332ba16c..000000000
--- a/app/controllers/public_timelines_controller.rb
+++ /dev/null
@@ -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
diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb
index 6dbc2667a..2890c179d 100644
--- a/app/controllers/tags_controller.rb
+++ b/app/controllers/tags_controller.rb
@@ -21,7 +21,7 @@ class TagsController < ApplicationController
   def show
     respond_to do |format|
       format.html do
-        expires_in 0, public: true
+        redirect_to web_path("tags/#{@tag.name}")
       end
 
       format.rss do
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index db33292c1..14d27b148 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -198,10 +198,7 @@ module ApplicationHelper
 
   def render_initial_state
     state_params = {
-      settings: {
-        known_fediverse: Setting.show_known_fediverse_at_about_page,
-      },
-
+      settings: {},
       text: [params[:title], params[:text], params[:url]].compact.join(' '),
     }
 
diff --git a/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js b/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js
deleted file mode 100644
index d3d8a6507..000000000
--- a/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js
+++ /dev/null
@@ -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>
-    );
-  }
-
-}
diff --git a/app/javascript/mastodon/features/standalone/public_timeline/index.js b/app/javascript/mastodon/features/standalone/public_timeline/index.js
deleted file mode 100644
index 19b0b14be..000000000
--- a/app/javascript/mastodon/features/standalone/public_timeline/index.js
+++ /dev/null
@@ -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>
-    );
-  }
-
-}
diff --git a/app/javascript/packs/about.js b/app/javascript/packs/about.js
deleted file mode 100644
index 892d825ec..000000000
--- a/app/javascript/packs/about.js
+++ /dev/null
@@ -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);
-});
diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb
index 1e6061277..e744344c5 100644
--- a/app/models/form/admin_settings.rb
+++ b/app/models/form/admin_settings.rb
@@ -19,7 +19,6 @@ class Form::AdminSettings
     theme
     activity_api_enabled
     peers_api_enabled
-    show_known_fediverse_at_about_page
     preview_sensitive_media
     custom_css
     profile_directory
@@ -42,7 +41,6 @@ class Form::AdminSettings
     timeline_preview
     activity_api_enabled
     peers_api_enabled
-    show_known_fediverse_at_about_page
     preview_sensitive_media
     profile_directory
     trends
diff --git a/app/views/about/show.html.haml b/app/views/about/show.html.haml
index fb292941b..d61b3cd51 100644
--- a/app/views/about/show.html.haml
+++ b/app/views/about/show.html.haml
@@ -17,25 +17,12 @@
         = render 'registration'
 
       .directory
-        - if Setting.profile_directory
-          .directory__tag
-            = optional_link_to Setting.profile_directory, explore_path do
-              %h4
-                = fa_icon 'address-book fw'
-                = t('about.discover_users')
-                %small= t('about.browse_directory')
-
-              .avatar-stack
-                - @instance_presenter.sample_accounts.each do |account|
-                  = image_tag current_account&.user&.setting_auto_play_gif ? account.avatar_original_url : account.avatar_static_url, alt: '', class: 'account__avatar'
-
-        - if Setting.timeline_preview
-          .directory__tag
-            = optional_link_to Setting.timeline_preview, public_timeline_path do
-              %h4
-                = fa_icon 'globe fw'
-                = t('about.see_whats_happening')
-                %small= t('about.browse_public_posts')
+        .directory__tag
+          = link_to web_path do
+            %h4
+              = fa_icon 'globe fw'
+              = t('about.see_whats_happening')
+              %small= t('about.browse_public_posts')
 
         .directory__tag
           = link_to 'https://joinmastodon.org/apps', target: '_blank', rel: 'noopener noreferrer' do
diff --git a/app/views/admin/settings/edit.html.haml b/app/views/admin/settings/edit.html.haml
index 1dfd21643..a00cd0222 100644
--- a/app/views/admin/settings/edit.html.haml
+++ b/app/views/admin/settings/edit.html.haml
@@ -57,9 +57,6 @@
     .fields-group
       = f.input :timeline_preview, as: :boolean, wrapper: :with_label, label: t('admin.settings.timeline_preview.title'), hint: t('admin.settings.timeline_preview.desc_html')
 
-    .fields-group
-      = f.input :show_known_fediverse_at_about_page, as: :boolean, wrapper: :with_label, label: t('admin.settings.show_known_fediverse_at_about_page.title'), hint: t('admin.settings.show_known_fediverse_at_about_page.desc_html')
-
   .fields-group
     = f.input :open_deletion, as: :boolean, wrapper: :with_label, label: t('admin.settings.registrations.deletion.title'), hint: t('admin.settings.registrations.deletion.desc_html')
 
diff --git a/app/views/directories/index.html.haml b/app/views/directories/index.html.haml
deleted file mode 100644
index 48f8c4bc2..000000000
--- a/app/views/directories/index.html.haml
+++ /dev/null
@@ -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
diff --git a/app/views/layouts/public.html.haml b/app/views/layouts/public.html.haml
index 14f86c970..9b9e725e9 100644
--- a/app/views/layouts/public.html.haml
+++ b/app/views/layouts/public.html.haml
@@ -12,7 +12,6 @@
               = logo_as_symbol(:wordmark)
 
             - unless whitelist_mode?
-              = link_to t('directories.directory'), explore_path, class: 'nav-link optional' if Setting.profile_directory
               = link_to t('about.about_this'), about_more_path, class: 'nav-link optional'
               = link_to t('about.apps'), 'https://joinmastodon.org/apps', class: 'nav-link optional'
 
diff --git a/app/views/public_timelines/show.html.haml b/app/views/public_timelines/show.html.haml
deleted file mode 100644
index 9254bd348..000000000
--- a/app/views/public_timelines/show.html.haml
+++ /dev/null
@@ -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
diff --git a/app/views/tags/_og.html.haml b/app/views/tags/_og.html.haml
deleted file mode 100644
index 37f644cf2..000000000
--- a/app/views/tags/_og.html.haml
+++ /dev/null
@@ -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'
diff --git a/app/views/tags/show.html.haml b/app/views/tags/show.html.haml
deleted file mode 100644
index 6dfb4f9b3..000000000
--- a/app/views/tags/show.html.haml
+++ /dev/null
@@ -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
diff --git a/config/locales/en.yml b/config/locales/en.yml
index dd341e0c8..8f4ea652b 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -1,7 +1,6 @@
 ---
 en:
   about:
-    about_hashtag_html: These are public posts tagged with <strong>#%{hashtag}</strong>. You can interact with them if you have an account anywhere in the fediverse.
     about_mastodon_html: 'The social network of the future: No ads, no corporate surveillance, ethical design, and decentralization! Own your data with Mastodon!'
     about_this: About
     active_count_after: active
@@ -10,14 +9,11 @@ en:
     api: API
     apps: Mobile apps
     apps_platforms: Use Mastodon from iOS, Android and other platforms
-    browse_directory: Browse a profile directory and filter by interests
-    browse_local_posts: Browse a live stream of public posts from this server
     browse_public_posts: Browse a live stream of public posts on Mastodon
     contact: Contact
     contact_missing: Not set
     contact_unavailable: N/A
     continue_to_web: Continue to web app
-    discover_users: Discover users
     documentation: Documentation
     federation_hint_html: With an account on %{instance} you'll be able to follow people on any Mastodon server and beyond.
     get_apps: Try a mobile app
@@ -783,9 +779,6 @@ en:
           none: Nobody can sign up
           open: Anyone can sign up
         title: Registrations mode
-      show_known_fediverse_at_about_page:
-        desc_html: When disabled, restricts the public timeline linked from the landing page to showing only local content
-        title: Include federated content on unauthenticated public timeline page
       site_description:
         desc_html: Introductory paragraph on the API. Describe what makes this Mastodon server special and anything else important. You can use HTML tags, in particular <code>&lt;a&gt;</code> and <code>&lt;em&gt;</code>.
         title: Server description
@@ -1109,10 +1102,6 @@ en:
       more_details_html: For more details, see the <a href="%{terms_path}">privacy policy</a>.
       username_available: Your username will become available again
       username_unavailable: Your username will remain unavailable
-  directories:
-    directory: Profile directory
-    explanation: Discover users based on their interests
-    explore_mastodon: Explore %{title}
   disputes:
     strikes:
       action_taken: Action taken
diff --git a/config/routes.rb b/config/routes.rb
index 5d0b3004b..d2ede87d3 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -95,7 +95,6 @@ Rails.application.routes.draw do
   get  '/interact/:id', to: 'remote_interaction#new', as: :remote_interaction
   post '/interact/:id', to: 'remote_interaction#create'
 
-  get '/explore', to: 'directories#index', as: :explore
   get '/settings', to: redirect('/settings/profile')
 
   namespace :settings do
@@ -188,7 +187,9 @@ Rails.application.routes.draw do
   resource :relationships, only: [:show, :update]
   resource :statuses_cleanup, controller: :statuses_cleanup, only: [:show, :update]
 
-  get '/public', to: 'public_timelines#show', as: :public_timeline
+  get '/explore', to: redirect('/web/explore')
+  get '/public', to: redirect('/web/public')
+  get '/public/local', to: redirect('/web/public/local')
   get '/media_proxy/:id/(*any)', to: 'media_proxy#show', as: :media_proxy
 
   resource :authorize_interaction, only: [:show, :create]
diff --git a/config/settings.yml b/config/settings.yml
index 41742118b..ec8fead0f 100644
--- a/config/settings.yml
+++ b/config/settings.yml
@@ -66,7 +66,6 @@ defaults: &defaults
   bootstrap_timeline_accounts: ''
   activity_api_enabled: true
   peers_api_enabled: true
-  show_known_fediverse_at_about_page: true
   show_domain_blocks: 'disabled'
   show_domain_blocks_rationale: 'disabled'
   require_invite_text: false
diff --git a/package.json b/package.json
index f7804616c..e3b06c5e7 100644
--- a/package.json
+++ b/package.json
@@ -97,7 +97,6 @@
     "react-immutable-proptypes": "^2.2.0",
     "react-immutable-pure-component": "^2.2.2",
     "react-intl": "^2.9.0",
-    "react-masonry-infinite": "^1.2.2",
     "react-motion": "^0.5.2",
     "react-notification": "^6.8.5",
     "react-overlays": "^0.9.3",
diff --git a/spec/controllers/tags_controller_spec.rb b/spec/controllers/tags_controller_spec.rb
index 69def90cf..1fd8494d6 100644
--- a/spec/controllers/tags_controller_spec.rb
+++ b/spec/controllers/tags_controller_spec.rb
@@ -10,14 +10,9 @@ RSpec.describe TagsController, type: :controller do
     let!(:late)    { Fabricate(:status, tags: [tag], text: 'late #test') }
 
     context 'when tag exists' do
-      it 'returns http success' do
+      it 'redirects to web version' do
         get :show, params: { id: 'test', max_id: late.id }
-        expect(response).to have_http_status(200)
-      end
-
-      it 'renders application layout' do
-        get :show, params: { id: 'test', max_id: late.id }
-        expect(response).to render_template layout: 'public'
+        expect(response).to redirect_to('/web/tags/test')
       end
     end
 
diff --git a/yarn.lock b/yarn.lock
index 343e156f8..9bbf3cb10 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2934,13 +2934,6 @@ braces@^3.0.2, braces@~3.0.2:
   dependencies:
     fill-range "^7.0.1"
 
-bricks.js@^1.7.0:
-  version "1.8.0"
-  resolved "https://registry.yarnpkg.com/bricks.js/-/bricks.js-1.8.0.tgz#8fdeb3c0226af251f4d5727a7df7f9ac0092b4b2"
-  integrity sha1-j96zwCJq8lH01XJ6fff5rACStLI=
-  dependencies:
-    knot.js "^1.1.5"
-
 brorand@^1.0.1, brorand@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
@@ -7110,11 +7103,6 @@ klona@^2.0.4:
   resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0"
   integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==
 
-knot.js@^1.1.5:
-  version "1.1.5"
-  resolved "https://registry.yarnpkg.com/knot.js/-/knot.js-1.1.5.tgz#28e72522f703f50fe98812fde224dd72728fef5d"
-  integrity sha1-KOclIvcD9Q/piBL94iTdcnKP710=
-
 known-css-properties@^0.25.0:
   version "0.25.0"
   resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.25.0.tgz#6ebc4d4b412f602e5cfbeb4086bd544e34c0a776"
@@ -9226,13 +9214,6 @@ react-immutable-pure-component@^2.2.2:
   resolved "https://registry.yarnpkg.com/react-immutable-pure-component/-/react-immutable-pure-component-2.2.2.tgz#3014d3e20cd5a7a4db73b81f1f1464f4d351684b"
   integrity sha512-vkgoMJUDqHZfXXnjVlG3keCxSO/U6WeDQ5/Sl0GK2cH8TOxEzQ5jXqDXHEL/jqk6fsNxV05oH5kD7VNMUE2k+A==
 
-react-infinite-scroller@^1.0.12:
-  version "1.2.4"
-  resolved "https://registry.yarnpkg.com/react-infinite-scroller/-/react-infinite-scroller-1.2.4.tgz#f67eaec4940a4ce6417bebdd6e3433bfc38826e9"
-  integrity sha512-/oOa0QhZjXPqaD6sictN2edFMsd3kkMiE19Vcz5JDgHpzEJVqYcmq+V3mkwO88087kvKGe1URNksHEOt839Ubw==
-  dependencies:
-    prop-types "^15.5.8"
-
 react-intl-translations-manager@^5.0.3:
   version "5.0.3"
   resolved "https://registry.yarnpkg.com/react-intl-translations-manager/-/react-intl-translations-manager-5.0.3.tgz#aee010ecf35975673e033ca5d7d3f4147894324d"
@@ -9274,15 +9255,6 @@ react-lifecycles-compat@^3.0.4:
   resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
   integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
 
-react-masonry-infinite@^1.2.2:
-  version "1.2.2"
-  resolved "https://registry.yarnpkg.com/react-masonry-infinite/-/react-masonry-infinite-1.2.2.tgz#20c1386f9ccdda9747527c8f42bc2c02dd2e7951"
-  integrity sha1-IME4b5zN2pdHUnyPQrwsAt0ueVE=
-  dependencies:
-    bricks.js "^1.7.0"
-    prop-types "^15.5.10"
-    react-infinite-scroller "^1.0.12"
-
 react-motion@^0.5.2:
   version "0.5.2"
   resolved "https://registry.yarnpkg.com/react-motion/-/react-motion-0.5.2.tgz#0dd3a69e411316567927917c6626551ba0607316"