Merge remote-tracking branch 'origin/features/main' into features/quote

This commit is contained in:
2024-02-04 11:40:23 +09:00
61 changed files with 770 additions and 238 deletions

View File

@@ -10,6 +10,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
cache_if_unauthenticated!
@statuses = load_statuses
accounts = @statuses.filter_map { |status| status.quote&.account }.uniq
account_ids = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
render json: @statuses, each_serializer: REST::StatusSerializer,
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
account_relationships: AccountRelationshipsPresenter.new(accounts, current_user&.account_id)

View File

@@ -8,6 +8,7 @@ class Api::V1::BookmarksController < Api::BaseController
def index
@statuses = load_statuses
accounts = @statuses.filter_map { |status| status.quote&.account }.uniq
account_ids = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
render json: @statuses, each_serializer: REST::StatusSerializer,
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
account_relationships: AccountRelationshipsPresenter.new(accounts, current_user&.account_id)

View File

@@ -8,6 +8,7 @@ class Api::V1::FavouritesController < Api::BaseController
def index
@statuses = load_statuses
accounts = @statuses.filter_map { |status| status.quote&.account }.uniq
account_ids = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
render json: @statuses, each_serializer: REST::StatusSerializer,
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
account_relationships: AccountRelationshipsPresenter.new(accounts, current_user&.account_id)

View File

@@ -50,6 +50,7 @@ class Api::V1::StatusesController < Api::BaseController
@context = Context.new(ancestors: loaded_ancestors, descendants: loaded_descendants)
statuses = [@status] + @context.ancestors + @context.descendants
accounts = statuses.filter_map { |status| status.quote&.account }.uniq
account_ids = statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
render json: @context, serializer: REST::ContextSerializer,
relationships: StatusRelationshipsPresenter.new(statuses, current_user&.account_id),

View File

@@ -11,6 +11,8 @@ class Api::V1::Timelines::HomeController < Api::V1::Timelines::BaseController
@statuses = load_statuses
@relationships = StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
accounts = @statuses.filter_map { |status| status.quote&.account }.uniq
account_ids = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
@account_relationships = AccountRelationshipsPresenter.new(accounts, current_user&.account_id)
end

View File

@@ -10,6 +10,7 @@ class Api::V1::Timelines::ListController < Api::V1::Timelines::BaseController
def show
accounts = @statuses.filter_map { |status| status.quote&.account }.uniq
account_ids = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
render json: @statuses,
each_serializer: REST::StatusSerializer,

View File

@@ -9,6 +9,7 @@ class Api::V1::Timelines::PublicController < Api::V1::Timelines::BaseController
cache_if_unauthenticated!
@statuses = load_statuses
accounts = @statuses.filter_map { |status| status.quote&.account }.uniq
account_ids = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
render json: @statuses, each_serializer: REST::StatusSerializer,
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),

View File

@@ -10,6 +10,7 @@ class Api::V1::Timelines::TagController < Api::V1::Timelines::BaseController
cache_if_unauthenticated!
@statuses = load_statuses
accounts = @statuses.filter_map { |status| status.quote&.account }.uniq
account_ids = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
render json: @statuses, each_serializer: REST::StatusSerializer,
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),

View File

@@ -11,6 +11,8 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
before_action :set_last_used_at_by_app, only: :index, unless: -> { request.format == :json }
before_action :set_last_used_at_by_app, only: :index, unless: -> { request.format == :json }
skip_before_action :require_functional!
include Localized

Binary file not shown.

Before

Width:  |  Height:  |  Size: 588 B

After

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 735 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,89 @@
import { changeCompose } from '../actions/compose';
export const UTILBTNS_GOJI = 'UTILBTNS_GOJI';
export const UTILBTNS_HARUKIN = 'UTILBTNS_HARUKIN';
export const UTILBTNS_RISA = 'UTILBTNS_RISA';
export function submitGoji (textarea) {
return function (dispatch, getState) {
if (!textarea.value) {
let text = [
"#ゴジモリィィィィイイ",
":goji:"
].join("\r\n");
dispatch(submitGojiRequest());
dispatch(changeCompose(text));
textarea.focus();
}
}
}
export function submitGojiRequest () {
return {
type: UTILBTNS_GOJI
}
}
export function submitHarukin (textarea) {
return function (dispatch, getState) {
const HARUKINS = [":harukin: ", ":harukin_old: ", ":harukin_ika: ", ":harukin_tako: "];
const MAX = 6;
if (!textarea.value) {
let text = "";
let quantity = Math.round(Math.random() * MAX + 1);
let type = Math.round(Math.random() * (HARUKINS.length - 1));
let harukin = HARUKINS[type];
switch (quantity) {
default:
text = [
harukin.repeat(quantity),
"🔥 ".repeat(quantity)
].join("\r\n");
break;
case MAX + 1:
text = `${harukin}💕\r\n`.repeat(6);
break;
}
dispatch(submitHarukinRequest());
dispatch(changeCompose(text));
textarea.focus();
}
}
}
export function submitHarukinRequest () {
return {
type: UTILBTNS_HARUKIN
}
}
export function submitRisa (textarea) {
return function (dispatch, getState) {
if (!textarea.value) {
let text = [
"@risa2 "
].join("\r\n");
dispatch(submitRisaRequest());
dispatch(changeCompose(text));
textarea.focus();
}
}
}
export function submitRisaRequest () {
return {
type: UTILBTNS_RISA
}
}

View File

@@ -1,8 +1,8 @@
import api, { getLinks } from '../api';
import { uniq } from '../utils/uniq';
import { fetchRelationships } from './accounts';
import { importFetchedStatuses } from './importer';
import { uniq } from '../utils/uniq';
export const BOOKMARKED_STATUSES_FETCH_REQUEST = 'BOOKMARKED_STATUSES_FETCH_REQUEST';
export const BOOKMARKED_STATUSES_FETCH_SUCCESS = 'BOOKMARKED_STATUSES_FETCH_SUCCESS';

View File

@@ -1,8 +1,8 @@
import api, { getLinks } from '../api';
import { uniq } from '../utils/uniq';
import { fetchRelationships } from './accounts';
import { importFetchedStatuses } from './importer';
import { uniq } from '../utils/uniq';
export const FAVOURITED_STATUSES_FETCH_REQUEST = 'FAVOURITED_STATUSES_FETCH_REQUEST';
export const FAVOURITED_STATUSES_FETCH_SUCCESS = 'FAVOURITED_STATUSES_FETCH_SUCCESS';

View File

@@ -1,4 +1,4 @@
import logo from '@/images/logo.svg';
import logo from '@/images/y-zu-logo.svg';
export const WordmarkLogo: React.FC = () => (
<svg viewBox='0 0 261 66' className='logo logo--wordmark' role='img'>
@@ -9,4 +9,4 @@ export const WordmarkLogo: React.FC = () => (
export const SymbolLogo: React.FC = () => (
<img src={logo} alt='Mastodon' className='logo logo--icon' />
);
);

View File

@@ -310,6 +310,10 @@ class MediaGallery extends PureComponent {
style.height /= 2;
}
if (quote && style.height) {
style.height /= 2;
}
const size = media.take(4).size;
const uncached = media.every(attachment => attachment.get('type') === 'unknown');

View File

@@ -6,7 +6,6 @@ import classNames from 'classnames';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import { HotKeys } from 'react-hotkeys';
@@ -286,6 +285,15 @@ class Status extends ImmutablePureComponent {
}
};
handleQuoteUserClick = () =>{
if (!this.props) {
return;
}
const { status } = this.props;
this.location.href(`/@${status.getIn(['account', 'acct'])}`);
}
handleExpandedToggle = () => {
this.props.onToggleHidden(this._properStatus());
};
@@ -456,7 +464,7 @@ class Status extends ImmutablePureComponent {
};
render () {
const { intl, hidden, featured, unread, showThread, scrollKey, pictureInPicture, previousId, nextInReplyToId, rootId, quoteMuted, contextType } = this.props;
const { intl, hidden, featured, unread, showThread, scrollKey, pictureInPicture, previousId, quoteMuted, nextInReplyToId, rootId, contextType } = this.props;
let { status, account, ...other } = this.props;
@@ -594,7 +602,7 @@ class Status extends ImmutablePureComponent {
const description = attachment.getIn(['translation', 'description']) || attachment.get('description');
return (
<Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer}>
<Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} >
{Component => (
<Component
preview={attachment.get('preview_url')}
@@ -607,8 +615,8 @@ class Status extends ImmutablePureComponent {
sensitive={status.get('sensitive')}
onOpenVideo={this.handleOpenVideo}
deployPictureInPicture={pictureInPicture.get('available') ? this.handleDeployPictureInPicture : undefined}
visible={quote ? this.state.showQuoteMedia : this.state.showMedia}
onToggleVisibility={quote ? this.handleToggleQuoteMediaVisibility : this.handleToggleMediaVisibility}
visible={this.state.showMedia}
onToggleVisibility={this.handleToggleMediaVisibility}
quote={quote}
/>
)}
@@ -626,8 +634,8 @@ class Status extends ImmutablePureComponent {
onOpenMedia={this.handleOpenMedia}
cacheWidth={this.props.cacheMediaWidth}
defaultWidth={this.props.cachedMediaWidth}
visible={quote ? this.state.showQuoteMedia : this.state.showMedia}
onToggleVisibility={quote ? this.handleToggleQuoteMediaVisibility : this.handleToggleMediaVisibility}
visible={this.state.showMedia}
onToggleVisibility={this.handleToggleMediaVisibility}
quote={quote}
/>
)}
@@ -645,8 +653,7 @@ class Status extends ImmutablePureComponent {
/>
);
}
return null;
return null;
};
const statusAvatar = (status, account) => {
@@ -685,6 +692,14 @@ class Status extends ImmutablePureComponent {
<RelativeTimestamp timestamp={status.get('created_at')} />{status.get('edited_at') && <abbr title={intl.formatMessage(messages.edited, { date: intl.formatDate(status.get('edited_at'), { hour12: false, year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) })}> *</abbr>}
</a>
{/* <a onClick={this.handleAccountClick} href={`/@${status.getIn(['account', 'acct'])}`} title={status.getIn(['account', 'acct'])} className='status__display-name' target='_blank' rel='noopener noreferrer'>
<div className='status__avatar'>
{statusAvatar}
</div>
<DisplayName account={status.get('account')} />
</a> */}
{identity(status, account, false)}
</div>
@@ -701,8 +716,8 @@ class Status extends ImmutablePureComponent {
{media(status)}
{quote(status, this.props.muted, quoteMuted, this.handleQuoteClick, this.handleExpandedQuoteToggle, identity, media, this.context.router, contextType)}
{quote(status, this.props.muted, quoteMuted, this.handleQuoteClick, this.handleExpandedQuoteToggle, identity, media, this.props, contextType)}
{expanded && hashtagBar}
<StatusActionBar scrollKey={scrollKey} status={status} account={account} onFilter={matchedFilters ? this.handleFilterClick : null} {...other} />

View File

@@ -17,6 +17,7 @@ import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
import QuoteIcon from '@material-symbols/400-24px/format_quote.svg?react';
import StarBorderIcon from '@/material-icons/400-24px/star.svg?react';
import VisibilityIcon from '@/material-icons/400-24px/visibility.svg?react';
import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react';

View File

@@ -40,6 +40,9 @@ const messages = defineMessages({
publish: { id: 'compose_form.publish', defaultMessage: 'Post' },
saveChanges: { id: 'compose_form.save_changes', defaultMessage: 'Update' },
reply: { id: 'compose_form.reply', defaultMessage: 'Reply' },
utilBtns_goji: { id: 'compose_form.utilBtns_goji', defaultMessage: 'Typo!!!' },
utilBtns_harukin: { id: 'compose_form.utilBtns_harukin', defaultMessage: 'Burn Harukin' },
utilBtns_risa: { id: 'compose_form.utilBtns_risa', defaultMessage: 'Risa' }
});
class ComposeForm extends ImmutablePureComponent {
@@ -72,6 +75,9 @@ class ComposeForm extends ImmutablePureComponent {
singleColumn: PropTypes.bool,
lang: PropTypes.string,
maxChars: PropTypes.number,
onGojiSubmit: PropTypes.func.isRequired,
onHarukinSubmit: PropTypes.func.isRequired,
onRisaSubmit: PropTypes.func.isRequired,
...WithOptionalRouterPropTypes
};
@@ -225,6 +231,10 @@ class ComposeForm extends ImmutablePureComponent {
this.props.onPickEmoji(position, data, needsSpace);
};
handleOnGojiSubmit = () => this.props.onGojiSubmit(this.textareaRef.current);
handleOnHarukinSubmit = () => this.props.onHarukinSubmit(this.textareaRef.current);
handleOnRisaSubmit = () => this.props.onRisaSubmit(this.textareaRef.current);
render () {
const { intl, onPaste, autoFocus, withoutNavigation, maxChars } = this.props;
const { highlighted } = this.state;
@@ -314,6 +324,10 @@ class ComposeForm extends ImmutablePureComponent {
</div>
</div>
</div>
<div className="compose-form__utilBtns">
<Button className="compose-form__utilBtns-goji" text={intl.formatMessage(messages.utilBtns_goji)} onClick={this.handleOnGojiSubmit} block />
<Button className="compose-form__utilBtns-harukin" text={intl.formatMessage(messages.utilBtns_harukin)} onClick={this.handleOnHarukinSubmit} block />
</div>
</form>
);
}

View File

@@ -45,4 +45,4 @@ export const ReplyIndicator = () => {
</div>
</div>
);
};
};

View File

@@ -10,6 +10,11 @@ import {
insertEmojiCompose,
uploadCompose,
} from '../../../actions/compose';
import {
submitGoji,
submitHarukin,
submitRisa
} from '../../../actions/UtilBtns';
import ComposeForm from '../components/compose_form';
const mapStateToProps = state => ({
@@ -28,7 +33,7 @@ const mapStateToProps = state => ({
anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
isInReply: state.getIn(['compose', 'in_reply_to']) !== null,
lang: state.getIn(['compose', 'language']),
maxChars: state.getIn(['server', 'server', 'configuration', 'statuses', 'max_characters'], 500),
maxChars: state.getIn(['server', 'server', 'configuration', 'statuses', 'max_characters'], 2048),
});
const mapDispatchToProps = (dispatch) => ({
@@ -65,6 +70,18 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(insertEmojiCompose(position, data, needsSpace));
},
onRisaSubmit (textarea) {
dispatch(submitRisa(textarea));
},
onGojiSubmit (textarea) {
dispatch(submitGoji(textarea));
},
onHarukinSubmit (textarea) {
dispatch(submitHarukin(textarea));
},
});
export default connect(mapStateToProps, mapDispatchToProps)(ComposeForm);

View File

@@ -0,0 +1,36 @@
import { connect } from 'react-redux';
import { cancelReplyCompose, cancelQuoteCompose } from '../../../actions/compose';
import { makeGetStatus } from '../../../selectors';
import ReplyIndicator from '../components/reply_indicator';
const makeMapStateToProps = () => {
const getStatus = makeGetStatus();
const mapStateToProps = (state, props) => {
let statusId = state.getIn(['compose', 'id'], null);
let editing = true;
if (statusId === null) {
statusId = state.getIn(['compose', props.quote ? 'quote_from' : 'in_reply_to']);
editing = false;
}
return {
status: getStatus(state, { id: statusId }),
quote: props.quote,
editing,
};
};
return mapStateToProps;
};
const mapDispatchToProps = dispatch => ({
onCancel (quote) {
dispatch(quote ? cancelQuoteCompose() : cancelReplyCompose());
},
});
export default connect(makeMapStateToProps, mapDispatchToProps)(ReplyIndicator);

View File

@@ -17,6 +17,7 @@ import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
import QuoteIcon from '@/material-icons/400-24px/format_quote.svg?react';
import StarBorderIcon from '@/material-icons/400-24px/star.svg?react';
import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react';
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react';
@@ -326,7 +327,6 @@ class ActionBar extends PureComponent {
<div className='detailed-status__button'><IconButton className='star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' iconComponent={status.get('favourited') ? StarIcon : StarBorderIcon} onClick={this.handleFavouriteClick} /></div>
<div className='detailed-status__button'><IconButton disabled={!publicStatus} title={StatusActionBar.quoteTitle(intl, messages, publicStatus)} icon='format-quote' iconComponent={FormatQuoteIcon} onClick={this.handleQuoteClick} /></div>
<div className='detailed-status__button'><IconButton className='bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' iconComponent={status.get('bookmarked') ? BookmarkIcon : BookmarkBorderIcon} onClick={this.handleBookmarkClick} /></div>
<div className='detailed-status__action-bar-dropdown'>
<DropdownMenuContainer icon='ellipsis-h' iconComponent={MoreHorizIcon} status={status} items={menu} direction='left' title={intl.formatMessage(messages.more)} />
</div>

View File

@@ -7,7 +7,6 @@ import { Link, withRouter } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
@@ -23,7 +22,7 @@ import { WithRouterPropTypes } from 'mastodon/utils/react_router';
import { Avatar } from '../../../components/avatar';
import { DisplayName } from '../../../components/display_name';
import MediaGallery from '../../../components/media_gallery';
import { mapStateToProps, quote } from '../../../components/status';
import { quote } from '../../../components/status';
import StatusContent from '../../../components/status_content';
import Audio from '../../audio';
import scheduleIdleTask from '../../ui/util/schedule_idle_task';
@@ -89,6 +88,15 @@ class DetailedStatus extends ImmutablePureComponent {
}
};
handleQuoteUserClick = () =>{
if (!this.props) {
return;
}
const { status } = this.props;
this.location.href(`/@${status.getIn(['account', 'acct'])}`);
}
_measureHeight (heightJustChanged) {
if (this.props.measureHeight && this.node) {
scheduleIdleTask(() => this.node && this.setState({ height: Math.ceil(this.node.scrollHeight) + 1 }));
@@ -254,9 +262,8 @@ class DetailedStatus extends ImmutablePureComponent {
card={status.get('card', null)} quote={quote} />);
}
return null;
};
}
if (status.get('application')) {
applicationLink = <> · <a className='detailed-status__application' href={status.getIn(['application', 'website'])} target='_blank' rel='noopener noreferrer'>{status.getIn(['application', 'name'])}</a></>;
}
@@ -344,7 +351,7 @@ class DetailedStatus extends ImmutablePureComponent {
{media(status, false)}
{quote(status, false, quoteMuted, this.handleQuoteClick, this.handleExpandedQuoteToggle, identity, media, this.context.router)}
{quote(status, false, quoteMuted, this.handleQuoteClick, this.handleExpandedQuoteToggle, identity, media, this.props)}
{expanded && hashtagBar}

View File

@@ -29,6 +29,13 @@ import ComposePanel from './compose_panel';
import DrawerLoading from './drawer_loading';
import NavigationPanel from './navigation_panel';
import { place_tab_bar_at_bottom } from 'mastodon/initial_state';
import { Link } from 'react-router-dom';
import { Icon } from 'mastodon/components/icon';
import TabsBar from './tabs_bar';
import { ReactComponent as EditIcon } from '@material-symbols/svg-600/outlined/edit.svg';
const componentMap = {
'COMPOSE': Compose,
'HOME': HomeTimeline,
@@ -149,13 +156,40 @@ export default class ColumnsArea extends ImmutablePureComponent {
const { renderComposePanel } = this.state;
if (singleColumn) {
return (
<div className='columns-area__panels'>
<div className='columns-area__panels__pane columns-area__panels__pane--compositional'>
<div className='columns-area__panels__pane__inner'>
{renderComposePanel && <ComposePanel />}
if (place_tab_bar_at_bottom) {
return (
<div className='columns-area__panels tab-ber-bottom'>
<div className='columns-area__panels__pane columns-area__panels__pane--compositional'>
<div className='columns-area__panels__pane__inner'>
{renderComposePanel && <ComposePanel />}
</div>
</div>
<div className='columns-area__panels__main timeline'>
<div className='tabs-bar__wrapper'><TabsBarPortal /></div>
<div className='columns-area columns-area--mobile'>{children}</div>
</div>
<div className='columns-area__panels__main navber'>
{location.pathname !== '/publish' && <Link to='/publish' icon='Edit' className='button bottom_right'><EditIcon fill="white"/></Link>}
<TabsBar key='tabs' />
</div>
<div className='columns-area__panels__pane columns-area__panels__pane--start columns-area__panels__pane--navigational columns-area__panels__pane-tab-ber'>
<div className='columns-area__panels__pane__inner'>
<NavigationPanel />
</div>
</div>
</div>
);
} else {
return (
<div className='columns-area__panels'>
<div className='columns-area__panels__pane columns-area__panels__pane--compositional'>
<div className='columns-area__panels__pane__inner'>
{renderComposePanel && <ComposePanel />}
</div>
</div>
<div className='columns-area__panels__main'>
<div className='tabs-bar__wrapper'><TabsBarPortal /></div>
@@ -168,7 +202,8 @@ export default class ColumnsArea extends ImmutablePureComponent {
</div>
</div>
</div>
);
);
}
}
return (

View File

@@ -0,0 +1,59 @@
import PropTypes from 'prop-types';
import { Component } from 'react';
import { defineMessages, injectIntl } from 'react-intl';
import ColumnLink from './column_link';
import NotificationsCounterIcon from './notifications_counter_icon';
import { ReactComponent as HomeIcon } from '@material-symbols/svg-600/outlined/home-fill.svg';
import { ReactComponent as MoreHorizIcon } from '@material-symbols/svg-600/outlined/more_horiz.svg';
import { ReactComponent as PublicIcon } from '@material-symbols/svg-600/outlined/public.svg';
import { ReactComponent as SearchIcon } from '@material-symbols/svg-600/outlined/search.svg';
const messages = defineMessages({
home: { id: 'tabs_bar.home', defaultMessage: 'Home' },
notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' },
explore: { id: 'explore.title', defaultMessage: 'Explore' },
firehose: { id: 'column.firehose', defaultMessage: 'Live feeds' },
direct: { id: 'navigation_bar.direct', defaultMessage: 'Private mentions' },
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
followsAndFollowers: { id: 'navigation_bar.follows_and_followers', defaultMessage: 'Follows and followers' },
about: { id: 'navigation_bar.about', defaultMessage: 'About' },
search: { id: 'navigation_bar.search', defaultMessage: 'Search' },
gettingStarted: { id: 'getting_started.heading', defaultMessage: 'Getting Started' },
});
class TabsBar extends Component {
static contextTypes = {
router: PropTypes.object.isRequired,
identity: PropTypes.object.isRequired,
};
static propTypes = {
intl: PropTypes.object.isRequired,
};
isFirehoseActive = (match, location) => {
return match || location.pathname.startsWith('/public');
};
render () {
const { intl } = this.props;
return (
<div className='tabs-bar__wrapper'>
<ColumnLink transparent to='/home' icon='home' iconComponent={HomeIcon} text={intl.formatMessage(messages.home)} />
<ColumnLink transparent to='/notifications' icon={<NotificationsCounterIcon className='column-link__icon' />} text={intl.formatMessage(messages.notifications)} />
<ColumnLink transparent to='/public/local' iconComponent={PublicIcon} isActive={this.isFirehoseActive} icon='globe' text={intl.formatMessage(messages.firehose)} />
<ColumnLink transparent to='/search' icon='search' iconComponent={SearchIcon} text={intl.formatMessage(messages.search)} />
<ColumnLink transparent to='/getting-started' icon='bars' iconComponent={MoreHorizIcon} text={intl.formatMessage(messages.gettingStarted)} />
</div>
);
}
}
export default injectIntl(TabsBar);

View File

@@ -438,7 +438,7 @@ class UI extends PureComponent {
handleHotkeyNew = e => {
e.preventDefault();
const element = this.node.querySelector('.compose-form__autosuggest-wrapper textarea');
const element = this.node.querySelector('.autosuggest-textarea__textarea');
if (element) {
element.focus();

View File

@@ -42,6 +42,7 @@
* @property {boolean} use_blurhash
* @property {boolean=} use_pending_items
* @property {string} version
* @property {boolean} place_tab_bar_at_bottom
* @property {string} sso_redirect
*/
@@ -107,6 +108,7 @@ export const languages = initialState?.languages;
export const criticalUpdatesPending = initialState?.critical_updates_pending;
// @ts-expect-error
export const statusPageUrl = getMeta('status_page_url');
export const place_tab_bar_at_bottom = getMeta('place_tab_bar_at_bottom');
export const sso_redirect = getMeta('sso_redirect');
export default initialState;

View File

@@ -23,13 +23,13 @@
"account.cancel_follow_request": "フォローリクエストの取り消し",
"account.copy": "プロフィールへのリンクをコピー",
"account.direct": "@{name}さんに非公開でメンション",
"account.disable_notifications": "@{name}さんの投稿時の通知を停止",
"account.disable_notifications": "@{name}さんのトゥート時の通知を停止",
"account.domain_blocked": "ドメインブロック中",
"account.edit_profile": "プロフィール編集",
"account.enable_notifications": "@{name}さんの投稿時に通知",
"account.enable_notifications": "@{name}さんのトゥート時に通知",
"account.endorse": "プロフィールで紹介する",
"account.featured_tags.last_status_at": "最終投稿 {date}",
"account.featured_tags.last_status_never": "投稿がありません",
"account.featured_tags.last_status_at": "最終トゥート {date}",
"account.featured_tags.last_status_never": "トゥートがありません",
"account.featured_tags.title": "{name}の注目ハッシュタグ",
"account.follow": "フォロー",
"account.follow_back": "フォローバック",
@@ -56,14 +56,14 @@
"account.mutual": "相互フォロー中",
"account.no_bio": "説明が提供されていません。",
"account.open_original_page": "元のページを開く",
"account.posts": "投稿",
"account.posts_with_replies": "投稿と返信",
"account.posts": "トゥート",
"account.posts_with_replies": "トゥートと返信",
"account.report": "@{name}さんを通報",
"account.requested": "フォロー承認待ちです。クリックしてキャンセル",
"account.requested_follow": "{name}さんがあなたにフォローリクエストしました",
"account.share": "@{name}さんのプロフィールを共有する",
"account.show_reblogs": "@{name}さんからのブーストを表示",
"account.statuses_counter": "{counter} 投稿",
"account.statuses_counter": "{counter} トゥート",
"account.unblock": "@{name}さんのブロックを解除",
"account.unblock_domain": "{domain}のブロックを解除",
"account.unblock_short": "ブロック解除",
@@ -121,7 +121,7 @@
"column.lists": "リスト",
"column.mutes": "ミュートしたユーザー",
"column.notifications": "通知",
"column.pins": "固定された投稿",
"column.pins": "固定されたトゥート",
"column.public": "連合タイムライン",
"column_back_button.label": "戻る",
"column_header.hide_settings": "設定を隠す",
@@ -136,21 +136,31 @@
"community.column_settings.remote_only": "リモートのみ表示",
"compose.language.change": "言語を変更",
"compose.language.search": "言語を検索...",
"compose.published.body": "投稿されました!",
"compose.published.body": "トゥートされました!",
"compose.published.open": "開く",
"compose.saved.body": "変更を保存しました。",
"compose_form.direct_message_warning_learn_more": "もっと詳しく",
"compose_form.encryption_warning": "Mastodonの投稿はエンドツーエンド暗号化に対応していません。安全に送受信されるべき情報をMastodonで共有しないでください。",
"compose_form.hashtag_warning": "この投稿は公開設定ではないのでハッシュタグの一覧に表示されません。公開投稿だけがハッシュタグで検索できます。",
"compose_form.lock_disclaimer": "あなたのアカウントは{locked}になっていません。誰でもあなたをフォローすることができ、フォロワー限定の投稿を見ることができます。",
"compose_form.encryption_warning": "Mastodonのトゥートはエンドツーエンド暗号化に対応していません。安全に送受信されるべき情報をMastodonで共有しないでください。",
"compose_form.hashtag_warning": "このトゥートは公開設定ではないのでハッシュタグの一覧に表示されません。公開トゥートだけがハッシュタグで検索できます。",
"compose_form.lock_disclaimer": "あなたのアカウントは{locked}になっていません。誰でもあなたをフォローすることができ、フォロワー限定のトゥートを見ることができます。",
"compose_form.lock_disclaimer.lock": "承認制",
"compose_form.placeholder": "今なにしてる?",
"compose_form.poll.duration": "アンケート期間",
"compose_form.poll.switch_to_multiple": "複数選択に変更",
"compose_form.poll.switch_to_single": "単一選択に変更",
"compose_form.publish_form": "投稿",
"compose_form.publish": "トゥート",
"compose_form.publish_form": "トゥート",
"compose_form.publish_loud": "{publish}",
"compose_form.save_changes": "変更を保存",
"compose_form.sensitive.hide": "メディアを閲覧注意にする",
"compose_form.sensitive.marked": "メディアに閲覧注意が設定されています",
"compose_form.sensitive.unmarked": "メディアに閲覧注意が設定されていません",
"compose_form.spoiler.marked": "本文は警告の後ろに隠されます",
"compose_form.spoiler.unmarked": "本文は隠されていません",
"compose_form.spoiler_placeholder": "ここに警告を書いてください",
"compose_form.utilBtns_goji": "誤字盛!",
"compose_form.utilBtns_harukin": "はるきん焼却",
"compose_form.utilBtns_risa": "りさ姉",
"confirmation_modal.cancel": "キャンセル",
"confirmations.block.block_and_report": "ブロックし通報",
"confirmations.block.confirm": "ブロック",
@@ -170,12 +180,12 @@
"confirmations.logout.confirm": "ログアウト",
"confirmations.logout.message": "本当にログアウトしますか?",
"confirmations.mute.confirm": "ミュート",
"confirmations.mute.explanation": "これにより相手の投稿と返信は見えなくなりますが、相手はあなたをフォローし続け投稿を見ることができます。",
"confirmations.mute.explanation": "これにより相手のトゥートと返信は見えなくなりますが、相手はあなたをフォローし続けトゥートを見ることができます。",
"confirmations.mute.message": "本当に{name}さんをミュートしますか?",
"confirmations.quote.confirm": "引用",
"confirmations.quote.message": "今引用すると現在作成中のメッセージが上書きされます。本当に実行しますか?",
"confirmations.redraft.confirm": "削除して下書きに戻す",
"confirmations.redraft.message": "投稿を削除して下書きに戻します。この投稿へのお気に入り登録やブーストは失われ、返信は孤立することになります。よろしいですか?",
"confirmations.redraft.message": "トゥートを削除して下書きに戻します。このトゥートへのお気に入り登録やブーストは失われ、返信は孤立することになります。よろしいですか?",
"confirmations.reply.confirm": "返信",
"confirmations.reply.message": "今返信すると現在作成中のメッセージが上書きされます。本当に実行しますか?",
"confirmations.unfollow.confirm": "フォロー解除",
@@ -193,12 +203,12 @@
"directory.recently_active": "最近の活動順",
"disabled_account_banner.account_settings": "アカウント設定",
"disabled_account_banner.text": "あなたのアカウント『{disabledAccount}』は現在無効になっています。",
"dismissable_banner.community_timeline": "これらは{domain}がホストしている人たちの最新の公開投稿です。",
"dismissable_banner.community_timeline": "これらは{domain}がホストしている人たちの最新の公開トゥートです。",
"dismissable_banner.dismiss": "閉じる",
"dismissable_banner.explore_links": "ネットワーク上で話題になっているニュースです。たくさんのユーザーにシェアされた記事ほど上位に表示されます。",
"dismissable_banner.explore_statuses": "ネットワーク上で注目を集めている投稿です。ブーストやお気に入り登録の多い新しい投稿が上位に表示されます。",
"dismissable_banner.explore_statuses": "ネットワーク上で注目を集めているトゥートです。ブーストやお気に入り登録の多い新しいトゥートが上位に表示されます。",
"dismissable_banner.explore_tags": "ネットワーク上でトレンドになっているハッシュタグです。たくさんのユーザーに使われたタグほど上位に表示されます。",
"dismissable_banner.public_timeline": "{domain}のユーザーがリモートフォローしているアカウントからの公開投稿のタイムラインです。",
"dismissable_banner.public_timeline": "{domain}のユーザーがリモートフォローしているアカウントからの公開トゥートのタイムラインです。",
"embed.instructions": "下記のコードをコピーしてウェブサイトに埋め込みます。",
"embed.preview": "表示例:",
"emoji_button.activity": "活動",
@@ -218,7 +228,7 @@
"emoji_button.travel": "旅行と場所",
"empty_column.account_hides_collections": "このユーザーはこの情報を開示しないことにしています。",
"empty_column.account_suspended": "アカウントは停止されています",
"empty_column.account_timeline": "投稿がありません!",
"empty_column.account_timeline": "トゥートがありません!",
"empty_column.account_unavailable": "プロフィールは利用できません",
"empty_column.blocks": "まだ誰もブロックしていません。",
"empty_column.bookmarked_statuses": "まだ何もブックマーク登録していません。ブックマーク登録するとここに表示されます。",
@@ -226,17 +236,17 @@
"empty_column.direct": "非公開の返信はまだありません。非公開でやりとりをするとここに表示されます。",
"empty_column.domain_blocks": "ブロックしているドメインはありません。",
"empty_column.explore_statuses": "まだ何もありません。後で確認してください。",
"empty_column.favourited_statuses": "お気に入りの投稿はまだありません。お気に入りに登録すると、ここに表示されます。",
"empty_column.favourites": "まだ誰もこの投稿をお気に入りに登録していません。お気に入りに登録されると、ここに表示されます。",
"empty_column.favourited_statuses": "お気に入りのトゥートはまだありません。お気に入りに登録すると、ここに表示されます。",
"empty_column.favourites": "まだ誰もこのトゥートをお気に入りに登録していません。お気に入りに登録されると、ここに表示されます。",
"empty_column.follow_requests": "まだフォローリクエストを受けていません。フォローリクエストを受けるとここに表示されます。",
"empty_column.followed_tags": "まだハッシュタグをフォローしていません。フォローするとここに表示されます。",
"empty_column.hashtag": "このハッシュタグはまだ使われていません。",
"empty_column.home": "ホームタイムラインはまだ空っぽです。だれかをフォローして埋めてみましょう。",
"empty_column.list": "このリストにはまだなにもありません。このリストのメンバーが新しい投稿をするとここに表示されます。",
"empty_column.list": "このリストにはまだなにもありません。このリストのメンバーが新しいトゥートをするとここに表示されます。",
"empty_column.lists": "まだリストがありません。リストを作るとここに表示されます。",
"empty_column.mutes": "まだ誰もミュートしていません。",
"empty_column.notifications": "まだ通知がありません。他の人とふれ合って会話を始めましょう。",
"empty_column.public": "ここにはまだ何もありません! 公開で何かを投稿したり、他のサーバーのユーザーをフォローしたりしていっぱいにしましょう",
"empty_column.public": "ここにはまだ何もありません! 公開で何かをトゥートしたり、他のサーバーのユーザーをフォローしたりしていっぱいにしましょう",
"error.unexpected_crash.explanation": "不具合かブラウザの互換性問題のため、このページを正しく表示できませんでした。",
"error.unexpected_crash.explanation_addons": "このページは正しく表示できませんでした。このエラーはブラウザのアドオンや自動翻訳ツールによって引き起こされることがあります。",
"error.unexpected_crash.next_steps": "ページの再読み込みをお試しください。それでも解決しない場合、別のブラウザかアプリを使えば使用できることがあります。",
@@ -247,24 +257,24 @@
"explore.suggested_follows": "ユーザー",
"explore.title": "探索する",
"explore.trending_links": "ニュース",
"explore.trending_statuses": "投稿",
"explore.trending_statuses": "トゥート",
"explore.trending_tags": "ハッシュタグ",
"filter_modal.added.context_mismatch_explanation": "このフィルターカテゴリーはあなたがアクセスした投稿のコンテキストには適用されません。この投稿のコンテキストでもフィルターを適用するにはフィルターを編集する必要があります。",
"filter_modal.added.context_mismatch_explanation": "このフィルターカテゴリーはあなたがアクセスしたトゥートのコンテキストには適用されません。このトゥートのコンテキストでもフィルターを適用するにはフィルターを編集する必要があります。",
"filter_modal.added.context_mismatch_title": "コンテキストが一致しません!",
"filter_modal.added.expired_explanation": "このフィルターカテゴリーは有効期限が切れています。適用するには有効期限を更新してください。",
"filter_modal.added.expired_title": "フィルターの有効期限が切れています!",
"filter_modal.added.review_and_configure": "このフィルターカテゴリーを確認して設定するには、{settings_link}に移動します。",
"filter_modal.added.review_and_configure_title": "フィルター設定",
"filter_modal.added.settings_link": "設定",
"filter_modal.added.short_explanation": "この投稿はフィルターカテゴリー『{title}』に追加されました。",
"filter_modal.added.short_explanation": "このトゥートはフィルターカテゴリー『{title}』に追加されました。",
"filter_modal.added.title": "フィルターを追加しました!",
"filter_modal.select_filter.context_mismatch": "このコンテキストには当てはまりません",
"filter_modal.select_filter.expired": "期限切れ",
"filter_modal.select_filter.prompt_new": "新しいカテゴリー: {name}",
"filter_modal.select_filter.search": "検索または新規作成",
"filter_modal.select_filter.subtitle": "既存のカテゴリーを使用するか新規作成します",
"filter_modal.select_filter.title": "この投稿をフィルターする",
"filter_modal.title.status": "投稿をフィルターする",
"filter_modal.select_filter.title": "このトゥートをフィルターする",
"filter_modal.title.status": "トゥートをフィルターする",
"firehose.all": "すべて",
"firehose.local": "このサーバー",
"firehose.remote": "ほかのサーバー",
@@ -291,7 +301,7 @@
"hashtag.column_settings.tag_mode.any": "いずれかを含む",
"hashtag.column_settings.tag_mode.none": "これらを除く",
"hashtag.column_settings.tag_toggle": "このカラムに追加のタグを含める",
"hashtag.counter_by_accounts": "{count, plural, other {{counter}人投稿}}",
"hashtag.counter_by_accounts": "{count, plural, other {{counter}人トゥート}}",
"hashtag.counter_by_uses": "{count, plural, other {{counter}件}}",
"hashtag.counter_by_uses_today": "今日{count, plural, other {{counter}件}}",
"hashtag.follow": "ハッシュタグをフォローする",
@@ -302,17 +312,17 @@
"home.column_settings.basic": "基本設定",
"home.column_settings.show_reblogs": "ブースト表示",
"home.column_settings.show_replies": "返信表示",
"home.explore_prompt.body": "ユーザーやハッシュタグをフォローすると、この「ホーム」タイムラインに投稿やブーストが流れるようになります。タイムラインをもう少しにぎやかにしてみませんか?",
"home.explore_prompt.body": "ユーザーやハッシュタグをフォローすると、この「ホーム」タイムラインにトゥートやブーストが流れるようになります。タイムラインをもう少しにぎやかにしてみませんか?",
"home.explore_prompt.title": "Mastodonのタイムラインへようこそ。",
"home.hide_announcements": "お知らせを隠す",
"home.pending_critical_update.body": "速やかにMastodonサーバーをアップデートしてください。",
"home.pending_critical_update.link": "詳細",
"home.pending_critical_update.title": "緊急のセキュリティアップデートがあります",
"home.show_announcements": "お知らせを表示",
"interaction_modal.description.favourite": "Mastodonのアカウントがあれば投稿をお気に入り登録して投稿者に気持ちを伝えたり、あとで見返すことができます。",
"interaction_modal.description.follow": "Mastodonのアカウントで{name}さんをフォローしてホームフィードで投稿を受け取れます。",
"interaction_modal.description.reblog": "Mastodonのアカウントでこの投稿をブーストして自分のフォロワーに共有できます。",
"interaction_modal.description.reply": "Mastodonのアカウントでこの投稿に反応できます。",
"interaction_modal.description.favourite": "Mastodonのアカウントがあればトゥートをお気に入り登録してトゥート者に気持ちを伝えたり、あとで見返すことができます。",
"interaction_modal.description.follow": "Mastodonのアカウントで{name}さんをフォローしてホームフィードでトゥートを受け取れます。",
"interaction_modal.description.reblog": "Mastodonのアカウントでこのトゥートをブーストして自分のフォロワーに共有できます。",
"interaction_modal.description.reply": "Mastodonのアカウントでこのトゥートに反応できます。",
"interaction_modal.login.action": "サーバーに移動",
"interaction_modal.login.prompt": "登録したサーバーのドメイン (例: mastodon.social)",
"interaction_modal.no_account_yet": "Mastodonにアカウントがない場合は",
@@ -320,10 +330,10 @@
"interaction_modal.on_this_server": "このサーバー",
"interaction_modal.sign_in": "このサーバーにアカウントがなくても、ほかのサーバーや互換性のあるプラットフォームのアカウントを使用できます。",
"interaction_modal.sign_in_hint": "ワンポイント: ここでは自分のアカウントのドメインを入力します。うまくいかない場合はドメインまで含めた完全なユーザー名を入力してみてください (例: @Mastodon@mastodon.social)。ドメインやユーザー名は登録完了時のメールに記載されています。",
"interaction_modal.title.favourite": "{name}さんの投稿をお気に入り登録",
"interaction_modal.title.favourite": "{name}さんのトゥートをお気に入り登録",
"interaction_modal.title.follow": "{name}さんをフォロー",
"interaction_modal.title.reblog": "{name}さんの投稿をブースト",
"interaction_modal.title.reply": "{name}さんの投稿にリプライ",
"interaction_modal.title.reblog": "{name}さんのトゥートをブースト",
"interaction_modal.title.reply": "{name}さんのトゥートにリプライ",
"intervals.full.days": "{number}日",
"intervals.full.hours": "{number}時間",
"intervals.full.minutes": "{number}分",
@@ -331,12 +341,12 @@
"keyboard_shortcuts.blocked": "ブロックしたユーザーのリストを開く",
"keyboard_shortcuts.boost": "ブースト",
"keyboard_shortcuts.column": "左からn番目のカラムの最新に移動",
"keyboard_shortcuts.compose": "投稿の入力欄に移動",
"keyboard_shortcuts.compose": "トゥートの入力欄に移動",
"keyboard_shortcuts.description": "説明",
"keyboard_shortcuts.direct": "非公開の返信のカラムを開く",
"keyboard_shortcuts.down": "カラム内一つ下に移動",
"keyboard_shortcuts.enter": "投稿の詳細を表示",
"keyboard_shortcuts.favourite": "お気に入りの投稿",
"keyboard_shortcuts.enter": "トゥートの詳細を表示",
"keyboard_shortcuts.favourite": "お気に入りのトゥート",
"keyboard_shortcuts.favourites": "お気に入りリストを開く",
"keyboard_shortcuts.federated": "連合タイムラインを開く",
"keyboard_shortcuts.heading": "キーボードショートカット",
@@ -349,7 +359,7 @@
"keyboard_shortcuts.my_profile": "自分のプロフィールを開く",
"keyboard_shortcuts.notifications": "通知カラムを開く",
"keyboard_shortcuts.open_media": "メディアを開く",
"keyboard_shortcuts.pinned": "固定した投稿のリストを開く",
"keyboard_shortcuts.pinned": "固定したトゥートのリストを開く",
"keyboard_shortcuts.profile": "プロフィールを開く",
"keyboard_shortcuts.reply": "返信",
"keyboard_shortcuts.requests": "フォローリクエストのリストを開く",
@@ -358,8 +368,8 @@
"keyboard_shortcuts.start": "\"スタート\" カラムを開く",
"keyboard_shortcuts.toggle_hidden": "CWで隠れた文を見る/隠す",
"keyboard_shortcuts.toggle_sensitivity": "非表示のメディアを見る/隠す",
"keyboard_shortcuts.toot": "新規投稿",
"keyboard_shortcuts.unfocus": "投稿の入力欄・検索欄から離れる",
"keyboard_shortcuts.toot": "新規トゥート",
"keyboard_shortcuts.unfocus": "トゥートの入力欄・検索欄から離れる",
"keyboard_shortcuts.up": "カラム内一つ上に移動",
"lightbox.close": "閉じる",
"lightbox.compress": "画像ビューボックスを閉じる",
@@ -374,7 +384,7 @@
"lists.delete": "リストを削除",
"lists.edit": "リストを編集",
"lists.edit.submit": "タイトルを変更",
"lists.exclusive": "ホームタイムラインからこれらの投稿を非表示にする",
"lists.exclusive": "ホームタイムラインからこれらのトゥートを非表示にする",
"lists.new.create": "リストを作成",
"lists.new.title_placeholder": "新規リスト名",
"lists.replies_policy.followed": "フォロー中のユーザー全員",
@@ -395,7 +405,7 @@
"navigation_bar.blocks": "ブロックしたユーザー",
"navigation_bar.bookmarks": "ブックマーク",
"navigation_bar.community_timeline": "ローカルタイムライン",
"navigation_bar.compose": "投稿の新規作成",
"navigation_bar.compose": "トゥートの新規作成",
"navigation_bar.direct": "非公開の返信",
"navigation_bar.discover": "見つける",
"navigation_bar.domain_blocks": "ブロックしたドメイン",
@@ -410,7 +420,7 @@
"navigation_bar.mutes": "ミュートしたユーザー",
"navigation_bar.opened_in_classic_interface": "投稿やプロフィールを直接開いた場合は一時的に標準UIで表示されます。",
"navigation_bar.personal": "個人用",
"navigation_bar.pins": "固定した投稿",
"navigation_bar.pins": "固定したトゥート",
"navigation_bar.preferences": "ユーザー設定",
"navigation_bar.public_timeline": "連合タイムライン",
"navigation_bar.search": "検索",
@@ -418,15 +428,15 @@
"not_signed_in_indicator.not_signed_in": "この機能を使うにはログインする必要があります。",
"notification.admin.report": "{name}さんが{target}さんを通報しました",
"notification.admin.sign_up": "{name}さんがサインアップしました",
"notification.favourite": "{name}さんがお気に入りしました",
"notification.favourite": "{name}さんがあなたのトゥートに╰( ^o^)╮-=ニ=一=三★しました",
"notification.follow": "{name}さんにフォローされました",
"notification.follow_request": "{name}さんがあなたにフォローリクエストしました",
"notification.mention": "{name}さんがあなたに返信しました",
"notification.own_poll": "アンケートが終了しました",
"notification.poll": "アンケートが終了しました",
"notification.reblog": "{name}さんがあなたの投稿をブーストしました",
"notification.status": "{name}さんが投稿しました",
"notification.update": "{name}さんが投稿を編集しました",
"notification.reblog": "{name}さんがあなたのトゥートをブーストしました",
"notification.status": "{name}さんがトゥートしました",
"notification.update": "{name}さんがトゥートを編集しました",
"notifications.clear": "通知を消去",
"notifications.clear_confirmation": "本当に通知を消去しますか?",
"notifications.column_settings.admin.report": "新しい通報:",
@@ -444,7 +454,7 @@
"notifications.column_settings.reblog": "ブースト:",
"notifications.column_settings.show": "カラムに表示",
"notifications.column_settings.sound": "通知音を再生",
"notifications.column_settings.status": "新しい投稿:",
"notifications.column_settings.status": "新しいトゥート:",
"notifications.column_settings.unread_notifications.category": "未読の通知:",
"notifications.column_settings.unread_notifications.highlight": "未読の通知を強調表示",
"notifications.column_settings.update": "編集:",
@@ -493,7 +503,7 @@
"onboarding.steps.follow_people.body": "ユーザーをフォローしてみましょう。これがMastodonを楽しむ基本です。",
"onboarding.steps.follow_people.title": "ホームタイムラインを埋める",
"onboarding.steps.publish_status.body": "試しになにか書いてみましょう。写真、ビデオ、アンケートなど、なんでも大丈夫です {emoji}",
"onboarding.steps.publish_status.title": "はじめての投稿",
"onboarding.steps.publish_status.title": "はじめてのトゥート",
"onboarding.steps.setup_profile.body": "ほかのユーザーが親しみやすいように、プロフィールを整えましょう。",
"onboarding.steps.setup_profile.title": "プロフィールを完成させる",
"onboarding.steps.share_profile.body": "Mastodonのアカウントをほかの人に紹介しましょう。",
@@ -536,7 +546,7 @@
"relative_time.today": "今日",
"reply_indicator.cancel": "キャンセル",
"report.block": "ブロック",
"report.block_explanation": "相手の投稿が表示されなくなります。相手はあなたの投稿を見ることやフォローすることができません。相手はブロックされていることがわかります。",
"report.block_explanation": "相手のトゥートが表示されなくなります。相手はあなたのトゥートを見ることやフォローすることができません。相手はブロックされていることがわかります。",
"report.categories.legal": "法令違反",
"report.categories.other": "その他",
"report.categories.spam": "スパム",
@@ -544,13 +554,13 @@
"report.category.subtitle": "近いものを選択してください",
"report.category.title": "この{type}について教えてください",
"report.category.title_account": "プロフィール",
"report.category.title_status": "投稿",
"report.category.title_status": "トゥート",
"report.close": "完了",
"report.comment.title": "その他に私たちに伝えておくべき事はありますか?",
"report.forward": "{target}に転送する",
"report.forward_hint": "このアカウントは別のサーバーに所属しています。通報内容を匿名で転送しますか?",
"report.mute": "ミュート",
"report.mute_explanation": "相手の投稿は表示されなくなります。相手は引き続きあなたをフォローして、あなたの投稿を表示することができますが、ミュートされていることはわかりません。",
"report.mute_explanation": "相手のトゥートは表示されなくなります。相手は引き続きあなたをフォローして、あなたのトゥートを表示することができますが、ミュートされていることはわかりません。",
"report.next": "次へ",
"report.placeholder": "追加コメント",
"report.reasons.dislike": "興味がありません",
@@ -566,7 +576,7 @@
"report.rules.subtitle": "当てはまるものをすべて選んでください:",
"report.rules.title": "どのルールに違反していますか?",
"report.statuses.subtitle": "当てはまるものをすべて選んでください:",
"report.statuses.title": "この通報を裏付けるような投稿はありますか?",
"report.statuses.title": "この通報を裏付けるようなトゥートはありますか?",
"report.submit": "通報する",
"report.target": "{target}さんを通報する",
"report.thanks.take_action": "次のような方法はいかがでしょうか?",
@@ -574,8 +584,8 @@
"report.thanks.title": "見えないようにしたいですか?",
"report.thanks.title_actionable": "ご報告ありがとうございます、追って確認します。",
"report.unfollow": "@{name}さんのフォローを解除",
"report.unfollow_explanation": "このアカウントをフォローしています。ホームフィードに彼らの投稿を表示しないようにするには、彼らのフォローを外してください。",
"report_notification.attached_statuses": "{count, plural, one {{count}件の投稿} other {{count}件の投稿}}が添付されました。",
"report.unfollow_explanation": "このアカウントをフォローしています。ホームフィードに彼らのトゥートを表示しないようにするには、彼らのフォローを外してください。",
"report_notification.attached_statuses": "{count, plural, one {{count}件のトゥート} other {{count}件のトゥート}}が添付されました。",
"report_notification.categories.legal": "法令違反",
"report_notification.categories.other": "その他",
"report_notification.categories.spam": "スパム",
@@ -587,7 +597,7 @@
"search.quick_action.go_to_account": "プロフィール {x} を見る",
"search.quick_action.go_to_hashtag": "ハッシュタグ {x} を見る",
"search.quick_action.open_url": "MastodonでURLを開く",
"search.quick_action.status_search": "{x}に該当する投稿",
"search.quick_action.status_search": "{x}に該当するトゥート",
"search.search_or_paste": "検索またはURLを入力",
"search_popout.full_text_search_disabled_message": "{domain}では利用できません。",
"search_popout.full_text_search_logged_out_message": "ログイン時のみ利用できます。",
@@ -602,7 +612,7 @@
"search_results.hashtags": "ハッシュタグ",
"search_results.nothing_found": "この検索条件では何も見つかりませんでした",
"search_results.see_all": "すべて表示",
"search_results.statuses": "投稿",
"search_results.statuses": "トゥート",
"search_results.title": "『{q}』の検索結果",
"server_banner.about_active_users": "過去30日間にこのサーバーを使用している人 (月間アクティブユーザー)",
"server_banner.active_users": "人のアクティブユーザー",
@@ -613,42 +623,42 @@
"sign_in_banner.create_account": "アカウント作成",
"sign_in_banner.sign_in": "ログイン",
"sign_in_banner.sso_redirect": "ログインまたは登録",
"sign_in_banner.text": "アカウントがあればユーザーやハッシュタグをフォローしたり、投稿のお気に入り登録やブースト、投稿への返信ができます。別のサーバーのユーザーとの交流も可能です。",
"sign_in_banner.text": "アカウントがあればユーザーやハッシュタグをフォローしたり、トゥートのお気に入り登録やブースト、トゥートへの返信ができます。別のサーバーのユーザーとの交流も可能です。",
"status.admin_account": "@{name}さんのモデレーション画面を開く",
"status.admin_domain": "{domain}のモデレーション画面を開く",
"status.admin_status": "この投稿をモデレーション画面で開く",
"status.admin_status": "このトゥートをモデレーション画面で開く",
"status.block": "@{name}さんをブロック",
"status.bookmark": "ブックマーク",
"status.cancel_reblog_private": "ブースト解除",
"status.cannot_quote": "この投稿は引用できません",
"status.cannot_reblog": "この投稿はブーストできません",
"status.copy": "投稿へのリンクをコピー",
"status.cannot_quote": "このトゥートは引用できません",
"status.cannot_reblog": "このトゥートはブーストできません",
"status.copy": "トゥートへのリンクをコピー",
"status.delete": "削除",
"status.detailed_status": "詳細な会話ビュー",
"status.direct": "@{name}さんに非公開で投稿",
"status.direct": "@{name}さんに非公開でトゥート",
"status.direct_indicator": "非公開の返信",
"status.edit": "編集",
"status.edited": "{date}に編集",
"status.edited_x_times": "{count}回編集",
"status.embed": "埋め込み",
"status.favourite": "お気に入り",
"status.filter": "この投稿をフィルターする",
"status.filter": "このトゥートをフィルターする",
"status.filtered": "フィルターされました",
"status.hide": "投稿を非表示",
"status.hide": "トゥートを非表示",
"status.history.created": "{name}さんが{date}に作成",
"status.history.edited": "{name}さんが{date}に編集",
"status.load_more": "もっと見る",
"status.media.open": "クリックして開く",
"status.media.show": "クリックして表示",
"status.media_hidden": "非表示のメディア",
"status.mention": "@{name}さんに投稿",
"status.mention": "@{name}さんにトゥート",
"status.more": "もっと見る",
"status.mute": "@{name}さんをミュート",
"status.mute_conversation": "会話をミュート",
"status.muted_quote": "ミュートされた引用",
"status.open": "詳細を表示",
"status.pin": "プロフィールに固定表示",
"status.pinned": "固定された投稿",
"status.pinned": "固定されたトゥート",
"status.quote": "引用",
"status.read_more": "もっと見る",
"status.reblog": "ブースト",
@@ -669,14 +679,14 @@
"status.show_more": "もっと見る",
"status.show_more_all": "全て見る",
"status.show_original": "原文を表示",
"status.title.with_attachments": "{user}さんの投稿 {attachmentCount, plural, other {({attachmentCount}件のメディア)}}",
"status.title.with_attachments": "{user}さんのトゥート {attachmentCount, plural, other {({attachmentCount}件のメディア)}}",
"status.translate": "翻訳",
"status.translated_from_with": "{provider}を使って{lang}から翻訳",
"status.uncached_media_warning": "プレビューは使用できません",
"status.unlisted_quote": "未収載の引用",
"status.unmute_conversation": "会話のミュートを解除",
"status.unpin": "プロフィールへの固定を解除",
"subscribed_languages.lead": "選択した言語の投稿だけがホームとリストのタイムラインに表示されます。全ての言語の投稿を受け取る場合は全てのチェックを外して下さい。",
"subscribed_languages.lead": "選択した言語のトゥートだけがホームとリストのタイムラインに表示されます。全ての言語のトゥートを受け取る場合は全てのチェックを外して下さい。",
"subscribed_languages.save": "変更を保存",
"subscribed_languages.target": "{target}さんの購読言語を変更します",
"tabs_bar.home": "ホーム",
@@ -689,10 +699,10 @@
"timeline_hint.remote_resource_not_displayed": "他のサーバーの{resource}は表示されません。",
"timeline_hint.resources.followers": "フォロワー",
"timeline_hint.resources.follows": "フォロー",
"timeline_hint.resources.statuses": "以前の投稿",
"timeline_hint.resources.statuses": "以前のトゥート",
"trends.counter_by_accounts": "過去{days, plural, one {{days}日} other {{days}日}}に{count, plural, one {{counter}人} other {{counter} 人}}",
"trends.trending_now": "トレンドタグ",
"ui.beforeunload": "Mastodonから離れると送信前の投稿は失われます。",
"ui.beforeunload": "Mastodonから離れると送信前のトゥートは失われます。",
"units.short.billion": "{count}B",
"units.short.million": "{count}M",
"units.short.thousand": "{count}K",

View File

@@ -4,6 +4,7 @@ import { List as ImmutableList, Map as ImmutableMap } from 'immutable';
import { toServerSideType } from 'mastodon/utils/filters';
import { me } from '../initial_state';
import {reblogRequest} from '../actions/interactions';
export { makeGetAccount } from "./accounts";

View File

@@ -191,7 +191,7 @@ function loaded() {
spoilerLink.textContent = (new IntlMessageFormat(message, locale)).format();
});
Rails.delegate(document, '.quote-status', 'click', ({ target }) => {
delegate(document, '.quote-status', 'click', ({ target }) => {
if (target.closest('.status__content__spoiler-link') ||
target.closest('.media-gallery') ||
target.closest('.video-player') ||

View File

@@ -23,3 +23,5 @@
@import 'mastodon/rtl';
@import 'mastodon/accessibility';
@import 'mastodon/rich_text';
@import 'plugin';

View File

@@ -0,0 +1,3 @@
@import 'light-pink/variables';
@import 'application';
@import 'mastodon-light/diff';

View File

@@ -0,0 +1,61 @@
// Dependent colors
$black: #000000;
$white: #ffffff;
$classic-base-color: #6e202f;
$classic-primary-color: #ffa7ae;
$classic-secondary-color: #faeef1;
$classic-highlight-color: #ff375b;
$blurple-600: #6e202f; // Iris
$blurple-500: #ff375b; // Brand purple
$blurple-300: #ffa7ae; // Faded Blue
$grey-600: #5a4c4f; // Trout
$grey-100: #f3dae1; // Topaz
// Differences
$success-green: lighten(#3c754d, 8%);
$base-overlay-background: $white !default;
$valid-value-color: $success-green !default;
$ui-base-color: $classic-secondary-color !default;
$ui-base-lighter-color: #ffe1e9;
$ui-primary-color: #f1adbf;
$ui-secondary-color: $classic-base-color !default;
$ui-highlight-color: $classic-highlight-color !default;
$ui-button-secondary-color: $grey-600 !default;
$ui-button-secondary-border-color: $grey-600 !default;
$ui-button-secondary-focus-color: $white !default;
$ui-button-tertiary-color: $blurple-500 !default;
$ui-button-tertiary-border-color: $blurple-500 !default;
$ui-button-color: $white !default;
$ui-button-background-color: $blurple-500 !default;
$ui-button-focus-background-color: $blurple-600 !default;
$primary-text-color: $black !default;
$darker-text-color: $classic-base-color !default;
$highlight-text-color: darken($ui-highlight-color, 8%) !default;
$dark-text-color: #6e202f;
$action-button-color: #ffa7ae;
$inverted-text-color: $black !default;
$lighter-text-color: $classic-base-color !default;
$light-text-color: #ffa7ae;
// Newly added colors
$account-background-color: $white !default;
// Invert darkened and lightened colors
@function darken($color, $amount) {
@return hsl(hue($color), saturation($color), lightness($color) + $amount);
}
@function lighten($color, $amount) {
@return hsl(hue($color), saturation($color), lightness($color) - $amount);
}
$emojis-requiring-inversion: 'chains';

View File

@@ -875,6 +875,29 @@ body > [data-popper-placement] {
background: $ui-highlight-color;
border-color: $ui-highlight-color;
color: $primary-text-color;
padding-top: 10px;
.compose-form__publish-button-wrapper {
padding-top: 15px;
button {
display: inline-block;
width: auto;
margin-right: 0.5em;
}
button:last-child {
margin-right: auto;
}
}
}
.compose-form__utilBtns {
padding-top: 10px;
* {
margin-bottom: 0.5em;
}
}
}
@@ -935,6 +958,45 @@ body > [data-popper-placement] {
}
}
.reply-indicator {
border-radius: 4px;
margin-bottom: 10px;
background: $ui-primary-color;
padding: 10px;
min-height: 23px;
overflow-y: auto;
flex: 0 2 auto;
&.quote-indicator {
background: $success-green;
}
}
.reply-indicator__header {
margin-bottom: 5px;
overflow: hidden;
}
.reply-indicator__cancel {
float: right;
line-height: 24px;
}
.reply-indicator__display-name {
color: $inverted-text-color;
display: block;
max-width: 100%;
line-height: 24px;
overflow: hidden;
padding-inline-end: 25px;
text-decoration: none;
}
.reply-indicator__display-avatar {
float: left;
margin-inline-end: 5px;
}
.status__content--with-action {
cursor: pointer;
}
@@ -1370,7 +1432,8 @@ body > [data-popper-placement] {
strong,
span {
display: inline;
display: block;
line-height: 22px;
}
}

View File

@@ -0,0 +1,60 @@
// ここから下タブバーの実装
//投稿ボタン
.columns-area__panels__main .button.bottom_right {
position: fixed;
right: 18px;
bottom: 65px;
display: flex;
align-items: center;
justify-content: center;
width: 64px;
height: 64px;
font-size: 24px;
border-radius: 50%;
}
.tab-ber-bottom .navber {
display: none;
}
.column-link--transparent{
justify-content: center;
}
@media screen and (max-width: 630px) {
.tab-ber-bottom .timeline{
width:100%;
margin: 0 0 50px 0;
}
.tab-ber-bottom .navber{
display: flex;
width:100%;
bottom: 0;
position: fixed;
}
.tab-ber-bottom .navber .tabs-bar__wrapper {
bottom: 0;
width: 100%;
align-self: auto;
display: flex;
border-top: 1px solid lighten($ui-base-color, 8%);
}
.tab-ber-bottom .navber .tabs-bar__wrapper a{
flex: 1;
text-align: center;
}
.columns-area__panels__pane-tab-ber{
display: none;
}
.tab-ber-bottom .navber .tabs-bar__wrapper a span {
display: none;
}
}
//ここまで下タブバーの実装

Submodule app/javascript/styles/y-zu-dark added at a58c699a60

Submodule app/javascript/styles/y-zu-light added at a5c2283144

View File

@@ -443,7 +443,6 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
def quote_from_url(url)
return nil if url.nil?
quote = ResolveURLService.new.call(url)
status_from_uri(quote.uri) if quote
rescue

View File

@@ -14,7 +14,7 @@ module ActivityPub::CaseTransform
when String
camel_lower_cache[value] ||= if value.start_with?('_:')
"_:#{value.delete_prefix('_:').underscore.camelize(:lower)}"
elsif value.start_with?('_') || LanguagesHelper::ISO_639_1_REGIONAL.key?(value.to_sym)
elsif LanguagesHelper::ISO_639_1_REGIONAL.key?(value.to_sym)
value
else
value.underscore.camelize(:lower)

View File

@@ -141,7 +141,7 @@ class TextFormatter
def render_quote
link = link_to_url({ url: ap_tag_manager.url_for(quote) })
<<~HTML.squish
<span class="quote-inline"><br/>~~~~~~~~~~<br/>[#{link}]</span>
<span class="quote-inline"><br>RE: #{link}</span>
HTML
end

View File

@@ -20,4 +20,10 @@ class ApplicationMailer < ActionMailer::Base
headers['X-Auto-Response-Suppress'] = 'All'
headers['Auto-Submitted'] = 'auto-generated'
end
def set_autoreply_headers!
headers['Precedence'] = 'list'
headers['X-Auto-Response-Suppress'] = 'All'
headers['Auto-Submitted'] = 'auto-generated'
end
end

View File

@@ -95,6 +95,10 @@ module User::HasSettings
settings['web.disable_swiping']
end
def setting_place_tab_bar_at_bottom
settings['web.place_tab_bar_at_bottom']
end
def setting_always_send_emails
settings['always_send_emails']
end

View File

@@ -63,7 +63,7 @@ class Status < ApplicationRecord
with_options class_name: 'Status', optional: true do
belongs_to :thread, foreign_key: 'in_reply_to_id', inverse_of: :replies
belongs_to :reblog, foreign_key: 'reblog_of_id', inverse_of: :reblogs
belongs_to :quote, inverse_of: :quoted
belongs_to :quote, class_name: 'Status', inverse_of: :quoted, optional: true
end
has_many :favourites, inverse_of: :status, dependent: :destroy

View File

@@ -32,6 +32,7 @@ class UserSettings
setting :expand_content_warnings, default: false
setting :display_media, default: 'default', in: %w(default show_all hide_all)
setting :auto_play, default: false
setting :place_tab_bar_at_bottom, default: false
end
namespace :notification_emails do

View File

@@ -92,6 +92,14 @@ class Webhook < ApplicationRecord
end
end
def validate_permissions
errors.add(:events, :invalid_permissions) if defined?(@current_account) && required_permissions.any? { |permission| !@current_account.user_role.can?(permission) }
end
def strip_events
self.events = events.filter_map { |str| str.strip.presence } if events.present?
end
def generate_secret
self.secret = SecureRandom.hex(20) if secret.blank?
end

View File

@@ -29,6 +29,7 @@ class InitialStateSerializer < ActiveModel::Serializer
store[:use_blurhash] = object_account_user.setting_use_blurhash
store[:use_pending_items] = object_account_user.setting_use_pending_items
store[:show_trends] = Setting.trends && object_account_user.setting_trends
store[:place_tab_bar_at_bottom] = object.current_account.user.setting_place_tab_bar_at_bottom
else
store[:auto_play_gif] = Setting.auto_play_gif
store[:display_media] = Setting.display_media

View File

@@ -1,7 +1,7 @@
# frozen_string_literal: true
class StatusLengthValidator < ActiveModel::Validator
MAX_CHARS = 500
MAX_CHARS = 2048
URL_PLACEHOLDER_CHARS = 23
URL_PLACEHOLDER = 'x' * 23

View File

@@ -2,7 +2,7 @@
%html{ lang: I18n.locale }
%head
%meta{ charset: 'utf-8' }/
%meta{ name: 'viewport', content: 'width=device-width, initial-scale=1' }/
%meta{ name: 'viewport', content: 'width=device-width, initial-scale=1, viewport-fit=cover'}/
- if cdn_host?
%link{ rel: 'dns-prefetch', href: cdn_host }/

View File

@@ -57,6 +57,7 @@
= ff.input :'web.reduce_motion', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_reduce_motion')
= ff.input :'web.disable_swiping', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_disable_swiping')
= ff.input :'web.use_system_font', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_system_font_ui')
= ff.input :'web.place_tab_bar_at_bottom', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_place_tab_bar_at_bottom')
%h4= t 'appearance.discovery'

View File

@@ -14,6 +14,14 @@
= render 'statuses/author', author: status.account
.status__content.emojify{ data: ({ spoiler: current_account&.user&.setting_expand_spoilers ? 'expanded' : 'folded' } if status.spoiler_text?) }<
- if status.spoiler_text?
%p<
%span.p-summary> #{prerender_custom_emojis(h(status.spoiler_text), status.emojis)}&nbsp;
%button.status__content__spoiler-link= t('statuses.show_more')
.e-content{ lang: status.language }
= prerender_custom_emojis(status_content_format(status), status.emojis)
= render 'statuses/text', status: status
- if status.quote?