Add account conversations column
This commit is contained in:
parent
ccafeaf32f
commit
4b3f4be472
13 changed files with 258 additions and 0 deletions
80
app/controllers/api/v1/accounts/conversations_controller.rb
Normal file
80
app/controllers/api/v1/accounts/conversations_controller.rb
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::V1::Accounts::ConversationsController < Api::BaseController
|
||||||
|
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }
|
||||||
|
before_action :require_user!
|
||||||
|
before_action :set_account
|
||||||
|
|
||||||
|
after_action :insert_pagination_headers
|
||||||
|
|
||||||
|
def index
|
||||||
|
@statuses = load_statuses
|
||||||
|
|
||||||
|
if compact?
|
||||||
|
render json: CompactStatusesPresenter.new(statuses: @statuses), serializer: REST::CompactStatusesSerializer
|
||||||
|
else
|
||||||
|
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(account_ids, current_user&.account_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_account
|
||||||
|
@account = Account.find(params[:account_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_statuses
|
||||||
|
@account.suspended? ? [] : cached_account_statuses
|
||||||
|
end
|
||||||
|
|
||||||
|
def cached_account_statuses
|
||||||
|
cache_collection_paginated_by_id(
|
||||||
|
conversation_account_statuses,
|
||||||
|
Status,
|
||||||
|
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||||
|
params_slice(:max_id, :since_id, :min_id)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def conversation_account_statuses
|
||||||
|
@account.conversation_statuses(current_account)
|
||||||
|
end
|
||||||
|
|
||||||
|
def compact?
|
||||||
|
truthy_param?(:compact)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_params(core_params)
|
||||||
|
params.slice(:limit, :compact).permit(:limit, :compact).merge(core_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def insert_pagination_headers
|
||||||
|
set_pagination_headers(next_path, prev_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def next_path
|
||||||
|
if records_continue?
|
||||||
|
api_v1_account_statuses_url pagination_params(max_id: pagination_max_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def prev_path
|
||||||
|
unless @statuses.empty?
|
||||||
|
api_v1_account_statuses_url pagination_params(min_id: pagination_since_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def records_continue?
|
||||||
|
@statuses.size == limit_param(DEFAULT_STATUSES_LIMIT)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_max_id
|
||||||
|
@statuses.last.id
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_since_id
|
||||||
|
@statuses.first.id
|
||||||
|
end
|
||||||
|
end
|
|
@ -183,6 +183,7 @@ export const expandPublicTimeline = ({ maxId, onlyMedia, withoutMedia,
|
||||||
export const expandDomainTimeline = (domain, { maxId, onlyMedia, withoutMedia, withoutBot } = {}, done = noOp) => expandTimeline(`domain${withoutBot ? ':nobot' : ':bot'}${withoutMedia ? ':nomedia' : ''}${onlyMedia ? ':media' : ''}:${domain}`, '/api/v1/timelines/public', { local: false, domain: domain, max_id: maxId, only_media: !!onlyMedia, without_media: !!withoutMedia, without_bot: !!withoutBot }, done);
|
export const expandDomainTimeline = (domain, { maxId, onlyMedia, withoutMedia, withoutBot } = {}, done = noOp) => expandTimeline(`domain${withoutBot ? ':nobot' : ':bot'}${withoutMedia ? ':nomedia' : ''}${onlyMedia ? ':media' : ''}:${domain}`, '/api/v1/timelines/public', { local: false, domain: domain, max_id: maxId, only_media: !!onlyMedia, without_media: !!withoutMedia, without_bot: !!withoutBot }, done);
|
||||||
export const expandGroupTimeline = (id, { maxId, onlyMedia, withoutMedia, tagged } = {}, done = noOp) => expandTimeline(`group:${id}${withoutMedia ? ':nomedia' : ''}${onlyMedia ? ':media' : ''}${tagged ? `:${tagged}` : ''}`, `/api/v1/timelines/group/${id}`, { max_id: maxId, only_media: !!onlyMedia, without_media: !!withoutMedia, tagged: tagged }, done);
|
export const expandGroupTimeline = (id, { maxId, onlyMedia, withoutMedia, tagged } = {}, done = noOp) => expandTimeline(`group:${id}${withoutMedia ? ':nomedia' : ''}${onlyMedia ? ':media' : ''}${tagged ? `:${tagged}` : ''}`, `/api/v1/timelines/group/${id}`, { max_id: maxId, only_media: !!onlyMedia, without_media: !!withoutMedia, tagged: tagged }, done);
|
||||||
export const expandAccountTimeline = (accountId, { maxId, withReplies } = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies, max_id: maxId });
|
export const expandAccountTimeline = (accountId, { maxId, withReplies } = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies, max_id: maxId });
|
||||||
|
export const expandAccountCoversations = (accountId, { maxId } = {}) => expandTimeline(`account:${accountId}:conversations`, `/api/v1/accounts/${accountId}/conversations`, { max_id: maxId });
|
||||||
export const expandAccountFeaturedTimeline = accountId => expandTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true });
|
export const expandAccountFeaturedTimeline = accountId => expandTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true });
|
||||||
export const expandAccountMediaTimeline = (accountId, { maxId } = {}) => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId, only_media: true, limit: 40 });
|
export const expandAccountMediaTimeline = (accountId, { maxId } = {}) => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId, only_media: true, limit: 40 });
|
||||||
export const expandListTimeline = (id, { maxId } = {}, done = noOp) => expandTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`, { max_id: maxId }, done);
|
export const expandListTimeline = (id, { maxId } = {}, done = noOp) => expandTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`, { max_id: maxId }, done);
|
||||||
|
|
|
@ -27,6 +27,8 @@ const messages = defineMessages({
|
||||||
edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
|
edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
|
||||||
linkVerifiedOn: { id: 'account.link_verified_on', defaultMessage: 'Ownership of this link was checked on {date}' },
|
linkVerifiedOn: { id: 'account.link_verified_on', defaultMessage: 'Ownership of this link was checked on {date}' },
|
||||||
account_locked: { id: 'account.locked_info', defaultMessage: 'This account privacy status is set to locked. The owner manually reviews who can follow them.' },
|
account_locked: { id: 'account.locked_info', defaultMessage: 'This account privacy status is set to locked. The owner manually reviews who can follow them.' },
|
||||||
|
conversations: { id: 'account.conversations', defaultMessage: 'Show conversations with @{name}' },
|
||||||
|
conversations_all: { id: 'account.conversations_all', defaultMessage: 'Show all conversations' },
|
||||||
mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' },
|
mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' },
|
||||||
direct: { id: 'account.direct', defaultMessage: 'Direct message @{name}' },
|
direct: { id: 'account.direct', defaultMessage: 'Direct message @{name}' },
|
||||||
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
|
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
|
||||||
|
@ -79,6 +81,7 @@ class Header extends ImmutablePureComponent {
|
||||||
onBlock: PropTypes.func.isRequired,
|
onBlock: PropTypes.func.isRequired,
|
||||||
onMention: PropTypes.func.isRequired,
|
onMention: PropTypes.func.isRequired,
|
||||||
onDirect: PropTypes.func.isRequired,
|
onDirect: PropTypes.func.isRequired,
|
||||||
|
onConversations: PropTypes.func.isRequired,
|
||||||
onReblogToggle: PropTypes.func.isRequired,
|
onReblogToggle: PropTypes.func.isRequired,
|
||||||
onNotifyToggle: PropTypes.func.isRequired,
|
onNotifyToggle: PropTypes.func.isRequired,
|
||||||
onReport: PropTypes.func.isRequired,
|
onReport: PropTypes.func.isRequired,
|
||||||
|
@ -207,7 +210,12 @@ class Header extends ImmutablePureComponent {
|
||||||
menu.push({ text: intl.formatMessage(messages.mention, { name: account.get('username') }), action: this.props.onMention });
|
menu.push({ text: intl.formatMessage(messages.mention, { name: account.get('username') }), action: this.props.onMention });
|
||||||
menu.push({ text: intl.formatMessage(messages.direct, { name: account.get('username') }), action: this.props.onDirect });
|
menu.push({ text: intl.formatMessage(messages.direct, { name: account.get('username') }), action: this.props.onDirect });
|
||||||
menu.push(null);
|
menu.push(null);
|
||||||
|
|
||||||
|
menu.push({ text: intl.formatMessage(messages.conversations, { name: account.get('username') }), action: this.props.onConversations });
|
||||||
|
} else {
|
||||||
|
menu.push({ text: intl.formatMessage(messages.conversations_all), action: this.props.onConversations });
|
||||||
}
|
}
|
||||||
|
menu.push(null);
|
||||||
|
|
||||||
if ('share' in navigator) {
|
if ('share' in navigator) {
|
||||||
menu.push({ text: intl.formatMessage(messages.share, { name: account.get('username') }), action: this.handleShare });
|
menu.push({ text: intl.formatMessage(messages.share, { name: account.get('username') }), action: this.handleShare });
|
||||||
|
|
139
app/javascript/mastodon/features/account_conversations/index.js
Normal file
139
app/javascript/mastodon/features/account_conversations/index.js
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { fetchAccount } from '../../actions/accounts';
|
||||||
|
import { expandAccountCoversations } from '../../actions/timelines';
|
||||||
|
import StatusList from '../../components/status_list';
|
||||||
|
import LoadingIndicator from '../../components/loading_indicator';
|
||||||
|
import Column from '../ui/components/column';
|
||||||
|
import HeaderContainer from '../account_timeline/containers/header_container';
|
||||||
|
import ColumnBackButton from '../../components/column_back_button';
|
||||||
|
import { List as ImmutableList } from 'immutable';
|
||||||
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
import { fetchAccountIdentityProofs } from '../../actions/identity_proofs';
|
||||||
|
import MissingIndicator from 'mastodon/components/missing_indicator';
|
||||||
|
import TimelineHint from 'mastodon/components/timeline_hint';
|
||||||
|
|
||||||
|
const emptyList = ImmutableList();
|
||||||
|
|
||||||
|
const mapStateToProps = (state, { params: { accountId } }) => {
|
||||||
|
return {
|
||||||
|
remote: !!(state.getIn(['accounts', accountId, 'acct']) !== state.getIn(['accounts', accountId, 'username'])),
|
||||||
|
remoteUrl: state.getIn(['accounts', accountId, 'url']),
|
||||||
|
isAccount: !!state.getIn(['accounts', accountId]),
|
||||||
|
statusIds: state.getIn(['timelines', `account:${accountId}:conversations`, 'items'], emptyList),
|
||||||
|
isLoading: state.getIn(['timelines', `account:${accountId}:conversations`, 'isLoading']),
|
||||||
|
hasMore: state.getIn(['timelines', `account:${accountId}:conversations`, 'hasMore']),
|
||||||
|
suspended: state.getIn(['accounts', accountId, 'suspended'], false),
|
||||||
|
blockedBy: state.getIn(['relationships', accountId, 'blocked_by'], false),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const RemoteHint = ({ url }) => (
|
||||||
|
<TimelineHint url={url} resource={<FormattedMessage id='timeline_hint.resources.statuses' defaultMessage='Older toots' />} />
|
||||||
|
);
|
||||||
|
|
||||||
|
RemoteHint.propTypes = {
|
||||||
|
url: PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default @connect(mapStateToProps)
|
||||||
|
class AccountConversations extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
params: PropTypes.object.isRequired,
|
||||||
|
dispatch: PropTypes.func.isRequired,
|
||||||
|
statusIds: ImmutablePropTypes.list,
|
||||||
|
isLoading: PropTypes.bool,
|
||||||
|
hasMore: PropTypes.bool,
|
||||||
|
withReplies: PropTypes.bool,
|
||||||
|
blockedBy: PropTypes.bool,
|
||||||
|
isAccount: PropTypes.bool,
|
||||||
|
suspended: PropTypes.bool,
|
||||||
|
remote: PropTypes.bool,
|
||||||
|
remoteUrl: PropTypes.string,
|
||||||
|
multiColumn: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
componentWillMount () {
|
||||||
|
const { params: { accountId }, dispatch } = this.props;
|
||||||
|
|
||||||
|
dispatch(fetchAccount(accountId));
|
||||||
|
dispatch(fetchAccountIdentityProofs(accountId));
|
||||||
|
|
||||||
|
dispatch(expandAccountCoversations(accountId));
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps (nextProps) {
|
||||||
|
const { dispatch } = this.props;
|
||||||
|
|
||||||
|
if (nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) {
|
||||||
|
dispatch(fetchAccount(nextProps.params.accountId));
|
||||||
|
dispatch(fetchAccountIdentityProofs(nextProps.params.accountId));
|
||||||
|
|
||||||
|
dispatch(expandAccountCoversations(nextProps.params.accountId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleLoadMore = maxId => {
|
||||||
|
this.props.dispatch(expandAccountCoversations(this.props.params.accountId, { maxId }));
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { statusIds, isLoading, hasMore, blockedBy, suspended, isAccount, multiColumn, remote, remoteUrl } = this.props;
|
||||||
|
|
||||||
|
if (!isAccount) {
|
||||||
|
return (
|
||||||
|
<Column>
|
||||||
|
<ColumnBackButton multiColumn={multiColumn} />
|
||||||
|
<MissingIndicator />
|
||||||
|
</Column>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!statusIds && isLoading) {
|
||||||
|
return (
|
||||||
|
<Column>
|
||||||
|
<LoadingIndicator />
|
||||||
|
</Column>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let emptyMessage;
|
||||||
|
|
||||||
|
if (suspended) {
|
||||||
|
emptyMessage = <FormattedMessage id='empty_column.account_suspended' defaultMessage='Account suspended' />;
|
||||||
|
} else if (blockedBy) {
|
||||||
|
emptyMessage = <FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' />;
|
||||||
|
} else if (remote && statusIds.isEmpty()) {
|
||||||
|
emptyMessage = <RemoteHint url={remoteUrl} />;
|
||||||
|
} else {
|
||||||
|
emptyMessage = <FormattedMessage id='empty_column.account_timeline' defaultMessage='No toots here!' />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const remoteMessage = remote ? <RemoteHint url={remoteUrl} /> : null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Column>
|
||||||
|
<ColumnBackButton multiColumn={multiColumn} />
|
||||||
|
|
||||||
|
<StatusList
|
||||||
|
prepend={<HeaderContainer accountId={this.props.params.accountId} />}
|
||||||
|
alwaysPrepend
|
||||||
|
append={remoteMessage}
|
||||||
|
scrollKey='account_conversations'
|
||||||
|
statusIds={(suspended || blockedBy) ? emptyList : statusIds}
|
||||||
|
isLoading={isLoading}
|
||||||
|
hasMore={hasMore}
|
||||||
|
onLoadMore={this.handleLoadMore}
|
||||||
|
emptyMessage={emptyMessage}
|
||||||
|
bindToDocument={!multiColumn}
|
||||||
|
timelineId='account_conversations'
|
||||||
|
/>
|
||||||
|
</Column>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ export default class Header extends ImmutablePureComponent {
|
||||||
onBlock: PropTypes.func.isRequired,
|
onBlock: PropTypes.func.isRequired,
|
||||||
onMention: PropTypes.func.isRequired,
|
onMention: PropTypes.func.isRequired,
|
||||||
onDirect: PropTypes.func.isRequired,
|
onDirect: PropTypes.func.isRequired,
|
||||||
|
onConversations: PropTypes.func.isRequired,
|
||||||
onReblogToggle: PropTypes.func.isRequired,
|
onReblogToggle: PropTypes.func.isRequired,
|
||||||
onReport: PropTypes.func.isRequired,
|
onReport: PropTypes.func.isRequired,
|
||||||
onMute: PropTypes.func.isRequired,
|
onMute: PropTypes.func.isRequired,
|
||||||
|
@ -63,6 +64,10 @@ export default class Header extends ImmutablePureComponent {
|
||||||
this.props.onDirect(this.props.account, this.context.router.history);
|
this.props.onDirect(this.props.account, this.context.router.history);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleConversations = () => {
|
||||||
|
this.props.onConversations(this.props.account, this.context.router.history);
|
||||||
|
}
|
||||||
|
|
||||||
handleReport = () => {
|
handleReport = () => {
|
||||||
this.props.onReport(this.props.account);
|
this.props.onReport(this.props.account);
|
||||||
}
|
}
|
||||||
|
@ -130,6 +135,7 @@ export default class Header extends ImmutablePureComponent {
|
||||||
onBlock={this.handleBlock}
|
onBlock={this.handleBlock}
|
||||||
onMention={this.handleMention}
|
onMention={this.handleMention}
|
||||||
onDirect={this.handleDirect}
|
onDirect={this.handleDirect}
|
||||||
|
onConversations={this.handleConversations}
|
||||||
onReblogToggle={this.handleReblogToggle}
|
onReblogToggle={this.handleReblogToggle}
|
||||||
onNotifyToggle={this.handleNotifyToggle}
|
onNotifyToggle={this.handleNotifyToggle}
|
||||||
onReport={this.handleReport}
|
onReport={this.handleReport}
|
||||||
|
|
|
@ -99,6 +99,10 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||||
dispatch(directCompose(account, router));
|
dispatch(directCompose(account, router));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onConversations (account, router) {
|
||||||
|
router.push(`/accounts/${account.get('id')}/conversations`);
|
||||||
|
},
|
||||||
|
|
||||||
onReblogToggle (account) {
|
onReblogToggle (account) {
|
||||||
if (account.getIn(['relationship', 'showing_reblogs'])) {
|
if (account.getIn(['relationship', 'showing_reblogs'])) {
|
||||||
dispatch(followAccount(account.get('id'), { reblogs: false }));
|
dispatch(followAccount(account.get('id'), { reblogs: false }));
|
||||||
|
|
|
@ -34,6 +34,7 @@ import {
|
||||||
GroupTimeline,
|
GroupTimeline,
|
||||||
AccountTimeline,
|
AccountTimeline,
|
||||||
AccountGallery,
|
AccountGallery,
|
||||||
|
AccountConversations,
|
||||||
HomeTimeline,
|
HomeTimeline,
|
||||||
Followers,
|
Followers,
|
||||||
Following,
|
Following,
|
||||||
|
@ -204,6 +205,7 @@ class SwitchingColumnsArea extends React.PureComponent {
|
||||||
<WrappedRoute path='/accounts/:accountId/following' component={Following} content={children} />
|
<WrappedRoute path='/accounts/:accountId/following' component={Following} content={children} />
|
||||||
<WrappedRoute path='/accounts/:accountId/subscribing' component={Subscribing} content={children} />
|
<WrappedRoute path='/accounts/:accountId/subscribing' component={Subscribing} content={children} />
|
||||||
<WrappedRoute path='/accounts/:accountId/media' component={AccountGallery} content={children} />
|
<WrappedRoute path='/accounts/:accountId/media' component={AccountGallery} content={children} />
|
||||||
|
<WrappedRoute path='/accounts/:accountId/conversations' component={AccountConversations} content={children} />
|
||||||
|
|
||||||
<WrappedRoute path='/follow_requests' component={FollowRequests} content={children} />
|
<WrappedRoute path='/follow_requests' component={FollowRequests} content={children} />
|
||||||
<WrappedRoute path='/blocks' component={Blocks} content={children} />
|
<WrappedRoute path='/blocks' component={Blocks} content={children} />
|
||||||
|
|
|
@ -74,6 +74,10 @@ export function AccountGallery () {
|
||||||
return import(/* webpackChunkName: "features/account_gallery" */'../../account_gallery');
|
return import(/* webpackChunkName: "features/account_gallery" */'../../account_gallery');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function AccountConversations () {
|
||||||
|
return import(/* webpackChunkName: "features/account_conversations" */'../../account_conversations');
|
||||||
|
}
|
||||||
|
|
||||||
export function Followers () {
|
export function Followers () {
|
||||||
return import(/* webpackChunkName: "features/followers" */'../../followers');
|
return import(/* webpackChunkName: "features/followers" */'../../followers');
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
"account.blocked": "Blocked",
|
"account.blocked": "Blocked",
|
||||||
"account.browse_more_on_origin_server": "Browse more on the original profile",
|
"account.browse_more_on_origin_server": "Browse more on the original profile",
|
||||||
"account.cancel_follow_request": "Cancel follow request",
|
"account.cancel_follow_request": "Cancel follow request",
|
||||||
|
"account.conversations": "Show conversations with @{name}",
|
||||||
|
"account.conversations_all": "Show all conversations",
|
||||||
"account.direct": "Direct message @{name}",
|
"account.direct": "Direct message @{name}",
|
||||||
"account.disable_notifications": "Stop notifying me when @{name} posts",
|
"account.disable_notifications": "Stop notifying me when @{name} posts",
|
||||||
"account.domain_blocked": "Domain blocked",
|
"account.domain_blocked": "Domain blocked",
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
"account.blocked": "ブロック済み",
|
"account.blocked": "ブロック済み",
|
||||||
"account.browse_more_on_origin_server": "リモートで表示",
|
"account.browse_more_on_origin_server": "リモートで表示",
|
||||||
"account.cancel_follow_request": "フォローリクエストを取り消す",
|
"account.cancel_follow_request": "フォローリクエストを取り消す",
|
||||||
|
"account.conversations": "@{name}さんとの会話を表示",
|
||||||
|
"account.conversations_all": "すべての会話を表示",
|
||||||
"account.direct": "@{name}さんにダイレクトメッセージ",
|
"account.direct": "@{name}さんにダイレクトメッセージ",
|
||||||
"account.disable_notifications": "@{name} の投稿時の通知を停止",
|
"account.disable_notifications": "@{name} の投稿時の通知を停止",
|
||||||
"account.domain_blocked": "ドメインブロック中",
|
"account.domain_blocked": "ドメインブロック中",
|
||||||
|
|
|
@ -425,6 +425,14 @@ class Account < ApplicationRecord
|
||||||
end.permitted_for(self, account)
|
end.permitted_for(self, account)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def conversation_statuses(account)
|
||||||
|
if account.nil? || account.class.name == 'Account' && account.id == id
|
||||||
|
Status.unscoped.recent.union_all(Status.include_expired.joins(:mentions).where(account_id: id, mentions: {silent: false})).union_all(Status.include_expired.joins(:mentions).where(mentions: {account_id: id, silent: false}))
|
||||||
|
else
|
||||||
|
Status.unscoped.recent.union_all(Status.include_expired.joins(:mentions).where(account_id: id, mentions: {account_id: account.id, silent: false})).union_all(Status.include_expired.joins(:mentions).where(account_id: account.id, mentions: {account_id: id, silent: false}))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def index_text
|
def index_text
|
||||||
ActionController::Base.helpers.strip_tags(note)
|
ActionController::Base.helpers.strip_tags(note)
|
||||||
end
|
end
|
||||||
|
|
|
@ -136,6 +136,7 @@ class REST::InstanceSerializer < ActiveModel::Serializer
|
||||||
:status_reference,
|
:status_reference,
|
||||||
:searchability,
|
:searchability,
|
||||||
:status_compact_mode,
|
:status_compact_mode,
|
||||||
|
:account_conversations,
|
||||||
]
|
]
|
||||||
|
|
||||||
capabilities << :profile_search unless Chewy.enabled?
|
capabilities << :profile_search unless Chewy.enabled?
|
||||||
|
|
|
@ -479,6 +479,7 @@ Rails.application.routes.draw do
|
||||||
resources :circles, only: :index, controller: 'accounts/circles'
|
resources :circles, only: :index, controller: 'accounts/circles'
|
||||||
resources :identity_proofs, only: :index, controller: 'accounts/identity_proofs'
|
resources :identity_proofs, only: :index, controller: 'accounts/identity_proofs'
|
||||||
resources :featured_tags, only: :index, controller: 'accounts/featured_tags'
|
resources :featured_tags, only: :index, controller: 'accounts/featured_tags'
|
||||||
|
resources :conversations, only: :index, controller: 'accounts/conversations'
|
||||||
|
|
||||||
member do
|
member do
|
||||||
post :follow
|
post :follow
|
||||||
|
|
Loading…
Reference in a new issue