Add dismissable hints to various timelines in web UI (#19315)
Co-authored-by: Yamagishi Kazutoshi <ykzts@desire.sh>
This commit is contained in:
parent
a5112b51fd
commit
f41ec9af05
51
app/javascript/mastodon/components/dismissable_banner.js
Normal file
51
app/javascript/mastodon/components/dismissable_banner.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
import React from 'react';
|
||||
import IconButton from './icon_button';
|
||||
import PropTypes from 'prop-types';
|
||||
import { injectIntl, defineMessages } from 'react-intl';
|
||||
import { bannerSettings } from 'mastodon/settings';
|
||||
|
||||
const messages = defineMessages({
|
||||
dismiss: { id: 'dismissable_banner.dismiss', defaultMessage: 'Dismiss' },
|
||||
});
|
||||
|
||||
export default @injectIntl
|
||||
class DismissableBanner extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
children: PropTypes.node,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
visible: !bannerSettings.get(this.props.id),
|
||||
};
|
||||
|
||||
handleDismiss = () => {
|
||||
const { id } = this.props;
|
||||
this.setState({ visible: false }, () => bannerSettings.set(id, true));
|
||||
}
|
||||
|
||||
render () {
|
||||
const { visible } = this.state;
|
||||
|
||||
if (!visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { children, intl } = this.props;
|
||||
|
||||
return (
|
||||
<div className='dismissable-banner'>
|
||||
<div className='dismissable-banner__message'>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
<div className='dismissable-banner__action'>
|
||||
<IconButton icon='times' title={intl.formatMessage(messages.dismiss)} onClick={this.handleDismiss} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -10,6 +10,8 @@ import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
|||
import ColumnSettingsContainer from './containers/column_settings_container';
|
||||
import { connectCommunityStream } from '../../actions/streaming';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { domain } from 'mastodon/initial_state';
|
||||
import DismissableBanner from 'mastodon/components/dismissable_banner';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'column.community', defaultMessage: 'Local timeline' },
|
||||
|
@ -134,6 +136,10 @@ class CommunityTimeline extends React.PureComponent {
|
|||
<ColumnSettingsContainer columnId={columnId} />
|
||||
</ColumnHeader>
|
||||
|
||||
<DismissableBanner id='community_timeline'>
|
||||
<FormattedMessage id='dismissable_banner.community_timeline' defaultMessage='These are the most recent public posts from people whose accounts are hosted by {domain}.' values={{ domain }} />
|
||||
</DismissableBanner>
|
||||
|
||||
<StatusListContainer
|
||||
trackScroll={!pinned}
|
||||
scrollKey={`community_timeline-${columnId}`}
|
||||
|
|
|
@ -6,6 +6,7 @@ import LoadingIndicator from 'mastodon/components/loading_indicator';
|
|||
import { connect } from 'react-redux';
|
||||
import { fetchTrendingLinks } from 'mastodon/actions/trends';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import DismissableBanner from 'mastodon/components/dismissable_banner';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
links: state.getIn(['trends', 'links', 'items']),
|
||||
|
@ -29,9 +30,17 @@ class Links extends React.PureComponent {
|
|||
render () {
|
||||
const { isLoading, links } = this.props;
|
||||
|
||||
const banner = (
|
||||
<DismissableBanner id='explore/links'>
|
||||
<FormattedMessage id='dismissable_banner.explore_links' defaultMessage='These news stories are being talked about by people on this and other servers of the decentralized network right now.' />
|
||||
</DismissableBanner>
|
||||
);
|
||||
|
||||
if (!isLoading && links.isEmpty()) {
|
||||
return (
|
||||
<div className='explore__links scrollable scrollable--flex'>
|
||||
{banner}
|
||||
|
||||
<div className='empty-column-indicator'>
|
||||
<FormattedMessage id='empty_column.explore_statuses' defaultMessage='Nothing is trending right now. Check back later!' />
|
||||
</div>
|
||||
|
@ -41,6 +50,8 @@ class Links extends React.PureComponent {
|
|||
|
||||
return (
|
||||
<div className='explore__links'>
|
||||
{banner}
|
||||
|
||||
{isLoading ? (<LoadingIndicator />) : links.map(link => (
|
||||
<Story
|
||||
key={link.get('id')}
|
||||
|
|
|
@ -6,6 +6,7 @@ import { FormattedMessage } from 'react-intl';
|
|||
import { connect } from 'react-redux';
|
||||
import { fetchTrendingStatuses, expandTrendingStatuses } from 'mastodon/actions/trends';
|
||||
import { debounce } from 'lodash';
|
||||
import DismissableBanner from 'mastodon/components/dismissable_banner';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
statusIds: state.getIn(['status_lists', 'trending', 'items']),
|
||||
|
@ -40,17 +41,23 @@ class Statuses extends React.PureComponent {
|
|||
const emptyMessage = <FormattedMessage id='empty_column.explore_statuses' defaultMessage='Nothing is trending right now. Check back later!' />;
|
||||
|
||||
return (
|
||||
<StatusList
|
||||
trackScroll
|
||||
statusIds={statusIds}
|
||||
scrollKey='explore-statuses'
|
||||
hasMore={hasMore}
|
||||
isLoading={isLoading}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
emptyMessage={emptyMessage}
|
||||
bindToDocument={!multiColumn}
|
||||
withCounters
|
||||
/>
|
||||
<>
|
||||
<DismissableBanner id='explore/statuses'>
|
||||
<FormattedMessage id='dismissable_banner.explore_statuses' defaultMessage='These posts from this and other servers in the decentralized network are gaining traction on this server right now.' />
|
||||
</DismissableBanner>
|
||||
|
||||
<StatusList
|
||||
trackScroll
|
||||
statusIds={statusIds}
|
||||
scrollKey='explore-statuses'
|
||||
hasMore={hasMore}
|
||||
isLoading={isLoading}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
emptyMessage={emptyMessage}
|
||||
bindToDocument={!multiColumn}
|
||||
withCounters
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import LoadingIndicator from 'mastodon/components/loading_indicator';
|
|||
import { connect } from 'react-redux';
|
||||
import { fetchTrendingHashtags } from 'mastodon/actions/trends';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import DismissableBanner from 'mastodon/components/dismissable_banner';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
hashtags: state.getIn(['trends', 'tags', 'items']),
|
||||
|
@ -29,9 +30,17 @@ class Tags extends React.PureComponent {
|
|||
render () {
|
||||
const { isLoading, hashtags } = this.props;
|
||||
|
||||
const banner = (
|
||||
<DismissableBanner id='explore/tags'>
|
||||
<FormattedMessage id='dismissable_banner.explore_tags' defaultMessage='These hashtags are gaining traction among people on this and other servers of the decentralized network right now.' />
|
||||
</DismissableBanner>
|
||||
);
|
||||
|
||||
if (!isLoading && hashtags.isEmpty()) {
|
||||
return (
|
||||
<div className='explore__links scrollable scrollable--flex'>
|
||||
{banner}
|
||||
|
||||
<div className='empty-column-indicator'>
|
||||
<FormattedMessage id='empty_column.explore_statuses' defaultMessage='Nothing is trending right now. Check back later!' />
|
||||
</div>
|
||||
|
@ -41,6 +50,8 @@ class Tags extends React.PureComponent {
|
|||
|
||||
return (
|
||||
<div className='explore__links'>
|
||||
{banner}
|
||||
|
||||
{isLoading ? (<LoadingIndicator />) : hashtags.map(hashtag => (
|
||||
<Hashtag key={hashtag.get('name')} hashtag={hashtag} />
|
||||
))}
|
||||
|
|
|
@ -10,6 +10,7 @@ import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
|||
import ColumnSettingsContainer from './containers/column_settings_container';
|
||||
import { connectPublicStream } from '../../actions/streaming';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import DismissableBanner from 'mastodon/components/dismissable_banner';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'column.public', defaultMessage: 'Federated timeline' },
|
||||
|
@ -137,6 +138,10 @@ class PublicTimeline extends React.PureComponent {
|
|||
<ColumnSettingsContainer columnId={columnId} />
|
||||
</ColumnHeader>
|
||||
|
||||
<DismissableBanner id='public_timeline'>
|
||||
<FormattedMessage id='dismissable_banner.public_timeline' defaultMessage='These are the most recent public posts from people on this and other servers of the decentralized network that this server knows about.' />
|
||||
</DismissableBanner>
|
||||
|
||||
<StatusListContainer
|
||||
timelineId={`public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
|
|
|
@ -45,3 +45,4 @@ export default class Settings {
|
|||
|
||||
export const pushNotificationsSetting = new Settings('mastodon_push_notification_data');
|
||||
export const tagHistory = new Settings('mastodon_tag_history');
|
||||
export const bannerSettings = new Settings('mastodon_banner_settings');
|
||||
|
|
|
@ -8312,3 +8312,28 @@ noscript {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dismissable-banner {
|
||||
background: $ui-base-color;
|
||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 30px;
|
||||
|
||||
&__message {
|
||||
flex: 1 1 auto;
|
||||
padding: 20px 15px;
|
||||
cursor: default;
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
color: $primary-text-color;
|
||||
}
|
||||
|
||||
&__action {
|
||||
padding: 15px;
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue