From b6c2e42c4578a3dfefd29534055fc8771233b28c Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Wed, 10 Aug 2022 23:12:45 +0200
Subject: [PATCH] Add logged-out access to the web UI

---
 app/controllers/api/v2/search_controller.rb   |  5 +-
 app/controllers/home_controller.rb            |  3 +-
 app/javascript/mastodon/components/logo.js    |  4 +-
 .../mastodon/components/server_banner.js      | 18 ++++
 .../mastodon/containers/mastodon.js           |  2 +-
 .../features/account/components/header.js     |  2 +-
 .../features/ui/components/columns_area.js    |  4 +-
 .../features/ui/components/compose_panel.js   | 24 +++++-
 .../features/ui/components/link_footer.js     |  9 +-
 .../ui/components/navigation_panel.js         | 83 ++++++++++++++-----
 .../features/ui/components/sign_in_banner.js  | 11 +++
 app/javascript/mastodon/features/ui/index.js  | 15 ++--
 app/javascript/mastodon/initial_state.js      |  2 +
 .../styles/mastodon/components.scss           | 44 +++++++++-
 app/serializers/initial_state_serializer.rb   | 11 ++-
 15 files changed, 189 insertions(+), 48 deletions(-)
 create mode 100644 app/javascript/mastodon/components/server_banner.js
 create mode 100644 app/javascript/mastodon/features/ui/components/sign_in_banner.js

diff --git a/app/controllers/api/v2/search_controller.rb b/app/controllers/api/v2/search_controller.rb
index a305601333..e384ecbaf1 100644
--- a/app/controllers/api/v2/search_controller.rb
+++ b/app/controllers/api/v2/search_controller.rb
@@ -5,8 +5,7 @@ class Api::V2::SearchController < Api::BaseController
 
   RESULTS_LIMIT = 20
 
-  before_action -> { doorkeeper_authorize! :read, :'read:search' }
-  before_action :require_user!
+  before_action -> { authorize_if_got_token! :read, :'read:search' }
 
   def index
     @search = Search.new(search_results)
@@ -24,7 +23,7 @@ class Api::V2::SearchController < Api::BaseController
       params[:q],
       current_account,
       limit_param(RESULTS_LIMIT),
-      search_params.merge(resolve: truthy_param?(:resolve), exclude_unreviewed: truthy_param?(:exclude_unreviewed))
+      search_params.merge(resolve: user_signed_in? ? truthy_param?(:resolve) : false, exclude_unreviewed: truthy_param?(:exclude_unreviewed))
     )
   end
 
diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb
index 7e443eb9e7..24fd39f1a6 100644
--- a/app/controllers/home_controller.rb
+++ b/app/controllers/home_controller.rb
@@ -1,8 +1,7 @@
 # frozen_string_literal: true
 
 class HomeController < ApplicationController
-  before_action :redirect_unauthenticated_to_permalinks!
-  before_action :authenticate_user!
+  # before_action :redirect_unauthenticated_to_permalinks!
   before_action :set_referrer_policy_header
 
   def index
diff --git a/app/javascript/mastodon/components/logo.js b/app/javascript/mastodon/components/logo.js
index d1c7f08a91..3570b3644b 100644
--- a/app/javascript/mastodon/components/logo.js
+++ b/app/javascript/mastodon/components/logo.js
@@ -1,8 +1,8 @@
 import React from 'react';
 
 const Logo = () => (
-  <svg viewBox='0 0 216.4144 232.00976' className='logo'>
-    <use xlinkHref='#mastodon-svg-logo' />
+  <svg viewBox='0 0 261 66' className='logo'>
+    <use xlinkHref='#logo-symbol-wordmark' />
   </svg>
 );
 
diff --git a/app/javascript/mastodon/components/server_banner.js b/app/javascript/mastodon/components/server_banner.js
new file mode 100644
index 0000000000..259c0ded3d
--- /dev/null
+++ b/app/javascript/mastodon/components/server_banner.js
@@ -0,0 +1,18 @@
+import React from 'react';
+import { server, domain } from 'mastodon/initial_state';
+
+const ServerBanner = () => (
+  <div className='hero-widget'>
+    <div className='hero-widget__img'>
+      <img src={server.hero} alt={domain} />
+    </div>
+
+    <div className='hero-widget__text'>
+      <p>{server.description}</p>
+
+      <a href='/about/more' className='button button--block button-secondary'>Learn more</a>
+    </div>
+  </div>
+);
+
+export default ServerBanner;
diff --git a/app/javascript/mastodon/containers/mastodon.js b/app/javascript/mastodon/containers/mastodon.js
index f4bef46863..08241522cf 100644
--- a/app/javascript/mastodon/containers/mastodon.js
+++ b/app/javascript/mastodon/containers/mastodon.js
@@ -26,7 +26,7 @@ const createIdentityContext = state => ({
   signedIn: !!state.meta.me,
   accountId: state.meta.me,
   accessToken: state.meta.access_token,
-  permissions: state.role.permissions,
+  permissions: state.role ? state.role.permissions : 0,
 });
 
 export default class Mastodon extends React.PureComponent {
diff --git a/app/javascript/mastodon/features/account/components/header.js b/app/javascript/mastodon/features/account/components/header.js
index 1ad9341c79..c71d72ab17 100644
--- a/app/javascript/mastodon/features/account/components/header.js
+++ b/app/javascript/mastodon/features/account/components/header.js
@@ -322,7 +322,7 @@ class Header extends ImmutablePureComponent {
                   </div>
                 )}
 
-                {account.get('id') !== me && <AccountNoteContainer account={account} />}
+                {(account.get('id') !== me && this.context.identity.signedIn) && <AccountNoteContainer account={account} />}
 
                 {account.get('note').length > 0 && account.get('note') !== '<p></p>' && <div className='account__header__content translate' dangerouslySetInnerHTML={content} />}
 
diff --git a/app/javascript/mastodon/features/ui/components/columns_area.js b/app/javascript/mastodon/features/ui/components/columns_area.js
index 68017a5f1e..83e10e003a 100644
--- a/app/javascript/mastodon/features/ui/components/columns_area.js
+++ b/app/javascript/mastodon/features/ui/components/columns_area.js
@@ -60,6 +60,7 @@ class ColumnsArea extends ImmutablePureComponent {
 
   static contextTypes = {
     router: PropTypes.object.isRequired,
+    identity: PropTypes.object.isRequired,
   };
 
   static propTypes = {
@@ -212,11 +213,12 @@ class ColumnsArea extends ImmutablePureComponent {
   render () {
     const { columns, children, singleColumn, isModalOpen, intl } = this.props;
     const { shouldAnimate, renderComposePanel } = this.state;
+    const { signedIn } = this.context.identity;
 
     const columnIndex = getIndex(this.context.router.history.location.pathname);
 
     if (singleColumn) {
-      const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : <Link key='floating-action-button' to='/publish' className='floating-action-button' aria-label={intl.formatMessage(messages.publish)}><Icon id='pencil' /></Link>;
+      const floatingActionButton = (!signedIn || shouldHideFAB(this.context.router.history.location.pathname)) ? null : <Link key='floating-action-button' to='/publish' className='floating-action-button' aria-label={intl.formatMessage(messages.publish)}><Icon id='pencil' /></Link>;
 
       const content = columnIndex !== -1 ? (
         <ReactSwipeableViews key='content' hysteresis={0.2} threshold={15} index={columnIndex} onChangeIndex={this.handleSwipe} onTransitionEnd={this.handleAnimationEnd} animateTransitions={shouldAnimate} springConfig={{ duration: '400ms', delay: '0s', easeFunction: 'ease' }} style={{ height: '100%' }} disabled={disableSwiping}>
diff --git a/app/javascript/mastodon/features/ui/components/compose_panel.js b/app/javascript/mastodon/features/ui/components/compose_panel.js
index 3d0c48c7a9..17737fb999 100644
--- a/app/javascript/mastodon/features/ui/components/compose_panel.js
+++ b/app/javascript/mastodon/features/ui/components/compose_panel.js
@@ -6,10 +6,15 @@ import ComposeFormContainer from 'mastodon/features/compose/containers/compose_f
 import NavigationContainer from 'mastodon/features/compose/containers/navigation_container';
 import LinkFooter from './link_footer';
 import { changeComposing } from 'mastodon/actions/compose';
+import ServerBanner from 'mastodon/components/server_banner';
 
 export default @connect()
 class ComposePanel extends React.PureComponent {
 
+  static contextTypes = {
+    identity: PropTypes.object.isRequired,
+  };
+
   static propTypes = {
     dispatch: PropTypes.func.isRequired,
   };
@@ -23,11 +28,26 @@ class ComposePanel extends React.PureComponent {
   }
 
   render() {
+    const { signedIn } = this.context.identity;
+
     return (
       <div className='compose-panel' onFocus={this.onFocus}>
         <SearchContainer openInRoute />
-        <NavigationContainer onClose={this.onBlur} />
-        <ComposeFormContainer singleColumn />
+
+        {!signedIn && (
+          <React.Fragment>
+            <ServerBanner />
+            <div className='flex-spacer' />
+          </React.Fragment>
+        )}
+
+        {signedIn && (
+          <React.Fragment>
+            <NavigationContainer onClose={this.onBlur} />
+            <ComposeFormContainer singleColumn />
+          </React.Fragment>
+        )}
+
         <LinkFooter withHotkeys />
       </div>
     );
diff --git a/app/javascript/mastodon/features/ui/components/link_footer.js b/app/javascript/mastodon/features/ui/components/link_footer.js
index bbb9b122a3..4f07a3aad3 100644
--- a/app/javascript/mastodon/features/ui/components/link_footer.js
+++ b/app/javascript/mastodon/features/ui/components/link_footer.js
@@ -49,20 +49,21 @@ class LinkFooter extends React.PureComponent {
 
   render () {
     const { withHotkeys } = this.props;
+    const { signedIn, permissions } = this.context.identity;
 
     return (
       <div className='getting-started__footer'>
         <ul>
-          {((this.context.identity.permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS) && <li><a href='/invites' target='_blank'><FormattedMessage id='getting_started.invite' defaultMessage='Invite people' /></a> · </li>}
+          {((permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS) && <li><a href='/invites' target='_blank'><FormattedMessage id='getting_started.invite' defaultMessage='Invite people' /></a> · </li>}
           {withHotkeys && <li><Link to='/keyboard-shortcuts'><FormattedMessage id='navigation_bar.keyboard_shortcuts' defaultMessage='Hotkeys' /></Link> · </li>}
-          <li><a href='/auth/edit'><FormattedMessage id='getting_started.security' defaultMessage='Security' /></a> · </li>
+          {signedIn && (<li><a href='/auth/edit'><FormattedMessage id='getting_started.security' defaultMessage='Security' /></a> · </li>)}
           {!limitedFederationMode && <li><a href='/about/more' target='_blank'><FormattedMessage id='navigation_bar.info' defaultMessage='About this server' /></a> · </li>}
           {profileDirectory && <li><Link to='/directory'><FormattedMessage id='getting_started.directory' defaultMessage='Profile directory' /></Link> · </li>}
           <li><a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='navigation_bar.apps' defaultMessage='Mobile apps' /></a> · </li>
           <li><a href='/terms' target='_blank'><FormattedMessage id='getting_started.terms' defaultMessage='Terms of service' /></a> · </li>
-          <li><a href='/settings/applications' target='_blank'><FormattedMessage id='getting_started.developers' defaultMessage='Developers' /></a> · </li>
+          {signedIn && (<li><a href='/settings/applications' target='_blank'><FormattedMessage id='getting_started.developers' defaultMessage='Developers' /></a> · </li>)}
           <li><a href='https://docs.joinmastodon.org' target='_blank'><FormattedMessage id='getting_started.documentation' defaultMessage='Documentation' /></a> · </li>
-          <li><a href='/auth/sign_out' onClick={this.handleLogoutClick}><FormattedMessage id='navigation_bar.logout' defaultMessage='Logout' /></a></li>
+          {signedIn && (<li><a href='/auth/sign_out' onClick={this.handleLogoutClick}><FormattedMessage id='navigation_bar.logout' defaultMessage='Logout' /></a></li>)}
         </ul>
 
         <p>
diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.js b/app/javascript/mastodon/features/ui/components/navigation_panel.js
index fe4ed5d772..882ee85760 100644
--- a/app/javascript/mastodon/features/ui/components/navigation_panel.js
+++ b/app/javascript/mastodon/features/ui/components/navigation_panel.js
@@ -1,5 +1,6 @@
 import React from 'react';
-import { NavLink, withRouter } from 'react-router-dom';
+import PropTypes from 'prop-types';
+import { NavLink } from 'react-router-dom';
 import { FormattedMessage } from 'react-intl';
 import Icon from 'mastodon/components/icon';
 import { showTrends } from 'mastodon/initial_state';
@@ -7,30 +8,68 @@ import NotificationsCounterIcon from './notifications_counter_icon';
 import FollowRequestsNavLink from './follow_requests_nav_link';
 import ListPanel from './list_panel';
 import TrendsContainer from 'mastodon/features/getting_started/containers/trends_container';
+import Logo from 'mastodon/components/logo';
+import SignInBanner from './sign_in_banner';
 
-const NavigationPanel = () => (
-  <div className='navigation-panel'>
-    <NavLink className='column-link column-link--transparent' to='/home' data-preview-title-id='column.home' data-preview-icon='home' ><Icon className='column-link__icon' id='home' fixedWidth /><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></NavLink>
-    <NavLink className='column-link column-link--transparent' to='/notifications' data-preview-title-id='column.notifications' data-preview-icon='bell' ><NotificationsCounterIcon className='column-link__icon' /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></NavLink>
-    <FollowRequestsNavLink />
-    <NavLink className='column-link column-link--transparent' to='/explore' data-preview-title-id='explore.title' data-preview-icon='hashtag'><Icon className='column-link__icon' id='hashtag' fixedWidth /><FormattedMessage id='explore.title' defaultMessage='Explore' /></NavLink>
-    <NavLink className='column-link column-link--transparent' to='/public/local' data-preview-title-id='column.community' data-preview-icon='users' ><Icon className='column-link__icon' id='users' fixedWidth /><FormattedMessage id='tabs_bar.local_timeline' defaultMessage='Local' /></NavLink>
-    <NavLink className='column-link column-link--transparent' exact to='/public' data-preview-title-id='column.public' data-preview-icon='globe' ><Icon className='column-link__icon' id='globe' fixedWidth /><FormattedMessage id='tabs_bar.federated_timeline' defaultMessage='Federated' /></NavLink>
-    <NavLink className='column-link column-link--transparent' to='/conversations'><Icon className='column-link__icon' id='at' fixedWidth /><FormattedMessage id='navigation_bar.direct' defaultMessage='Direct messages' /></NavLink>
-    <NavLink className='column-link column-link--transparent' to='/favourites'><Icon className='column-link__icon' id='star' fixedWidth /><FormattedMessage id='navigation_bar.favourites' defaultMessage='Favourites' /></NavLink>
-    <NavLink className='column-link column-link--transparent' to='/bookmarks'><Icon className='column-link__icon' id='bookmark' fixedWidth /><FormattedMessage id='navigation_bar.bookmarks' defaultMessage='Bookmarks' /></NavLink>
-    <NavLink className='column-link column-link--transparent' to='/lists'><Icon className='column-link__icon' id='list-ul' fixedWidth /><FormattedMessage id='navigation_bar.lists' defaultMessage='Lists' /></NavLink>
+export default class NavigationPanel extends React.Component {
 
-    <ListPanel />
+  static contextTypes = {
+    router: PropTypes.object.isRequired,
+    identity: PropTypes.object.isRequired,
+  };
 
-    <hr />
+  render () {
+    const { signedIn } = this.context.identity;
 
-    <a className='column-link column-link--transparent' href='/settings/preferences'><Icon className='column-link__icon' id='cog' fixedWidth /><FormattedMessage id='navigation_bar.preferences' defaultMessage='Preferences' /></a>
-    <a className='column-link column-link--transparent' href='/relationships'><Icon className='column-link__icon' id='users' fixedWidth /><FormattedMessage id='navigation_bar.follows_and_followers' defaultMessage='Follows and followers' /></a>
+    return (
+      <div className='navigation-panel'>
+        <Logo />
 
-    {showTrends && <div className='flex-spacer' />}
-    {showTrends && <TrendsContainer />}
-  </div>
-);
+        <hr />
 
-export default withRouter(NavigationPanel);
+        {signedIn && (
+          <React.Fragment>
+            <NavLink className='column-link column-link--transparent' to='/home' data-preview-title-id='column.home' data-preview-icon='home' ><Icon className='column-link__icon' id='home' fixedWidth /><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></NavLink>
+            <NavLink className='column-link column-link--transparent' to='/notifications' data-preview-title-id='column.notifications' data-preview-icon='bell' ><NotificationsCounterIcon className='column-link__icon' /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></NavLink>
+            <FollowRequestsNavLink />
+          </React.Fragment>
+        )}
+
+        <NavLink className='column-link column-link--transparent' to='/explore' data-preview-title-id='explore.title' data-preview-icon='hashtag'><Icon className='column-link__icon' id='hashtag' fixedWidth /><FormattedMessage id='explore.title' defaultMessage='Explore' /></NavLink>
+        <NavLink className='column-link column-link--transparent' to='/public/local' data-preview-title-id='column.community' data-preview-icon='users' ><Icon className='column-link__icon' id='users' fixedWidth /><FormattedMessage id='tabs_bar.local_timeline' defaultMessage='Local' /></NavLink>
+        <NavLink className='column-link column-link--transparent' exact to='/public' data-preview-title-id='column.public' data-preview-icon='globe' ><Icon className='column-link__icon' id='globe' fixedWidth /><FormattedMessage id='tabs_bar.federated_timeline' defaultMessage='Federated' /></NavLink>
+
+        {!signedIn && (
+          <React.Fragment>
+            <hr />
+            <SignInBanner />
+          </React.Fragment>
+        )}
+
+        {signedIn && (
+          <React.Fragment>
+            <NavLink className='column-link column-link--transparent' to='/conversations'><Icon className='column-link__icon' id='at' fixedWidth /><FormattedMessage id='navigation_bar.direct' defaultMessage='Direct messages' /></NavLink>
+            <NavLink className='column-link column-link--transparent' to='/favourites'><Icon className='column-link__icon' id='star' fixedWidth /><FormattedMessage id='navigation_bar.favourites' defaultMessage='Favourites' /></NavLink>
+            <NavLink className='column-link column-link--transparent' to='/bookmarks'><Icon className='column-link__icon' id='bookmark' fixedWidth /><FormattedMessage id='navigation_bar.bookmarks' defaultMessage='Bookmarks' /></NavLink>
+            <NavLink className='column-link column-link--transparent' to='/lists'><Icon className='column-link__icon' id='list-ul' fixedWidth /><FormattedMessage id='navigation_bar.lists' defaultMessage='Lists' /></NavLink>
+
+            <ListPanel />
+
+            <hr />
+
+            <a className='column-link column-link--transparent' href='/settings/preferences'><Icon className='column-link__icon' id='cog' fixedWidth /><FormattedMessage id='navigation_bar.preferences' defaultMessage='Preferences' /></a>
+            <a className='column-link column-link--transparent' href='/relationships'><Icon className='column-link__icon' id='users' fixedWidth /><FormattedMessage id='navigation_bar.follows_and_followers' defaultMessage='Follows and followers' /></a>
+          </React.Fragment>
+        )}
+
+        {showTrends && (
+          <React.Fragment>
+            <div className='flex-spacer' />
+            <TrendsContainer />
+          </React.Fragment>
+        )}
+      </div>
+    );
+  }
+
+}
diff --git a/app/javascript/mastodon/features/ui/components/sign_in_banner.js b/app/javascript/mastodon/features/ui/components/sign_in_banner.js
new file mode 100644
index 0000000000..2ab07628af
--- /dev/null
+++ b/app/javascript/mastodon/features/ui/components/sign_in_banner.js
@@ -0,0 +1,11 @@
+import React from 'react';
+import { FormattedMessage } from 'react-intl';
+
+const SignInBanner = () => (
+  <div className='sign-in-banner'>
+    <p><FormattedMessage id='sign_in_banner.text' defaultMessage='Sign in to follow profiles or hashtags, favourite, share and reply to posts.' /></p>
+    <a href='/auth/sign_in' className='button button--block'><FormattedMessage id='sign_in_banner.sign_in' defaultMessage='Sign in' /></a>
+  </div>
+);
+
+export default SignInBanner;
diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js
index 9a901f12a6..ecb91592e5 100644
--- a/app/javascript/mastodon/features/ui/index.js
+++ b/app/javascript/mastodon/features/ui/index.js
@@ -208,6 +208,7 @@ class UI extends React.PureComponent {
 
   static contextTypes = {
     router: PropTypes.object.isRequired,
+    identity: PropTypes.object.isRequired,
   };
 
   static propTypes = {
@@ -343,6 +344,8 @@ class UI extends React.PureComponent {
   }
 
   componentDidMount () {
+    const { signedIn } = this.context.identity;
+
     window.addEventListener('focus', this.handleWindowFocus, false);
     window.addEventListener('blur', this.handleWindowBlur, false);
     window.addEventListener('beforeunload', this.handleBeforeUnload, false);
@@ -359,16 +362,18 @@ class UI extends React.PureComponent {
     }
 
     // On first launch, redirect to the follow recommendations page
-    if (this.props.firstLaunch) {
+    if (signedIn && this.props.firstLaunch) {
       this.context.router.history.replace('/start');
       this.props.dispatch(closeOnboarding());
     }
 
-    this.props.dispatch(fetchMarkers());
-    this.props.dispatch(expandHomeTimeline());
-    this.props.dispatch(expandNotifications());
+    if (signedIn) {
+      this.props.dispatch(fetchMarkers());
+      this.props.dispatch(expandHomeTimeline());
+      this.props.dispatch(expandNotifications());
 
-    setTimeout(() => this.props.dispatch(fetchRules()), 3000);
+      setTimeout(() => this.props.dispatch(fetchRules()), 3000);
+    }
 
     this.hotkeys.__mousetrap__.stopCallback = (e, element) => {
       return ['TEXTAREA', 'SELECT', 'INPUT'].includes(element.tagName);
diff --git a/app/javascript/mastodon/initial_state.js b/app/javascript/mastodon/initial_state.js
index 7099752702..9cc75b6cbf 100644
--- a/app/javascript/mastodon/initial_state.js
+++ b/app/javascript/mastodon/initial_state.js
@@ -3,6 +3,7 @@ const initialState = element && JSON.parse(element.textContent);
 
 const getMeta = (prop) => initialState && initialState.meta && initialState.meta[prop];
 
+export const domain = getMeta('domain');
 export const reduceMotion = getMeta('reduce_motion');
 export const autoPlayGif = getMeta('auto_play_gif');
 export const displayMedia = getMeta('display_media');
@@ -26,5 +27,6 @@ export const title = getMeta('title');
 export const cropImages = getMeta('crop_images');
 export const disableSwiping = getMeta('disable_swiping');
 export const languages = initialState && initialState.languages;
+export const server = initialState && initialState.server;
 
 export default initialState;
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 3e202841b0..a9fdedbfb1 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -700,6 +700,15 @@
   transition: height 0.4s ease, opacity 0.4s ease;
 }
 
+.sign-in-banner {
+  padding: 10px;
+
+  p {
+    color: $darker-text-color;
+    margin-bottom: 20px;
+  }
+}
+
 .emojione {
   font-size: inherit;
   vertical-align: middle;
@@ -2660,6 +2669,26 @@ a.account__display-name {
   height: calc(100% - 10px);
   overflow-y: hidden;
 
+  .hero-widget {
+    box-shadow: none;
+
+    &__text,
+    &__img,
+    &__img img {
+      border-radius: 0;
+    }
+
+    &__text {
+      padding: 15px;
+      color: $secondary-text-color;
+
+      strong {
+        font-weight: 700;
+        color: $primary-text-color;
+      }
+    }
+  }
+
   .navigation-bar {
     padding-top: 20px;
     padding-bottom: 20px;
@@ -2667,10 +2696,6 @@ a.account__display-name {
     min-height: 20px;
   }
 
-  .flex-spacer {
-    background: transparent;
-  }
-
   .compose-form {
     flex: 1;
     overflow-y: hidden;
@@ -2709,6 +2734,17 @@ a.account__display-name {
     flex: 0 0 auto;
   }
 
+  .logo {
+    height: 30px;
+    margin: 10px auto;
+    width: auto;
+    flex: 0 0 auto;
+    margin-left: 15px;
+  }
+}
+
+.navigation-panel,
+.compose-panel {
   hr {
     flex: 0 0 auto;
     border: 0;
diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb
index 5eda877579..df076ffc62 100644
--- a/app/serializers/initial_state_serializer.rb
+++ b/app/serializers/initial_state_serializer.rb
@@ -1,9 +1,11 @@
 # frozen_string_literal: true
 
 class InitialStateSerializer < ActiveModel::Serializer
+  include RoutingHelper
+
   attributes :meta, :compose, :accounts,
              :media_attachments, :settings,
-             :languages
+             :languages, :server
 
   has_one :push_subscription, serializer: REST::WebPushSubscriptionSerializer
   has_one :role, serializer: REST::RoleSerializer
@@ -82,6 +84,13 @@ class InitialStateSerializer < ActiveModel::Serializer
     LanguagesHelper::SUPPORTED_LOCALES.map { |(key, value)| [key, value[0], value[1]] }
   end
 
+  def server
+    {
+      hero: instance_presenter.hero&.file&.url || instance_presenter.thumbnail&.file&.url || asset_pack_path('media/images/preview.png'),
+      description: instance_presenter.site_short_description.presence || I18n.t('about.about_mastodon_html'),
+    }
+  end
+
   private
 
   def instance_presenter
-- 
GitLab