Redesign landing page (#10232)
This commit is contained in:
@ -1,21 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AboutController < ApplicationController
|
||||
before_action :set_body_classes
|
||||
layout 'public'
|
||||
|
||||
before_action :set_instance_presenter, only: [:show, :more, :terms]
|
||||
|
||||
def show
|
||||
serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer)
|
||||
@initial_state_json = serializable_resource.to_json
|
||||
@hide_navbar = true
|
||||
end
|
||||
|
||||
def more
|
||||
render layout: 'public'
|
||||
end
|
||||
def more; end
|
||||
|
||||
def terms
|
||||
render layout: 'public'
|
||||
end
|
||||
def terms; end
|
||||
|
||||
private
|
||||
|
||||
@ -28,15 +24,4 @@ class AboutController < ApplicationController
|
||||
def set_instance_presenter
|
||||
@instance_presenter = InstancePresenter.new
|
||||
end
|
||||
|
||||
def set_body_classes
|
||||
@body_classes = 'with-modals'
|
||||
end
|
||||
|
||||
def initial_state_params
|
||||
{
|
||||
settings: { known_fediverse: Setting.show_known_fediverse_at_about_page },
|
||||
token: current_session&.token,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
34
app/controllers/public_timelines_controller.rb
Normal file
34
app/controllers/public_timelines_controller.rb
Normal file
@ -0,0 +1,34 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class PublicTimelinesController < ApplicationController
|
||||
layout 'public'
|
||||
|
||||
before_action :check_enabled
|
||||
before_action :set_body_classes
|
||||
before_action :set_instance_presenter
|
||||
|
||||
def show
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
@initial_state_json = ActiveModelSerializers::SerializableResource.new(
|
||||
InitialStatePresenter.new(settings: { known_fediverse: Setting.show_known_fediverse_at_about_page }, token: current_session&.token),
|
||||
serializer: InitialStateSerializer
|
||||
).to_json
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_enabled
|
||||
raise ActiveRecord::RecordNotFound unless Setting.timeline_preview
|
||||
end
|
||||
|
||||
def set_body_classes
|
||||
@body_classes = 'with-modals'
|
||||
end
|
||||
|
||||
def set_instance_presenter
|
||||
@instance_presenter = InstancePresenter.new
|
||||
end
|
||||
end
|
@ -13,8 +13,10 @@ class TagsController < ApplicationController
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer)
|
||||
@initial_state_json = serializable_resource.to_json
|
||||
@initial_state_json = ActiveModelSerializers::SerializableResource.new(
|
||||
InitialStatePresenter.new(settings: {}, token: current_session&.token),
|
||||
serializer: InitialStateSerializer
|
||||
).to_json
|
||||
end
|
||||
|
||||
format.rss do
|
||||
@ -25,8 +27,7 @@ class TagsController < ApplicationController
|
||||
end
|
||||
|
||||
format.json do
|
||||
@statuses = HashtagQueryService.new.call(@tag, params.slice(:any, :all, :none), current_account, params[:local])
|
||||
.paginate_by_max_id(PAGE_SIZE, params[:max_id])
|
||||
@statuses = HashtagQueryService.new.call(@tag, params.slice(:any, :all, :none), current_account, params[:local]).paginate_by_max_id(PAGE_SIZE, params[:max_id])
|
||||
@statuses = cache_collection(@statuses, Status)
|
||||
|
||||
render json: collection_presenter,
|
||||
@ -55,11 +56,4 @@ class TagsController < ApplicationController
|
||||
items: @statuses.map { |s| ActivityPub::TagManager.instance.uri_for(s) }
|
||||
)
|
||||
end
|
||||
|
||||
def initial_state_params
|
||||
{
|
||||
settings: {},
|
||||
token: current_session&.token,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
@ -56,4 +56,12 @@ module HomeHelper
|
||||
'emojify'
|
||||
end
|
||||
end
|
||||
|
||||
def optional_link_to(condition, path, options = {}, &block)
|
||||
if condition
|
||||
link_to(path, options, &block)
|
||||
else
|
||||
content_tag(:div, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -7,7 +7,6 @@ import { hydrateStore } from '../actions/store';
|
||||
import { IntlProvider, addLocaleData } from 'react-intl';
|
||||
import { getLocale } from '../locales';
|
||||
import PublicTimeline from '../features/standalone/public_timeline';
|
||||
import CommunityTimeline from '../features/standalone/community_timeline';
|
||||
import HashtagTimeline from '../features/standalone/hashtag_timeline';
|
||||
import ModalContainer from '../features/ui/containers/modal_container';
|
||||
import initialState from '../initial_state';
|
||||
@ -26,24 +25,22 @@ export default class TimelineContainer extends React.PureComponent {
|
||||
static propTypes = {
|
||||
locale: PropTypes.string.isRequired,
|
||||
hashtag: PropTypes.string,
|
||||
showPublicTimeline: PropTypes.bool.isRequired,
|
||||
local: PropTypes.bool,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
showPublicTimeline: initialState.settings.known_fediverse,
|
||||
local: !initialState.settings.known_fediverse,
|
||||
};
|
||||
|
||||
render () {
|
||||
const { locale, hashtag, showPublicTimeline } = this.props;
|
||||
const { locale, hashtag, local } = this.props;
|
||||
|
||||
let timeline;
|
||||
|
||||
if (hashtag) {
|
||||
timeline = <HashtagTimeline hashtag={hashtag} />;
|
||||
} else if (showPublicTimeline) {
|
||||
timeline = <PublicTimeline />;
|
||||
} else {
|
||||
timeline = <CommunityTimeline />;
|
||||
timeline = <PublicTimeline local={local} />;
|
||||
}
|
||||
|
||||
return (
|
||||
@ -51,6 +48,7 @@ export default class TimelineContainer extends React.PureComponent {
|
||||
<Provider store={store}>
|
||||
<Fragment>
|
||||
{timeline}
|
||||
|
||||
{ReactDOM.createPortal(
|
||||
<ModalContainer />,
|
||||
document.getElementById('modal-container'),
|
||||
|
@ -1,71 +0,0 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import StatusListContainer from '../../ui/containers/status_list_container';
|
||||
import { expandCommunityTimeline } from '../../../actions/timelines';
|
||||
import Column from '../../../components/column';
|
||||
import ColumnHeader from '../../../components/column_header';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import { connectCommunityStream } from '../../../actions/streaming';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'standalone.public_title', defaultMessage: 'A look inside...' },
|
||||
});
|
||||
|
||||
export default @connect()
|
||||
@injectIntl
|
||||
class CommunityTimeline extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
handleHeaderClick = () => {
|
||||
this.column.scrollTop();
|
||||
}
|
||||
|
||||
setRef = c => {
|
||||
this.column = c;
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
dispatch(expandCommunityTimeline());
|
||||
this.disconnect = dispatch(connectCommunityStream());
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
if (this.disconnect) {
|
||||
this.disconnect();
|
||||
this.disconnect = null;
|
||||
}
|
||||
}
|
||||
|
||||
handleLoadMore = maxId => {
|
||||
this.props.dispatch(expandCommunityTimeline({ maxId }));
|
||||
}
|
||||
|
||||
render () {
|
||||
const { intl } = this.props;
|
||||
|
||||
return (
|
||||
<Column ref={this.setRef} label={intl.formatMessage(messages.title)}>
|
||||
<ColumnHeader
|
||||
icon='users'
|
||||
title={intl.formatMessage(messages.title)}
|
||||
onClick={this.handleHeaderClick}
|
||||
/>
|
||||
|
||||
<StatusListContainer
|
||||
timelineId='community'
|
||||
onLoadMore={this.handleLoadMore}
|
||||
scrollKey='standalone_public_timeline'
|
||||
trackScroll={false}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -2,13 +2,13 @@ import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { expandHashtagTimeline } from '../../../actions/timelines';
|
||||
import { connectHashtagStream } from '../../../actions/streaming';
|
||||
import { expandHashtagTimeline } from 'mastodon/actions/timelines';
|
||||
import { connectHashtagStream } from 'mastodon/actions/streaming';
|
||||
import Masonry from 'react-masonry-infinite';
|
||||
import { List as ImmutableList } from 'immutable';
|
||||
import DetailedStatusContainer from '../../status/containers/detailed_status_container';
|
||||
import DetailedStatusContainer from 'mastodon/features/status/containers/detailed_status_container';
|
||||
import { debounce } from 'lodash';
|
||||
import LoadingIndicator from '../../../components/loading_indicator';
|
||||
import LoadingIndicator from 'mastodon/components/loading_indicator';
|
||||
|
||||
const mapStateToProps = (state, { hashtag }) => ({
|
||||
statusIds: state.getIn(['timelines', `hashtag:${hashtag}`, 'items'], ImmutableList()),
|
||||
|
@ -1,42 +1,59 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import StatusListContainer from '../../ui/containers/status_list_container';
|
||||
import { expandPublicTimeline } from '../../../actions/timelines';
|
||||
import Column from '../../../components/column';
|
||||
import ColumnHeader from '../../../components/column_header';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import { connectPublicStream } from '../../../actions/streaming';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { expandPublicTimeline, expandCommunityTimeline } from 'mastodon/actions/timelines';
|
||||
import { connectPublicStream, connectCommunityStream } from 'mastodon/actions/streaming';
|
||||
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 messages = defineMessages({
|
||||
title: { id: 'standalone.public_title', defaultMessage: 'A look inside...' },
|
||||
});
|
||||
const mapStateToProps = (state, { local }) => {
|
||||
const timeline = state.getIn(['timelines', local ? 'community' : 'public'], ImmutableMap());
|
||||
|
||||
export default @connect()
|
||||
@injectIntl
|
||||
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,
|
||||
intl: PropTypes.object.isRequired,
|
||||
statusIds: ImmutablePropTypes.list.isRequired,
|
||||
isLoading: PropTypes.bool.isRequired,
|
||||
hasMore: PropTypes.bool.isRequired,
|
||||
local: PropTypes.bool,
|
||||
};
|
||||
|
||||
handleHeaderClick = () => {
|
||||
this.column.scrollTop();
|
||||
}
|
||||
|
||||
setRef = c => {
|
||||
this.column = c;
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
const { dispatch } = this.props;
|
||||
this._connect();
|
||||
}
|
||||
|
||||
dispatch(expandPublicTimeline());
|
||||
this.disconnect = dispatch(connectPublicStream());
|
||||
componentDidUpdate (prevProps) {
|
||||
if (prevProps.local !== this.props.local) {
|
||||
this._disconnect();
|
||||
this._connect();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
this._disconnect();
|
||||
}
|
||||
|
||||
_connect () {
|
||||
const { dispatch, local } = this.props;
|
||||
|
||||
dispatch(local ? expandCommunityTimeline() : expandPublicTimeline());
|
||||
this.disconnect = dispatch(local ? connectCommunityStream() : connectPublicStream());
|
||||
}
|
||||
|
||||
_disconnect () {
|
||||
if (this.disconnect) {
|
||||
this.disconnect();
|
||||
this.disconnect = null;
|
||||
@ -44,27 +61,48 @@ class PublicTimeline extends React.PureComponent {
|
||||
}
|
||||
|
||||
handleLoadMore = maxId => {
|
||||
this.props.dispatch(expandPublicTimeline({ maxId }));
|
||||
const { dispatch, local } = this.props;
|
||||
dispatch(local ? expandCommunityTimeline({ maxId }) : expandPublicTimeline({ maxId }));
|
||||
}
|
||||
|
||||
setRef = c => {
|
||||
this.masonry = c;
|
||||
}
|
||||
|
||||
handleHeightChange = debounce(() => {
|
||||
if (!this.masonry) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.masonry.forcePack();
|
||||
}, 50)
|
||||
|
||||
render () {
|
||||
const { intl } = this.props;
|
||||
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 (
|
||||
<Column ref={this.setRef} label={intl.formatMessage(messages.title)}>
|
||||
<ColumnHeader
|
||||
icon='globe'
|
||||
title={intl.formatMessage(messages.title)}
|
||||
onClick={this.handleHeaderClick}
|
||||
/>
|
||||
|
||||
<StatusListContainer
|
||||
timelineId='public'
|
||||
onLoadMore={this.handleLoadMore}
|
||||
scrollKey='standalone_public_timeline'
|
||||
trackScroll={false}
|
||||
/>
|
||||
</Column>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
status: ImmutablePropTypes.map,
|
||||
onOpenMedia: PropTypes.func.isRequired,
|
||||
onOpenVideo: PropTypes.func.isRequired,
|
||||
onToggleHidden: PropTypes.func.isRequired,
|
||||
|
@ -193,6 +193,7 @@ $small-breakpoint: 960px;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-family: $font-display, sans-serif;
|
||||
font-weight: 500;
|
||||
font-size: 32px;
|
||||
line-height: 48px;
|
||||
@ -280,168 +281,6 @@ $small-breakpoint: 960px;
|
||||
}
|
||||
|
||||
.landing-page {
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-gap: 10px;
|
||||
grid-template-columns: 1fr 2fr;
|
||||
grid-auto-columns: 25%;
|
||||
grid-auto-rows: max-content;
|
||||
|
||||
.column-0 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.column-1 {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.column-2 {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.column-3 {
|
||||
grid-column: 3;
|
||||
grid-row: 1 / 3;
|
||||
}
|
||||
|
||||
.column-4 {
|
||||
grid-column: 1 / 3;
|
||||
grid-row: 2;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $small-breakpoint) {
|
||||
.grid {
|
||||
grid-template-columns: 40% 60%;
|
||||
|
||||
.column-0 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.column-1 {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
|
||||
&.non-preview .landing-page__forms {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.column-2 {
|
||||
grid-column: 2;
|
||||
grid-row: 1 / 3;
|
||||
|
||||
&.non-preview {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.column-3 {
|
||||
grid-column: 1;
|
||||
grid-row: 2 / 4;
|
||||
}
|
||||
|
||||
.column-4 {
|
||||
grid-column: 2;
|
||||
grid-row: 3;
|
||||
|
||||
&.non-preview {
|
||||
grid-column: 1 / 3;
|
||||
grid-row: 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $column-breakpoint) {
|
||||
.grid {
|
||||
grid-template-columns: 100%;
|
||||
|
||||
.column-0 {
|
||||
display: block;
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.column-1 {
|
||||
grid-column: 1;
|
||||
grid-row: 3;
|
||||
|
||||
.brand {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.column-2 {
|
||||
grid-column: 1;
|
||||
grid-row: 2;
|
||||
|
||||
.landing-page__logo,
|
||||
.landing-page__call-to-action {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.non-preview {
|
||||
grid-column: 1;
|
||||
grid-row: 2;
|
||||
}
|
||||
}
|
||||
|
||||
.column-3 {
|
||||
grid-column: 1;
|
||||
grid-row: 5;
|
||||
}
|
||||
|
||||
.column-4 {
|
||||
grid-column: 1;
|
||||
grid-row: 4;
|
||||
|
||||
&.non-preview {
|
||||
grid-column: 1;
|
||||
grid-row: 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.column-flex {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.separator-or {
|
||||
position: relative;
|
||||
margin: 40px 0;
|
||||
text-align: center;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
border-bottom: 1px solid rgba($ui-base-lighter-color, .6);
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
background: $ui-base-color;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: $darker-text-color;
|
||||
text-transform: uppercase;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 0 8px;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
p,
|
||||
li {
|
||||
font-family: $font-sans-serif, sans-serif;
|
||||
@ -458,28 +297,6 @@ $small-breakpoint: 960px;
|
||||
}
|
||||
}
|
||||
|
||||
.closed-registrations-message {
|
||||
margin-top: 20px;
|
||||
|
||||
&,
|
||||
p {
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
color: $darker-text-color;
|
||||
margin-bottom: 0;
|
||||
|
||||
a {
|
||||
color: $highlight-text-color;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
em {
|
||||
display: inline;
|
||||
margin: 0;
|
||||
@ -593,187 +410,6 @@ $small-breakpoint: 960px;
|
||||
}
|
||||
}
|
||||
|
||||
.container-alt {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.header-wrapper {
|
||||
padding-top: 15px;
|
||||
background: $ui-base-color;
|
||||
background: linear-gradient(150deg, lighten($ui-base-color, 8%), $ui-base-color);
|
||||
position: relative;
|
||||
|
||||
&.compact {
|
||||
background: $ui-base-color;
|
||||
padding-bottom: 15px;
|
||||
|
||||
.hero .heading {
|
||||
padding-bottom: 20px;
|
||||
font-family: $font-sans-serif, sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 30px;
|
||||
color: $darker-text-color;
|
||||
|
||||
a {
|
||||
color: $highlight-text-color;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.brand {
|
||||
a {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
img {
|
||||
height: 32px;
|
||||
position: relative;
|
||||
top: 4px;
|
||||
left: -10px;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
line-height: 30px;
|
||||
overflow: hidden;
|
||||
|
||||
.container-alt {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.links {
|
||||
position: relative;
|
||||
z-index: 4;
|
||||
|
||||
a {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: $darker-text-color;
|
||||
text-decoration: none;
|
||||
padding: 12px 16px;
|
||||
line-height: 32px;
|
||||
font-family: $font-display, sans-serif;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
|
||||
&:hover {
|
||||
color: $secondary-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
margin: 0;
|
||||
|
||||
&:first-child a {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
&:last-child a {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hero {
|
||||
margin-top: 50px;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
|
||||
.heading {
|
||||
position: relative;
|
||||
z-index: 4;
|
||||
padding-bottom: 150px;
|
||||
}
|
||||
|
||||
.simple_form,
|
||||
.closed-registrations-message {
|
||||
background: darken($ui-base-color, 4%);
|
||||
width: 280px;
|
||||
padding: 15px 20px;
|
||||
border-radius: 4px 4px 0 0;
|
||||
line-height: initial;
|
||||
position: relative;
|
||||
z-index: 4;
|
||||
|
||||
.actions {
|
||||
margin-bottom: 0;
|
||||
|
||||
button,
|
||||
.button,
|
||||
.block-button {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.closed-registrations-message {
|
||||
min-height: 330px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.about-short {
|
||||
background: darken($ui-base-color, 4%);
|
||||
padding: 50px 0 30px;
|
||||
font-family: $font-sans-serif, sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 30px;
|
||||
color: $darker-text-color;
|
||||
|
||||
a {
|
||||
color: $highlight-text-color;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
&.alternative {
|
||||
padding: 10px 0;
|
||||
|
||||
.brand {
|
||||
text-align: center;
|
||||
padding: 30px 0;
|
||||
margin-bottom: 10px;
|
||||
|
||||
img {
|
||||
position: static;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $small-breakpoint) {
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $column-breakpoint) {
|
||||
padding: 0;
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__information,
|
||||
&__forms {
|
||||
padding: 20px;
|
||||
@ -967,215 +603,7 @@ $small-breakpoint: 960px;
|
||||
}
|
||||
}
|
||||
|
||||
&__forms {
|
||||
height: 100%;
|
||||
|
||||
@media screen and (max-width: $small-breakpoint) {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $column-breakpoint) {
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
padding: 0 20px;
|
||||
margin-top: 30px;
|
||||
margin-bottom: 40px;
|
||||
|
||||
.separator-or {
|
||||
span {
|
||||
background: darken($ui-base-color, 8%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 40px 0;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.subtle-hint a {
|
||||
text-decoration: none;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#mastodon-timeline {
|
||||
display: flex;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
-ms-overflow-style: -ms-autohiding-scrollbar;
|
||||
font-family: $font-sans-serif, sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
font-weight: 400;
|
||||
color: $primary-text-color;
|
||||
width: 100%;
|
||||
flex: 1 1 auto;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
|
||||
.column-header {
|
||||
color: inherit;
|
||||
font-family: inherit;
|
||||
font-size: 16px;
|
||||
line-height: inherit;
|
||||
font-weight: inherit;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.column {
|
||||
padding: 0;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.scrollable {
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
font-weight: inherit;
|
||||
color: $primary-text-color;
|
||||
margin-bottom: 20px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $secondary-text-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.attachment-list__list {
|
||||
margin-left: 0;
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
font-weight: inherit;
|
||||
margin-bottom: 0;
|
||||
|
||||
a {
|
||||
color: $dark-text-color;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $column-breakpoint) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__features {
|
||||
& > p {
|
||||
padding-right: 60px;
|
||||
}
|
||||
|
||||
.features-list {
|
||||
margin: 40px 0;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
&__action {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.features-list {
|
||||
.features-list__row {
|
||||
display: flex;
|
||||
padding: 10px 0;
|
||||
justify-content: space-between;
|
||||
|
||||
.visual {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 15px;
|
||||
|
||||
.fa {
|
||||
display: block;
|
||||
color: $darker-text-color;
|
||||
font-size: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 16px;
|
||||
line-height: 30px;
|
||||
color: $darker-text-color;
|
||||
|
||||
h6 {
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: $small-breakpoint) {
|
||||
display: grid;
|
||||
grid-gap: 30px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-auto-columns: 50%;
|
||||
grid-auto-rows: max-content;
|
||||
}
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
padding-bottom: 50px;
|
||||
text-align: right;
|
||||
color: $dark-text-color;
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
&__footer {
|
||||
margin-top: 10px;
|
||||
text-align: center;
|
||||
color: $dark-text-color;
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 840px) {
|
||||
.container-alt {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.information-board {
|
||||
.container-alt {
|
||||
padding-right: 20px;
|
||||
@ -1211,109 +639,217 @@ $small-breakpoint: 960px;
|
||||
.features .container-alt {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.header {
|
||||
.links {
|
||||
padding-top: 15px;
|
||||
background: darken($ui-base-color, 4%);
|
||||
|
||||
a {
|
||||
padding: 12px 8px;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.brand img {
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.hero {
|
||||
margin-top: 30px;
|
||||
padding: 0;
|
||||
|
||||
.heading {
|
||||
padding: 30px 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.simple_form,
|
||||
.closed-registrations-message {
|
||||
background: darken($ui-base-color, 8%);
|
||||
width: 100%;
|
||||
border-radius: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cta {
|
||||
margin: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&.tag-page {
|
||||
@media screen and (max-width: $column-breakpoint) {
|
||||
.landing {
|
||||
margin-bottom: 100px;
|
||||
|
||||
@media screen and (max-width: 738px) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&__brand {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 100px;
|
||||
|
||||
img {
|
||||
height: 52px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
padding: 0;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 0;
|
||||
.directory {
|
||||
margin-top: 30px;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.hero-widget {
|
||||
margin-top: 30px;
|
||||
margin-bottom: 0;
|
||||
|
||||
h4 {
|
||||
padding: 10px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
font-size: 13px;
|
||||
color: $darker-text-color;
|
||||
}
|
||||
|
||||
&__text {
|
||||
border-radius: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
background: $ui-base-color;
|
||||
padding: 10px;
|
||||
border-radius: 0 0 4px 4px;
|
||||
display: flex;
|
||||
|
||||
&__column {
|
||||
flex: 1 1 50%;
|
||||
}
|
||||
}
|
||||
|
||||
#mastodon-timeline {
|
||||
.account {
|
||||
padding: 10px 0;
|
||||
border-bottom: 0;
|
||||
|
||||
.account__display-name {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
border-radius: 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.account__avatar {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
background-size: 44px 44px;
|
||||
}
|
||||
}
|
||||
|
||||
.grid {
|
||||
@media screen and (min-width: $small-breakpoint) {
|
||||
grid-template-columns: 33% 67%;
|
||||
&__counter {
|
||||
padding: 10px;
|
||||
|
||||
strong {
|
||||
font-family: $font-display, sans-serif;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.column-2 {
|
||||
grid-column: 2;
|
||||
span {
|
||||
font-size: 14px;
|
||||
color: $darker-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.simple_form .user_agreement .label_input > label {
|
||||
font-weight: 400;
|
||||
color: $darker-text-color;
|
||||
}
|
||||
|
||||
.simple_form p.lead {
|
||||
color: $darker-text-color;
|
||||
font-size: 15px;
|
||||
line-height: 20px;
|
||||
font-weight: 400;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
&__grid {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 50%) minmax(0, 50%);
|
||||
grid-gap: 30px;
|
||||
|
||||
@media screen and (max-width: 738px) {
|
||||
grid-template-columns: minmax(0, 100%);
|
||||
grid-gap: 10px;
|
||||
|
||||
&__column-login {
|
||||
grid-row: 1;
|
||||
}
|
||||
}
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.brand {
|
||||
text-align: unset;
|
||||
padding: 0;
|
||||
|
||||
img {
|
||||
height: 48px;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.cta {
|
||||
margin: 0;
|
||||
|
||||
.button {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $column-breakpoint) {
|
||||
.grid {
|
||||
grid-gap: 0;
|
||||
|
||||
.column-1 {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
.box-widget {
|
||||
order: 2;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.column-2 {
|
||||
display: none;
|
||||
.hero-widget {
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
order: 1;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
&__column-registration {
|
||||
grid-row: 2;
|
||||
}
|
||||
|
||||
.directory {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
grid-gap: 0;
|
||||
|
||||
.hero-widget {
|
||||
display: block;
|
||||
margin-bottom: 0;
|
||||
box-shadow: none;
|
||||
|
||||
&__img,
|
||||
&__img img,
|
||||
&__footer {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-widget,
|
||||
.box-widget,
|
||||
.directory__tag {
|
||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||
}
|
||||
|
||||
.directory {
|
||||
margin-top: 0;
|
||||
|
||||
&__tag {
|
||||
margin-bottom: 0;
|
||||
|
||||
& > a,
|
||||
& > div {
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.brand {
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.brand__tagline {
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: -10px;
|
||||
left: 50px;
|
||||
width: 300px;
|
||||
color: $ui-primary-color;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
position: static;
|
||||
width: auto;
|
||||
margin-top: 20px;
|
||||
color: $dark-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,6 +68,17 @@ code {
|
||||
top: 2px;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
label a {
|
||||
color: $highlight-text-color;
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -305,7 +316,7 @@ code {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&:focus:invalid {
|
||||
&:focus:invalid:not(:placeholder-shown) {
|
||||
border-color: lighten($error-red, 12%);
|
||||
}
|
||||
|
||||
@ -346,6 +357,10 @@ code {
|
||||
}
|
||||
}
|
||||
|
||||
.input.disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-top: 30px;
|
||||
display: flex;
|
||||
@ -392,6 +407,10 @@ code {
|
||||
background-color: darken($ui-highlight-color, 5%);
|
||||
}
|
||||
|
||||
&:disabled:hover {
|
||||
background-color: $ui-primary-color;
|
||||
}
|
||||
|
||||
&.negative {
|
||||
background: $error-value-color;
|
||||
|
||||
|
@ -295,6 +295,11 @@
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&.disabled > div {
|
||||
opacity: 0.5;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
h4 {
|
||||
flex: 1 1 auto;
|
||||
font-size: 18px;
|
||||
|
@ -21,6 +21,10 @@ class InstancePresenter
|
||||
Rails.cache.fetch('user_count') { User.confirmed.joins(:account).merge(Account.without_suspended).count }
|
||||
end
|
||||
|
||||
def active_user_count
|
||||
Rails.cache.fetch('active_user_count') { Redis.current.pfcount(*(0..3).map { |i| "activity:logins:#{i.weeks.ago.utc.to_date.cweek}" }) }
|
||||
end
|
||||
|
||||
def status_count
|
||||
Rails.cache.fetch('local_status_count') { Account.local.joins(:account_stat).sum('account_stats.statuses_count') }.to_i
|
||||
end
|
||||
@ -29,6 +33,10 @@ class InstancePresenter
|
||||
Rails.cache.fetch('distinct_domain_count') { Account.distinct.count(:domain) }
|
||||
end
|
||||
|
||||
def sample_accounts
|
||||
Rails.cache.fetch('sample_accounts', expires_in: 12.hours) { Account.local.searchable.joins(:account_stat).popular.limit(3) }
|
||||
end
|
||||
|
||||
def version_number
|
||||
Mastodon::Version
|
||||
end
|
||||
|
@ -1,25 +0,0 @@
|
||||
.features-list
|
||||
.features-list__row
|
||||
.text
|
||||
%h6= t 'about.features.real_conversation_title'
|
||||
= t 'about.features.real_conversation_body'
|
||||
.visual
|
||||
= fa_icon 'fw comments'
|
||||
.features-list__row
|
||||
.text
|
||||
%h6= t 'about.features.not_a_product_title'
|
||||
= t 'about.features.not_a_product_body'
|
||||
.visual
|
||||
= fa_icon 'fw users'
|
||||
.features-list__row
|
||||
.text
|
||||
%h6= t 'about.features.within_reach_title'
|
||||
= t 'about.features.within_reach_body'
|
||||
.visual
|
||||
= fa_icon 'fw mobile'
|
||||
.features-list__row
|
||||
.text
|
||||
%h6= t 'about.features.humane_approach_title'
|
||||
= t 'about.features.humane_approach_body'
|
||||
.visual
|
||||
= fa_icon 'fw leaf'
|
@ -1,15 +0,0 @@
|
||||
- if @instance_presenter.open_registrations
|
||||
= render 'registration'
|
||||
- else
|
||||
= link_to t('auth.register_elsewhere'), 'https://joinmastodon.org/#getting-started', class: 'button button-primary'
|
||||
|
||||
.closed-registrations-message
|
||||
- if @instance_presenter.closed_registrations_message.blank?
|
||||
%p= t('about.closed_registrations')
|
||||
- else
|
||||
= @instance_presenter.closed_registrations_message.html_safe
|
||||
|
||||
.separator-or
|
||||
%span= t('auth.or')
|
||||
|
||||
= link_to t('auth.login'), new_user_session_path, class: 'button button-alternative-2 webapp-btn'
|
@ -1,16 +0,0 @@
|
||||
.container-alt.links
|
||||
.brand
|
||||
= link_to root_url do
|
||||
= image_tag asset_pack_path('logo_full.svg'), alt: 'Mastodon'
|
||||
|
||||
%ul.nav
|
||||
%li
|
||||
- if user_signed_in?
|
||||
= link_to t('settings.back'), root_url, class: 'webapp-btn'
|
||||
- else
|
||||
= link_to t('auth.login'), new_user_session_path, class: 'webapp-btn'
|
||||
%li= link_to t('about.about_this'), about_more_path
|
||||
%li
|
||||
= link_to 'https://joinmastodon.org/#getting-started' do
|
||||
= "#{t('about.other_instances')}"
|
||||
%i.fa.fa-external-link{ style: 'padding-left: 5px;' }
|
13
app/views/about/_login.html.haml
Normal file
13
app/views/about/_login.html.haml
Normal file
@ -0,0 +1,13 @@
|
||||
= simple_form_for(new_user, url: user_session_path) do |f|
|
||||
.fields-group
|
||||
- if use_seamless_external_login?
|
||||
= f.input :email, placeholder: t('simple_form.labels.defaults.username_or_email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.username_or_email') }, hint: false
|
||||
- else
|
||||
= f.input :email, placeholder: t('simple_form.labels.defaults.email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }, hint: false
|
||||
|
||||
= f.input :password, placeholder: t('simple_form.labels.defaults.password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.password') }, hint: false
|
||||
|
||||
.actions
|
||||
= f.button :button, t('auth.login'), type: :submit, class: 'button button-primary'
|
||||
|
||||
%p.hint.subtle-hint= link_to t('auth.trouble_logging_in'), new_user_password_path
|
@ -1,12 +1,16 @@
|
||||
= simple_form_for(new_user, url: user_registration_path) do |f|
|
||||
= f.simple_fields_for :account do |account_fields|
|
||||
= account_fields.input :username, wrapper: :with_label, autofocus: true, label: false, required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off', placeholder: t('simple_form.labels.defaults.username') }, append: "@#{site_hostname}", hint: false
|
||||
%p.lead= t('about.federation_hint_html', instance: content_tag(:strong, site_hostname))
|
||||
|
||||
= f.input :email, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' }, hint: false
|
||||
= f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' }, hint: false
|
||||
= f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' }, hint: false
|
||||
.fields-group
|
||||
= f.simple_fields_for :account do |account_fields|
|
||||
= account_fields.input :username, wrapper: :with_label, autofocus: true, label: false, required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off', placeholder: t('simple_form.labels.defaults.username') }, append: "@#{site_hostname}", hint: false, disabled: !Setting.open_registrations
|
||||
|
||||
= f.input :email, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' }, hint: false, disabled: !Setting.open_registrations
|
||||
= f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' }, hint: false, disabled: !Setting.open_registrations
|
||||
= f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' }, hint: false, disabled: !Setting.open_registrations
|
||||
|
||||
.fields-group
|
||||
= f.input :agreement, as: :boolean, wrapper: :with_label, label: t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path), disabled: !Setting.open_registrations
|
||||
|
||||
.actions
|
||||
= f.button :button, t('auth.register'), type: :submit, class: 'button button-primary'
|
||||
|
||||
%p.hint.subtle-hint=t('auth.agreement_html', rules_path: about_more_path, terms_path: terms_path)
|
||||
= f.button :button, Setting.open_registrations ? t('auth.register') : t('auth.registration_closed', instance: site_hostname), type: :submit, class: 'button button-primary', disabled: !Setting.open_registrations
|
||||
|
@ -3,144 +3,76 @@
|
||||
|
||||
- content_for :header_tags do
|
||||
%link{ rel: 'canonical', href: about_url }/
|
||||
%script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json)
|
||||
= javascript_pack_tag 'about', integrity: true, crossorigin: 'anonymous'
|
||||
= render partial: 'shared/og'
|
||||
|
||||
.landing-page.alternative
|
||||
.container
|
||||
.grid
|
||||
.column-0
|
||||
.brand
|
||||
= link_to root_url do
|
||||
= image_tag asset_pack_path('logo_full.svg'), alt: 'Mastodon'
|
||||
.landing
|
||||
.landing__brand
|
||||
= link_to root_url, class: 'brand' do
|
||||
= image_tag asset_pack_path('logo_full.svg'), alt: 'Mastodon'
|
||||
%span.brand__tagline=t 'about.tagline'
|
||||
|
||||
- if Setting.timeline_preview
|
||||
.column-1
|
||||
.landing-page__forms
|
||||
.brand
|
||||
= link_to root_url do
|
||||
= image_tag asset_pack_path('logo_full.svg'), alt: 'Mastodon'
|
||||
.landing__grid
|
||||
.landing__grid__column.landing__grid__column-registration
|
||||
.box-widget
|
||||
= render 'registration'
|
||||
|
||||
= render 'forms'
|
||||
.directory
|
||||
.directory__tag{ class: Setting.profile_directory ? nil : 'disabled' }
|
||||
= optional_link_to Setting.profile_directory, explore_path do
|
||||
%h4
|
||||
= fa_icon 'address-book fw'
|
||||
= t('about.discover_users')
|
||||
%small= t('about.browse_directory')
|
||||
|
||||
- else
|
||||
.column-1.non-preview
|
||||
.landing-page__forms
|
||||
.brand
|
||||
= link_to root_url do
|
||||
= image_tag asset_pack_path('logo_full.svg'), alt: 'Mastodon'
|
||||
.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, width: 48, height: 48, alt: '', class: 'account__avatar'
|
||||
|
||||
= render 'forms'
|
||||
.directory__tag{ class: Setting.timeline_preview ? nil : 'disabled' }
|
||||
= 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')
|
||||
|
||||
- if Setting.timeline_preview
|
||||
.column-2
|
||||
.landing-page__hero
|
||||
= image_tag @instance_presenter.hero&.file&.url || @instance_presenter.thumbnail&.file&.url || asset_pack_path('preview.jpg'), alt: @instance_presenter.site_title
|
||||
.directory__tag
|
||||
= link_to 'https://joinmastodon.org/apps', target: '_blank', rel: 'noopener' do
|
||||
%h4
|
||||
= fa_icon 'tablet fw'
|
||||
= t('about.get_apps')
|
||||
%small= t('about.apps_platforms')
|
||||
|
||||
.landing-page__information
|
||||
.landing-page__short-description
|
||||
.row
|
||||
.landing-page__logo
|
||||
= image_tag asset_pack_path('logo_transparent.svg'), alt: 'Mastodon'
|
||||
.landing__grid__column.landing__grid__column-login
|
||||
.box-widget
|
||||
= render 'login'
|
||||
|
||||
%h1
|
||||
= @instance_presenter.site_title
|
||||
%small!= t 'about.hosted_on', domain: content_tag(:span, site_hostname)
|
||||
.hero-widget
|
||||
.hero-widget__img
|
||||
= image_tag @instance_presenter.hero&.file&.url || @instance_presenter.thumbnail&.file&.url || asset_pack_path('preview.jpg'), alt: @instance_presenter.site_title
|
||||
|
||||
%p= @instance_presenter.site_description.html_safe.presence || t('about.generic_description', domain: site_hostname)
|
||||
|
||||
.landing-page__call-to-action{ dir: 'ltr' }
|
||||
.row
|
||||
.row__information-board
|
||||
.information-board__section
|
||||
%span= t 'about.user_count_before'
|
||||
%strong= number_with_delimiter @instance_presenter.user_count
|
||||
%span= t 'about.user_count_after', count: @instance_presenter.user_count
|
||||
.information-board__section
|
||||
%span= t 'about.status_count_before'
|
||||
%strong= number_with_delimiter @instance_presenter.status_count
|
||||
%span= t 'about.status_count_after', count: @instance_presenter.status_count
|
||||
.row__mascot
|
||||
.landing-page__mascot
|
||||
= image_tag @instance_presenter.mascot&.file&.url || asset_pack_path('elephant_ui_plane.svg'), alt: ''
|
||||
|
||||
- else
|
||||
.column-2.non-preview
|
||||
.landing-page__hero
|
||||
= image_tag @instance_presenter.hero&.file&.url || @instance_presenter.thumbnail&.file&.url || asset_pack_path('preview.jpg'), alt: @instance_presenter.site_title
|
||||
|
||||
.landing-page__information
|
||||
.landing-page__short-description
|
||||
.row
|
||||
.landing-page__logo
|
||||
= image_tag asset_pack_path('logo_transparent.svg'), alt: 'Mastodon'
|
||||
|
||||
%h1
|
||||
= @instance_presenter.site_title
|
||||
%small!= t 'about.hosted_on', domain: content_tag(:span, site_hostname)
|
||||
|
||||
%p= @instance_presenter.site_description.html_safe.presence || t('about.generic_description', domain: site_hostname)
|
||||
|
||||
.landing-page__call-to-action
|
||||
.row
|
||||
.row__information-board
|
||||
.information-board__section
|
||||
%span= t 'about.user_count_before'
|
||||
%strong= number_with_delimiter @instance_presenter.user_count
|
||||
%span= t 'about.user_count_after', count: @instance_presenter.user_count
|
||||
.information-board__section
|
||||
%span= t 'about.status_count_before'
|
||||
%strong= number_with_delimiter @instance_presenter.status_count
|
||||
%span= t 'about.status_count_after', count: @instance_presenter.status_count
|
||||
.row__mascot
|
||||
.landing-page__mascot
|
||||
= image_tag @instance_presenter.mascot&.file&.url || asset_pack_path('elephant_ui_plane.svg'), alt: ''
|
||||
|
||||
- if Setting.timeline_preview
|
||||
.column-3
|
||||
#mastodon-timeline{ data: { props: Oj.dump(default_props) } }
|
||||
|
||||
- if Setting.timeline_preview
|
||||
.column-4.landing-page__information
|
||||
.landing-page__features
|
||||
.features-list
|
||||
%div
|
||||
%h3= t 'about.what_is_mastodon'
|
||||
%p= t 'about.about_mastodon_html'
|
||||
%div.contact
|
||||
%h3= t 'about.administered_by'
|
||||
= account_link_to(@instance_presenter.contact_account, link_to(t('about.learn_more'), about_more_path, class: 'button button-alternative'))
|
||||
|
||||
= render 'features'
|
||||
|
||||
.landing-page__features__action
|
||||
= link_to t('about.learn_more'), 'https://joinmastodon.org/', class: 'button button-alternative'
|
||||
|
||||
.landing-page__footer
|
||||
- if @instance_presenter.site_short_description.present?
|
||||
.hero-widget__text
|
||||
%p
|
||||
= link_to t('about.source_code'), @instance_presenter.source_url
|
||||
= " (#{@instance_presenter.version_number})"
|
||||
= @instance_presenter.site_short_description.html_safe.presence
|
||||
= link_to about_more_path do
|
||||
= t('about.learn_more')
|
||||
= fa_icon 'angle-double-right'
|
||||
|
||||
- else
|
||||
.column-4.non-preview.landing-page__information
|
||||
.landing-page__features
|
||||
.features-list
|
||||
%div
|
||||
%h3= t 'about.what_is_mastodon'
|
||||
%p= t 'about.about_mastodon_html'
|
||||
%div.contact
|
||||
%h3= t 'about.administered_by'
|
||||
= account_link_to(@instance_presenter.contact_account, link_to(t('about.learn_more'), about_more_path, class: 'button button-alternative'))
|
||||
.hero-widget__footer
|
||||
.hero-widget__footer__column
|
||||
%h4= t 'about.administered_by'
|
||||
|
||||
= render 'features'
|
||||
= account_link_to @instance_presenter.contact_account
|
||||
|
||||
.landing-page__features__action
|
||||
= link_to t('about.learn_more'), 'https://joinmastodon.org/', class: 'button button-alternative'
|
||||
.hero-widget__footer__column
|
||||
%h4= t 'about.server_stats'
|
||||
|
||||
.landing-page__footer
|
||||
%p
|
||||
= link_to t('about.source_code'), @instance_presenter.source_url
|
||||
= " (#{@instance_presenter.version_number})"
|
||||
|
||||
#modal-container
|
||||
%div{ style: 'display: flex' }
|
||||
.hero-widget__counter{ style: 'width: 50%' }
|
||||
%strong= number_to_human @instance_presenter.user_count, strip_insignificant_zeros: true
|
||||
%span= t 'about.user_count_after', count: @instance_presenter.user_count
|
||||
.hero-widget__counter{ style: 'width: 50%' }
|
||||
%strong= number_to_human @instance_presenter.active_user_count, strip_insignificant_zeros: true
|
||||
%span
|
||||
= t 'about.active_count_after'
|
||||
%abbr{ title: t('about.active_footnote') } *
|
||||
|
@ -3,23 +3,24 @@
|
||||
|
||||
- content_for :content do
|
||||
.public-layout
|
||||
.container
|
||||
%nav.header
|
||||
.nav-left
|
||||
= link_to root_url, class: 'brand' do
|
||||
= image_tag asset_pack_path('logo_full.svg'), alt: 'Mastodon'
|
||||
- unless @hide_navbar
|
||||
.container
|
||||
%nav.header
|
||||
.nav-left
|
||||
= link_to root_url, class: 'brand' do
|
||||
= image_tag asset_pack_path('logo_full.svg'), alt: 'Mastodon'
|
||||
|
||||
- if Setting.profile_directory
|
||||
= link_to t('directories.directory'), explore_path, class: 'nav-link optional'
|
||||
= 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'
|
||||
.nav-center
|
||||
.nav-right
|
||||
- if user_signed_in?
|
||||
= link_to t('settings.back'), root_url, class: 'nav-link nav-button webapp-btn'
|
||||
- else
|
||||
= link_to t('auth.login'), new_user_session_path, class: 'webapp-btn nav-link nav-button'
|
||||
= link_to t('auth.register'), open_registrations? ? new_user_registration_path : 'https://joinmastodon.org/#getting-started', class: 'webapp-btn nav-link nav-button'
|
||||
- if Setting.profile_directory
|
||||
= link_to t('directories.directory'), explore_path, class: 'nav-link optional'
|
||||
= 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'
|
||||
.nav-center
|
||||
.nav-right
|
||||
- if user_signed_in?
|
||||
= link_to t('settings.back'), root_url, class: 'nav-link nav-button webapp-btn'
|
||||
- else
|
||||
= link_to t('auth.login'), new_user_session_path, class: 'webapp-btn nav-link nav-button'
|
||||
= link_to t('auth.register'), open_registrations? ? new_user_registration_path : 'https://joinmastodon.org/#getting-started', class: 'webapp-btn nav-link nav-button'
|
||||
|
||||
.container= yield
|
||||
|
||||
|
14
app/views/public_timelines/show.html.haml
Normal file
14
app/views/public_timelines/show.html.haml
Normal file
@ -0,0 +1,14 @@
|
||||
- content_for :page_title do
|
||||
= t('about.see_whats_happening')
|
||||
|
||||
- content_for :header_tags do
|
||||
%meta{ name: 'robots', content: 'noindex' }/
|
||||
%script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json)
|
||||
= javascript_pack_tag 'about', integrity: true, crossorigin: 'anonymous'
|
||||
|
||||
.page-header
|
||||
%h1= t('about.see_whats_happening')
|
||||
%p= t('about.browse_public_posts')
|
||||
|
||||
#mastodon-timeline{ data: { props: Oj.dump(default_props) }}
|
||||
#modal-container
|
@ -2,6 +2,7 @@
|
||||
= "##{@tag.name}"
|
||||
|
||||
- content_for :header_tags do
|
||||
%meta{ name: 'robots', content: 'noindex' }/
|
||||
%link{ rel: 'alternate', type: 'application/rss+xml', href: tag_url(@tag, format: 'rss') }/
|
||||
|
||||
%script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json)
|
||||
|
Reference in New Issue
Block a user