mirror of
https://framagit.org/tykayn/mastodon.git
synced 2023-08-25 08:33:12 +02:00
parent
c5157ef07b
commit
9caa90025f
@ -7,11 +7,14 @@ class AccountsController < ApplicationController
|
|||||||
def show
|
def show
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html do
|
format.html do
|
||||||
|
@pinned_statuses = []
|
||||||
|
|
||||||
if current_account && @account.blocking?(current_account)
|
if current_account && @account.blocking?(current_account)
|
||||||
@statuses = []
|
@statuses = []
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@pinned_statuses = cache_collection(@account.pinned_statuses.limit(1), Status) unless media_requested?
|
||||||
@statuses = filtered_statuses.paginate_by_max_id(20, params[:max_id], params[:since_id])
|
@statuses = filtered_statuses.paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||||
@statuses = cache_collection(@statuses, Status)
|
@statuses = cache_collection(@statuses, Status)
|
||||||
@next_url = next_url unless @statuses.empty?
|
@next_url = next_url unless @statuses.empty?
|
||||||
@ -32,8 +35,8 @@ class AccountsController < ApplicationController
|
|||||||
|
|
||||||
def filtered_statuses
|
def filtered_statuses
|
||||||
default_statuses.tap do |statuses|
|
default_statuses.tap do |statuses|
|
||||||
statuses.merge!(only_media_scope) if request.path.ends_with?('/media')
|
statuses.merge!(only_media_scope) if media_requested?
|
||||||
statuses.merge!(no_replies_scope) unless request.path.ends_with?('/with_replies')
|
statuses.merge!(no_replies_scope) unless replies_requested?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -58,12 +61,20 @@ class AccountsController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def next_url
|
def next_url
|
||||||
if request.path.ends_with?('/media')
|
if media_requested?
|
||||||
short_account_media_url(@account, max_id: @statuses.last.id)
|
short_account_media_url(@account, max_id: @statuses.last.id)
|
||||||
elsif request.path.ends_with?('/with_replies')
|
elsif replies_requested?
|
||||||
short_account_with_replies_url(@account, max_id: @statuses.last.id)
|
short_account_with_replies_url(@account, max_id: @statuses.last.id)
|
||||||
else
|
else
|
||||||
short_account_url(@account, max_id: @statuses.last.id)
|
short_account_url(@account, max_id: @statuses.last.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def media_requested?
|
||||||
|
request.path.ends_with?('/media')
|
||||||
|
end
|
||||||
|
|
||||||
|
def replies_requested?
|
||||||
|
request.path.ends_with?('/with_replies')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -29,6 +29,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
|||||||
def account_statuses
|
def account_statuses
|
||||||
default_statuses.tap do |statuses|
|
default_statuses.tap do |statuses|
|
||||||
statuses.merge!(only_media_scope) if params[:only_media]
|
statuses.merge!(only_media_scope) if params[:only_media]
|
||||||
|
statuses.merge!(pinned_scope) if params[:pinned]
|
||||||
statuses.merge!(no_replies_scope) if params[:exclude_replies]
|
statuses.merge!(no_replies_scope) if params[:exclude_replies]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -53,6 +54,10 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
|||||||
@account.media_attachments.attached.reorder(nil).select(:status_id).distinct
|
@account.media_attachments.attached.reorder(nil).select(:status_id).distinct
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pinned_scope
|
||||||
|
@account.pinned_statuses
|
||||||
|
end
|
||||||
|
|
||||||
def no_replies_scope
|
def no_replies_scope
|
||||||
Status.without_replies
|
Status.without_replies
|
||||||
end
|
end
|
||||||
|
28
app/controllers/api/v1/statuses/pins_controller.rb
Normal file
28
app/controllers/api/v1/statuses/pins_controller.rb
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::V1::Statuses::PinsController < Api::BaseController
|
||||||
|
include Authorization
|
||||||
|
|
||||||
|
before_action -> { doorkeeper_authorize! :write }
|
||||||
|
before_action :require_user!
|
||||||
|
before_action :set_status
|
||||||
|
|
||||||
|
respond_to :json
|
||||||
|
|
||||||
|
def create
|
||||||
|
StatusPin.create!(account: current_account, status: @status)
|
||||||
|
render json: @status, serializer: REST::StatusSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
pin = StatusPin.find_by(account: current_account, status: @status)
|
||||||
|
pin&.destroy!
|
||||||
|
render json: @status, serializer: REST::StatusSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_status
|
||||||
|
@status = Status.find(params[:status_id])
|
||||||
|
end
|
||||||
|
end
|
@ -24,6 +24,14 @@ export const FAVOURITES_FETCH_REQUEST = 'FAVOURITES_FETCH_REQUEST';
|
|||||||
export const FAVOURITES_FETCH_SUCCESS = 'FAVOURITES_FETCH_SUCCESS';
|
export const FAVOURITES_FETCH_SUCCESS = 'FAVOURITES_FETCH_SUCCESS';
|
||||||
export const FAVOURITES_FETCH_FAIL = 'FAVOURITES_FETCH_FAIL';
|
export const FAVOURITES_FETCH_FAIL = 'FAVOURITES_FETCH_FAIL';
|
||||||
|
|
||||||
|
export const PIN_REQUEST = 'PIN_REQUEST';
|
||||||
|
export const PIN_SUCCESS = 'PIN_SUCCESS';
|
||||||
|
export const PIN_FAIL = 'PIN_FAIL';
|
||||||
|
|
||||||
|
export const UNPIN_REQUEST = 'UNPIN_REQUEST';
|
||||||
|
export const UNPIN_SUCCESS = 'UNPIN_SUCCESS';
|
||||||
|
export const UNPIN_FAIL = 'UNPIN_FAIL';
|
||||||
|
|
||||||
export function reblog(status) {
|
export function reblog(status) {
|
||||||
return function (dispatch, getState) {
|
return function (dispatch, getState) {
|
||||||
dispatch(reblogRequest(status));
|
dispatch(reblogRequest(status));
|
||||||
@ -233,3 +241,73 @@ export function fetchFavouritesFail(id, error) {
|
|||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function pin(status) {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
dispatch(pinRequest(status));
|
||||||
|
|
||||||
|
api(getState).post(`/api/v1/statuses/${status.get('id')}/pin`).then(response => {
|
||||||
|
dispatch(pinSuccess(status, response.data));
|
||||||
|
}).catch(error => {
|
||||||
|
dispatch(pinFail(status, error));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function pinRequest(status) {
|
||||||
|
return {
|
||||||
|
type: PIN_REQUEST,
|
||||||
|
status,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function pinSuccess(status, response) {
|
||||||
|
return {
|
||||||
|
type: PIN_SUCCESS,
|
||||||
|
status,
|
||||||
|
response,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function pinFail(status, error) {
|
||||||
|
return {
|
||||||
|
type: PIN_FAIL,
|
||||||
|
status,
|
||||||
|
error,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function unpin (status) {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
dispatch(unpinRequest(status));
|
||||||
|
|
||||||
|
api(getState).post(`/api/v1/statuses/${status.get('id')}/unpin`).then(response => {
|
||||||
|
dispatch(unpinSuccess(status, response.data));
|
||||||
|
}).catch(error => {
|
||||||
|
dispatch(unpinFail(status, error));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function unpinRequest(status) {
|
||||||
|
return {
|
||||||
|
type: UNPIN_REQUEST,
|
||||||
|
status,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function unpinSuccess(status, response) {
|
||||||
|
return {
|
||||||
|
type: UNPIN_SUCCESS,
|
||||||
|
status,
|
||||||
|
response,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function unpinFail(status, error) {
|
||||||
|
return {
|
||||||
|
type: UNPIN_FAIL,
|
||||||
|
status,
|
||||||
|
error,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -31,6 +31,7 @@ export default class Status extends ImmutablePureComponent {
|
|||||||
onFavourite: PropTypes.func,
|
onFavourite: PropTypes.func,
|
||||||
onReblog: PropTypes.func,
|
onReblog: PropTypes.func,
|
||||||
onDelete: PropTypes.func,
|
onDelete: PropTypes.func,
|
||||||
|
onPin: PropTypes.func,
|
||||||
onOpenMedia: PropTypes.func,
|
onOpenMedia: PropTypes.func,
|
||||||
onOpenVideo: PropTypes.func,
|
onOpenVideo: PropTypes.func,
|
||||||
onBlock: PropTypes.func,
|
onBlock: PropTypes.func,
|
||||||
|
@ -21,6 +21,8 @@ const messages = defineMessages({
|
|||||||
report: { id: 'status.report', defaultMessage: 'Report @{name}' },
|
report: { id: 'status.report', defaultMessage: 'Report @{name}' },
|
||||||
muteConversation: { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' },
|
muteConversation: { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' },
|
||||||
unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' },
|
unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' },
|
||||||
|
pin: { id: 'status.pin', defaultMessage: 'Pin on profile' },
|
||||||
|
unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
|
||||||
});
|
});
|
||||||
|
|
||||||
@injectIntl
|
@injectIntl
|
||||||
@ -41,6 +43,7 @@ export default class StatusActionBar extends ImmutablePureComponent {
|
|||||||
onBlock: PropTypes.func,
|
onBlock: PropTypes.func,
|
||||||
onReport: PropTypes.func,
|
onReport: PropTypes.func,
|
||||||
onMuteConversation: PropTypes.func,
|
onMuteConversation: PropTypes.func,
|
||||||
|
onPin: PropTypes.func,
|
||||||
me: PropTypes.number,
|
me: PropTypes.number,
|
||||||
withDismiss: PropTypes.bool,
|
withDismiss: PropTypes.bool,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
@ -77,6 +80,10 @@ export default class StatusActionBar extends ImmutablePureComponent {
|
|||||||
this.props.onDelete(this.props.status);
|
this.props.onDelete(this.props.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handlePinClick = () => {
|
||||||
|
this.props.onPin(this.props.status);
|
||||||
|
}
|
||||||
|
|
||||||
handleMentionClick = () => {
|
handleMentionClick = () => {
|
||||||
this.props.onMention(this.props.status.get('account'), this.context.router.history);
|
this.props.onMention(this.props.status.get('account'), this.context.router.history);
|
||||||
}
|
}
|
||||||
@ -121,6 +128,10 @@ export default class StatusActionBar extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (status.getIn(['account', 'id']) === me) {
|
if (status.getIn(['account', 'id']) === me) {
|
||||||
|
if (['public', 'unlisted'].indexOf(status.get('visibility')) !== -1) {
|
||||||
|
menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
|
||||||
|
}
|
||||||
|
|
||||||
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
|
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
|
||||||
} else {
|
} else {
|
||||||
menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
|
menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
|
||||||
|
@ -11,6 +11,8 @@ import {
|
|||||||
favourite,
|
favourite,
|
||||||
unreblog,
|
unreblog,
|
||||||
unfavourite,
|
unfavourite,
|
||||||
|
pin,
|
||||||
|
unpin,
|
||||||
} from '../actions/interactions';
|
} from '../actions/interactions';
|
||||||
import {
|
import {
|
||||||
blockAccount,
|
blockAccount,
|
||||||
@ -72,6 +74,14 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onPin (status) {
|
||||||
|
if (status.get('pinned')) {
|
||||||
|
dispatch(unpin(status));
|
||||||
|
} else {
|
||||||
|
dispatch(pin(status));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
onDelete (status) {
|
onDelete (status) {
|
||||||
if (!this.deleteModal) {
|
if (!this.deleteModal) {
|
||||||
dispatch(deleteStatus(status.get('id')));
|
dispatch(deleteStatus(status.get('id')));
|
||||||
|
@ -14,6 +14,8 @@ const messages = defineMessages({
|
|||||||
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
|
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
|
||||||
report: { id: 'status.report', defaultMessage: 'Report @{name}' },
|
report: { id: 'status.report', defaultMessage: 'Report @{name}' },
|
||||||
share: { id: 'status.share', defaultMessage: 'Share' },
|
share: { id: 'status.share', defaultMessage: 'Share' },
|
||||||
|
pin: { id: 'status.pin', defaultMessage: 'Pin on profile' },
|
||||||
|
unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
|
||||||
});
|
});
|
||||||
|
|
||||||
@injectIntl
|
@injectIntl
|
||||||
@ -31,6 +33,7 @@ export default class ActionBar extends React.PureComponent {
|
|||||||
onDelete: PropTypes.func.isRequired,
|
onDelete: PropTypes.func.isRequired,
|
||||||
onMention: PropTypes.func.isRequired,
|
onMention: PropTypes.func.isRequired,
|
||||||
onReport: PropTypes.func,
|
onReport: PropTypes.func,
|
||||||
|
onPin: PropTypes.func,
|
||||||
me: PropTypes.number.isRequired,
|
me: PropTypes.number.isRequired,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
@ -59,6 +62,10 @@ export default class ActionBar extends React.PureComponent {
|
|||||||
this.props.onReport(this.props.status);
|
this.props.onReport(this.props.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handlePinClick = () => {
|
||||||
|
this.props.onPin(this.props.status);
|
||||||
|
}
|
||||||
|
|
||||||
handleShare = () => {
|
handleShare = () => {
|
||||||
navigator.share({
|
navigator.share({
|
||||||
text: this.props.status.get('search_index'),
|
text: this.props.status.get('search_index'),
|
||||||
@ -72,6 +79,10 @@ export default class ActionBar extends React.PureComponent {
|
|||||||
let menu = [];
|
let menu = [];
|
||||||
|
|
||||||
if (me === status.getIn(['account', 'id'])) {
|
if (me === status.getIn(['account', 'id'])) {
|
||||||
|
if (['public', 'unlisted'].indexOf(status.get('visibility')) !== -1) {
|
||||||
|
menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
|
||||||
|
}
|
||||||
|
|
||||||
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
|
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
|
||||||
} else {
|
} else {
|
||||||
menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
|
menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
|
||||||
|
@ -12,6 +12,8 @@ import {
|
|||||||
unfavourite,
|
unfavourite,
|
||||||
reblog,
|
reblog,
|
||||||
unreblog,
|
unreblog,
|
||||||
|
pin,
|
||||||
|
unpin,
|
||||||
} from '../../actions/interactions';
|
} from '../../actions/interactions';
|
||||||
import {
|
import {
|
||||||
replyCompose,
|
replyCompose,
|
||||||
@ -87,6 +89,14 @@ export default class Status extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handlePin = (status) => {
|
||||||
|
if (status.get('pinned')) {
|
||||||
|
this.props.dispatch(unpin(status));
|
||||||
|
} else {
|
||||||
|
this.props.dispatch(pin(status));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleReplyClick = (status) => {
|
handleReplyClick = (status) => {
|
||||||
this.props.dispatch(replyCompose(status, this.context.router.history));
|
this.props.dispatch(replyCompose(status, this.context.router.history));
|
||||||
}
|
}
|
||||||
@ -187,6 +197,7 @@ export default class Status extends ImmutablePureComponent {
|
|||||||
onDelete={this.handleDeleteClick}
|
onDelete={this.handleDeleteClick}
|
||||||
onMention={this.handleMentionClick}
|
onMention={this.handleMentionClick}
|
||||||
onReport={this.handleReport}
|
onReport={this.handleReport}
|
||||||
|
onPin={this.handlePin}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{descendants}
|
{descendants}
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "أذكُر @{name}",
|
"status.mention": "أذكُر @{name}",
|
||||||
"status.mute_conversation": "Mute conversation",
|
"status.mute_conversation": "Mute conversation",
|
||||||
"status.open": "وسع هذه المشاركة",
|
"status.open": "وسع هذه المشاركة",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "رَقِّي",
|
"status.reblog": "رَقِّي",
|
||||||
"status.reblogged_by": "{name} رقى",
|
"status.reblogged_by": "{name} رقى",
|
||||||
"status.reply": "ردّ",
|
"status.reply": "ردّ",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "إعرض أقلّ",
|
"status.show_less": "إعرض أقلّ",
|
||||||
"status.show_more": "أظهر المزيد",
|
"status.show_more": "أظهر المزيد",
|
||||||
"status.unmute_conversation": "Unmute conversation",
|
"status.unmute_conversation": "Unmute conversation",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "تحرير",
|
"tabs_bar.compose": "تحرير",
|
||||||
"tabs_bar.federated_timeline": "الموحَّد",
|
"tabs_bar.federated_timeline": "الموحَّد",
|
||||||
"tabs_bar.home": "الرئيسية",
|
"tabs_bar.home": "الرئيسية",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "Споменаване",
|
"status.mention": "Споменаване",
|
||||||
"status.mute_conversation": "Mute conversation",
|
"status.mute_conversation": "Mute conversation",
|
||||||
"status.open": "Expand this status",
|
"status.open": "Expand this status",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "Споделяне",
|
"status.reblog": "Споделяне",
|
||||||
"status.reblogged_by": "{name} сподели",
|
"status.reblogged_by": "{name} сподели",
|
||||||
"status.reply": "Отговор",
|
"status.reply": "Отговор",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "Show less",
|
"status.show_less": "Show less",
|
||||||
"status.show_more": "Show more",
|
"status.show_more": "Show more",
|
||||||
"status.unmute_conversation": "Unmute conversation",
|
"status.unmute_conversation": "Unmute conversation",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "Съставяне",
|
"tabs_bar.compose": "Съставяне",
|
||||||
"tabs_bar.federated_timeline": "Federated",
|
"tabs_bar.federated_timeline": "Federated",
|
||||||
"tabs_bar.home": "Начало",
|
"tabs_bar.home": "Начало",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "Esmentar @{name}",
|
"status.mention": "Esmentar @{name}",
|
||||||
"status.mute_conversation": "Silenciar conversació",
|
"status.mute_conversation": "Silenciar conversació",
|
||||||
"status.open": "Ampliar aquest estat",
|
"status.open": "Ampliar aquest estat",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "Boost",
|
"status.reblog": "Boost",
|
||||||
"status.reblogged_by": "{name} ha retootejat",
|
"status.reblogged_by": "{name} ha retootejat",
|
||||||
"status.reply": "Respondre",
|
"status.reply": "Respondre",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "Mostra menys",
|
"status.show_less": "Mostra menys",
|
||||||
"status.show_more": "Mostra més",
|
"status.show_more": "Mostra més",
|
||||||
"status.unmute_conversation": "Activar conversació",
|
"status.unmute_conversation": "Activar conversació",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "Compondre",
|
"tabs_bar.compose": "Compondre",
|
||||||
"tabs_bar.federated_timeline": "Federada",
|
"tabs_bar.federated_timeline": "Federada",
|
||||||
"tabs_bar.home": "Inici",
|
"tabs_bar.home": "Inici",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "Erwähnen",
|
"status.mention": "Erwähnen",
|
||||||
"status.mute_conversation": "Mute conversation",
|
"status.mute_conversation": "Mute conversation",
|
||||||
"status.open": "Öffnen",
|
"status.open": "Öffnen",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "Teilen",
|
"status.reblog": "Teilen",
|
||||||
"status.reblogged_by": "{name} teilte",
|
"status.reblogged_by": "{name} teilte",
|
||||||
"status.reply": "Antworten",
|
"status.reply": "Antworten",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "Weniger anzeigen",
|
"status.show_less": "Weniger anzeigen",
|
||||||
"status.show_more": "Mehr anzeigen",
|
"status.show_more": "Mehr anzeigen",
|
||||||
"status.unmute_conversation": "Unmute conversation",
|
"status.unmute_conversation": "Unmute conversation",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "Schreiben",
|
"tabs_bar.compose": "Schreiben",
|
||||||
"tabs_bar.federated_timeline": "Föderation",
|
"tabs_bar.federated_timeline": "Föderation",
|
||||||
"tabs_bar.home": "Home",
|
"tabs_bar.home": "Home",
|
||||||
|
@ -189,6 +189,14 @@
|
|||||||
{
|
{
|
||||||
"defaultMessage": "Unmute conversation",
|
"defaultMessage": "Unmute conversation",
|
||||||
"id": "status.unmute_conversation"
|
"id": "status.unmute_conversation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"defaultMessage": "Pin on profile",
|
||||||
|
"id": "status.pin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"defaultMessage": "Unpin from profile",
|
||||||
|
"id": "status.unpin"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"path": "app/javascript/mastodon/components/status_action_bar.json"
|
"path": "app/javascript/mastodon/components/status_action_bar.json"
|
||||||
@ -1035,6 +1043,14 @@
|
|||||||
{
|
{
|
||||||
"defaultMessage": "Share",
|
"defaultMessage": "Share",
|
||||||
"id": "status.share"
|
"id": "status.share"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"defaultMessage": "Pin on profile",
|
||||||
|
"id": "status.pin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"defaultMessage": "Unpin from profile",
|
||||||
|
"id": "status.unpin"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"path": "app/javascript/mastodon/features/status/components/action_bar.json"
|
"path": "app/javascript/mastodon/features/status/components/action_bar.json"
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "Mention @{name}",
|
"status.mention": "Mention @{name}",
|
||||||
"status.mute_conversation": "Mute conversation",
|
"status.mute_conversation": "Mute conversation",
|
||||||
"status.open": "Expand this status",
|
"status.open": "Expand this status",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "Boost",
|
"status.reblog": "Boost",
|
||||||
"status.reblogged_by": "{name} boosted",
|
"status.reblogged_by": "{name} boosted",
|
||||||
"status.reply": "Reply",
|
"status.reply": "Reply",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "Show less",
|
"status.show_less": "Show less",
|
||||||
"status.show_more": "Show more",
|
"status.show_more": "Show more",
|
||||||
"status.unmute_conversation": "Unmute conversation",
|
"status.unmute_conversation": "Unmute conversation",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "Compose",
|
"tabs_bar.compose": "Compose",
|
||||||
"tabs_bar.federated_timeline": "Federated",
|
"tabs_bar.federated_timeline": "Federated",
|
||||||
"tabs_bar.home": "Home",
|
"tabs_bar.home": "Home",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "Mencii @{name}",
|
"status.mention": "Mencii @{name}",
|
||||||
"status.mute_conversation": "Mute conversation",
|
"status.mute_conversation": "Mute conversation",
|
||||||
"status.open": "Expand this status",
|
"status.open": "Expand this status",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "Diskonigi",
|
"status.reblog": "Diskonigi",
|
||||||
"status.reblogged_by": "{name} diskonigita",
|
"status.reblogged_by": "{name} diskonigita",
|
||||||
"status.reply": "Respondi",
|
"status.reply": "Respondi",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "Show less",
|
"status.show_less": "Show less",
|
||||||
"status.show_more": "Show more",
|
"status.show_more": "Show more",
|
||||||
"status.unmute_conversation": "Unmute conversation",
|
"status.unmute_conversation": "Unmute conversation",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "Ekskribi",
|
"tabs_bar.compose": "Ekskribi",
|
||||||
"tabs_bar.federated_timeline": "Federated",
|
"tabs_bar.federated_timeline": "Federated",
|
||||||
"tabs_bar.home": "Hejmo",
|
"tabs_bar.home": "Hejmo",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "Mencionar",
|
"status.mention": "Mencionar",
|
||||||
"status.mute_conversation": "Mute conversation",
|
"status.mute_conversation": "Mute conversation",
|
||||||
"status.open": "Expandir estado",
|
"status.open": "Expandir estado",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "Retoot",
|
"status.reblog": "Retoot",
|
||||||
"status.reblogged_by": "Retooteado por {name}",
|
"status.reblogged_by": "Retooteado por {name}",
|
||||||
"status.reply": "Responder",
|
"status.reply": "Responder",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "Mostrar menos",
|
"status.show_less": "Mostrar menos",
|
||||||
"status.show_more": "Mostrar más",
|
"status.show_more": "Mostrar más",
|
||||||
"status.unmute_conversation": "Unmute conversation",
|
"status.unmute_conversation": "Unmute conversation",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "Redactar",
|
"tabs_bar.compose": "Redactar",
|
||||||
"tabs_bar.federated_timeline": "Federated",
|
"tabs_bar.federated_timeline": "Federated",
|
||||||
"tabs_bar.home": "Inicio",
|
"tabs_bar.home": "Inicio",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "نامبردن از @{name}",
|
"status.mention": "نامبردن از @{name}",
|
||||||
"status.mute_conversation": "بیصداکردن گفتگو",
|
"status.mute_conversation": "بیصداکردن گفتگو",
|
||||||
"status.open": "این نوشته را باز کن",
|
"status.open": "این نوشته را باز کن",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "بازبوقیدن",
|
"status.reblog": "بازبوقیدن",
|
||||||
"status.reblogged_by": "{name} بازبوقید",
|
"status.reblogged_by": "{name} بازبوقید",
|
||||||
"status.reply": "پاسخ",
|
"status.reply": "پاسخ",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "نهفتن",
|
"status.show_less": "نهفتن",
|
||||||
"status.show_more": "نمایش",
|
"status.show_more": "نمایش",
|
||||||
"status.unmute_conversation": "باصداکردن گفتگو",
|
"status.unmute_conversation": "باصداکردن گفتگو",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "بنویسید",
|
"tabs_bar.compose": "بنویسید",
|
||||||
"tabs_bar.federated_timeline": "همگانی",
|
"tabs_bar.federated_timeline": "همگانی",
|
||||||
"tabs_bar.home": "خانه",
|
"tabs_bar.home": "خانه",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "Mainitse @{name}",
|
"status.mention": "Mainitse @{name}",
|
||||||
"status.mute_conversation": "Mute conversation",
|
"status.mute_conversation": "Mute conversation",
|
||||||
"status.open": "Expand this status",
|
"status.open": "Expand this status",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "Buustaa",
|
"status.reblog": "Buustaa",
|
||||||
"status.reblogged_by": "{name} buustasi",
|
"status.reblogged_by": "{name} buustasi",
|
||||||
"status.reply": "Vastaa",
|
"status.reply": "Vastaa",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "Show less",
|
"status.show_less": "Show less",
|
||||||
"status.show_more": "Show more",
|
"status.show_more": "Show more",
|
||||||
"status.unmute_conversation": "Unmute conversation",
|
"status.unmute_conversation": "Unmute conversation",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "Luo",
|
"tabs_bar.compose": "Luo",
|
||||||
"tabs_bar.federated_timeline": "Federated",
|
"tabs_bar.federated_timeline": "Federated",
|
||||||
"tabs_bar.home": "Koti",
|
"tabs_bar.home": "Koti",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "Mentionner",
|
"status.mention": "Mentionner",
|
||||||
"status.mute_conversation": "Masquer la conversation",
|
"status.mute_conversation": "Masquer la conversation",
|
||||||
"status.open": "Déplier ce statut",
|
"status.open": "Déplier ce statut",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "Partager",
|
"status.reblog": "Partager",
|
||||||
"status.reblogged_by": "{name} a partagé :",
|
"status.reblogged_by": "{name} a partagé :",
|
||||||
"status.reply": "Répondre",
|
"status.reply": "Répondre",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "Replier",
|
"status.show_less": "Replier",
|
||||||
"status.show_more": "Déplier",
|
"status.show_more": "Déplier",
|
||||||
"status.unmute_conversation": "Ne plus masquer la conversation",
|
"status.unmute_conversation": "Ne plus masquer la conversation",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "Composer",
|
"tabs_bar.compose": "Composer",
|
||||||
"tabs_bar.federated_timeline": "Fil public global",
|
"tabs_bar.federated_timeline": "Fil public global",
|
||||||
"tabs_bar.home": "Accueil",
|
"tabs_bar.home": "Accueil",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "פניה אל @{name}",
|
"status.mention": "פניה אל @{name}",
|
||||||
"status.mute_conversation": "השתקת שיחה",
|
"status.mute_conversation": "השתקת שיחה",
|
||||||
"status.open": "הרחבת הודעה",
|
"status.open": "הרחבת הודעה",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "הדהוד",
|
"status.reblog": "הדהוד",
|
||||||
"status.reblogged_by": "הודהד על ידי {name}",
|
"status.reblogged_by": "הודהד על ידי {name}",
|
||||||
"status.reply": "תגובה",
|
"status.reply": "תגובה",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "הראה פחות",
|
"status.show_less": "הראה פחות",
|
||||||
"status.show_more": "הראה יותר",
|
"status.show_more": "הראה יותר",
|
||||||
"status.unmute_conversation": "הסרת השתקת שיחה",
|
"status.unmute_conversation": "הסרת השתקת שיחה",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "חיבור",
|
"tabs_bar.compose": "חיבור",
|
||||||
"tabs_bar.federated_timeline": "ציר זמן בין-קהילתי",
|
"tabs_bar.federated_timeline": "ציר זמן בין-קהילתי",
|
||||||
"tabs_bar.home": "בבית",
|
"tabs_bar.home": "בבית",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "Spomeni @{name}",
|
"status.mention": "Spomeni @{name}",
|
||||||
"status.mute_conversation": "Utišaj razgovor",
|
"status.mute_conversation": "Utišaj razgovor",
|
||||||
"status.open": "Proširi ovaj status",
|
"status.open": "Proširi ovaj status",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "Podigni",
|
"status.reblog": "Podigni",
|
||||||
"status.reblogged_by": "{name} je podigao",
|
"status.reblogged_by": "{name} je podigao",
|
||||||
"status.reply": "Odgovori",
|
"status.reply": "Odgovori",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "Pokaži manje",
|
"status.show_less": "Pokaži manje",
|
||||||
"status.show_more": "Pokaži više",
|
"status.show_more": "Pokaži više",
|
||||||
"status.unmute_conversation": "Poništi utišavanje razgovora",
|
"status.unmute_conversation": "Poništi utišavanje razgovora",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "Sastavi",
|
"tabs_bar.compose": "Sastavi",
|
||||||
"tabs_bar.federated_timeline": "Federalni",
|
"tabs_bar.federated_timeline": "Federalni",
|
||||||
"tabs_bar.home": "Dom",
|
"tabs_bar.home": "Dom",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "Említés",
|
"status.mention": "Említés",
|
||||||
"status.mute_conversation": "Mute conversation",
|
"status.mute_conversation": "Mute conversation",
|
||||||
"status.open": "Expand this status",
|
"status.open": "Expand this status",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "Reblog",
|
"status.reblog": "Reblog",
|
||||||
"status.reblogged_by": "{name} reblogolta",
|
"status.reblogged_by": "{name} reblogolta",
|
||||||
"status.reply": "Válasz",
|
"status.reply": "Válasz",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "Show less",
|
"status.show_less": "Show less",
|
||||||
"status.show_more": "Show more",
|
"status.show_more": "Show more",
|
||||||
"status.unmute_conversation": "Unmute conversation",
|
"status.unmute_conversation": "Unmute conversation",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "Összeállítás",
|
"tabs_bar.compose": "Összeállítás",
|
||||||
"tabs_bar.federated_timeline": "Federated",
|
"tabs_bar.federated_timeline": "Federated",
|
||||||
"tabs_bar.home": "Kezdőlap",
|
"tabs_bar.home": "Kezdőlap",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "Balasan @{name}",
|
"status.mention": "Balasan @{name}",
|
||||||
"status.mute_conversation": "Mute conversation",
|
"status.mute_conversation": "Mute conversation",
|
||||||
"status.open": "Tampilkan status ini",
|
"status.open": "Tampilkan status ini",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "Boost",
|
"status.reblog": "Boost",
|
||||||
"status.reblogged_by": "di-boost {name}",
|
"status.reblogged_by": "di-boost {name}",
|
||||||
"status.reply": "Balas",
|
"status.reply": "Balas",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "Tampilkan lebih sedikit",
|
"status.show_less": "Tampilkan lebih sedikit",
|
||||||
"status.show_more": "Tampilkan semua",
|
"status.show_more": "Tampilkan semua",
|
||||||
"status.unmute_conversation": "Unmute conversation",
|
"status.unmute_conversation": "Unmute conversation",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "Tulis",
|
"tabs_bar.compose": "Tulis",
|
||||||
"tabs_bar.federated_timeline": "Gabungan",
|
"tabs_bar.federated_timeline": "Gabungan",
|
||||||
"tabs_bar.home": "Beranda",
|
"tabs_bar.home": "Beranda",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "Mencionar @{name}",
|
"status.mention": "Mencionar @{name}",
|
||||||
"status.mute_conversation": "Mute conversation",
|
"status.mute_conversation": "Mute conversation",
|
||||||
"status.open": "Detaligar ca mesajo",
|
"status.open": "Detaligar ca mesajo",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "Repetar",
|
"status.reblog": "Repetar",
|
||||||
"status.reblogged_by": "{name} repetita",
|
"status.reblogged_by": "{name} repetita",
|
||||||
"status.reply": "Respondar",
|
"status.reply": "Respondar",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "Montrar mine",
|
"status.show_less": "Montrar mine",
|
||||||
"status.show_more": "Montrar plue",
|
"status.show_more": "Montrar plue",
|
||||||
"status.unmute_conversation": "Unmute conversation",
|
"status.unmute_conversation": "Unmute conversation",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "Kompozar",
|
"tabs_bar.compose": "Kompozar",
|
||||||
"tabs_bar.federated_timeline": "Federata",
|
"tabs_bar.federated_timeline": "Federata",
|
||||||
"tabs_bar.home": "Hemo",
|
"tabs_bar.home": "Hemo",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "Nomina @{name}",
|
"status.mention": "Nomina @{name}",
|
||||||
"status.mute_conversation": "Mute conversation",
|
"status.mute_conversation": "Mute conversation",
|
||||||
"status.open": "Espandi questo post",
|
"status.open": "Espandi questo post",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "Condividi",
|
"status.reblog": "Condividi",
|
||||||
"status.reblogged_by": "{name} ha condiviso",
|
"status.reblogged_by": "{name} ha condiviso",
|
||||||
"status.reply": "Rispondi",
|
"status.reply": "Rispondi",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "Mostra meno",
|
"status.show_less": "Mostra meno",
|
||||||
"status.show_more": "Mostra di più",
|
"status.show_more": "Mostra di più",
|
||||||
"status.unmute_conversation": "Unmute conversation",
|
"status.unmute_conversation": "Unmute conversation",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "Scrivi",
|
"tabs_bar.compose": "Scrivi",
|
||||||
"tabs_bar.federated_timeline": "Federazione",
|
"tabs_bar.federated_timeline": "Federazione",
|
||||||
"tabs_bar.home": "Home",
|
"tabs_bar.home": "Home",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "返信",
|
"status.mention": "返信",
|
||||||
"status.mute_conversation": "会話をミュート",
|
"status.mute_conversation": "会話をミュート",
|
||||||
"status.open": "詳細を表示",
|
"status.open": "詳細を表示",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "ブースト",
|
"status.reblog": "ブースト",
|
||||||
"status.reblogged_by": "{name}さんにブーストされました",
|
"status.reblogged_by": "{name}さんにブーストされました",
|
||||||
"status.reply": "返信",
|
"status.reply": "返信",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "隠す",
|
"status.show_less": "隠す",
|
||||||
"status.show_more": "もっと見る",
|
"status.show_more": "もっと見る",
|
||||||
"status.unmute_conversation": "会話のミュートを解除",
|
"status.unmute_conversation": "会話のミュートを解除",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "投稿",
|
"tabs_bar.compose": "投稿",
|
||||||
"tabs_bar.federated_timeline": "連合",
|
"tabs_bar.federated_timeline": "連合",
|
||||||
"tabs_bar.home": "ホーム",
|
"tabs_bar.home": "ホーム",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "답장",
|
"status.mention": "답장",
|
||||||
"status.mute_conversation": "이 대화를 뮤트",
|
"status.mute_conversation": "이 대화를 뮤트",
|
||||||
"status.open": "상세 정보 표시",
|
"status.open": "상세 정보 표시",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "부스트",
|
"status.reblog": "부스트",
|
||||||
"status.reblogged_by": "{name}님이 부스트 했습니다",
|
"status.reblogged_by": "{name}님이 부스트 했습니다",
|
||||||
"status.reply": "답장",
|
"status.reply": "답장",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "숨기기",
|
"status.show_less": "숨기기",
|
||||||
"status.show_more": "더 보기",
|
"status.show_more": "더 보기",
|
||||||
"status.unmute_conversation": "이 대화의 뮤트 해제하기",
|
"status.unmute_conversation": "이 대화의 뮤트 해제하기",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "포스트",
|
"tabs_bar.compose": "포스트",
|
||||||
"tabs_bar.federated_timeline": "연합",
|
"tabs_bar.federated_timeline": "연합",
|
||||||
"tabs_bar.home": "홈",
|
"tabs_bar.home": "홈",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "Vermeld @{name}",
|
"status.mention": "Vermeld @{name}",
|
||||||
"status.mute_conversation": "Negeer conversatie",
|
"status.mute_conversation": "Negeer conversatie",
|
||||||
"status.open": "Toot volledig tonen",
|
"status.open": "Toot volledig tonen",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "Boost",
|
"status.reblog": "Boost",
|
||||||
"status.reblogged_by": "{name} boostte",
|
"status.reblogged_by": "{name} boostte",
|
||||||
"status.reply": "Reageren",
|
"status.reply": "Reageren",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "Minder tonen",
|
"status.show_less": "Minder tonen",
|
||||||
"status.show_more": "Meer tonen",
|
"status.show_more": "Meer tonen",
|
||||||
"status.unmute_conversation": "Conversatie niet meer negeren",
|
"status.unmute_conversation": "Conversatie niet meer negeren",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "Schrijven",
|
"tabs_bar.compose": "Schrijven",
|
||||||
"tabs_bar.federated_timeline": "Globaal",
|
"tabs_bar.federated_timeline": "Globaal",
|
||||||
"tabs_bar.home": "Start",
|
"tabs_bar.home": "Start",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "Nevn @{name}",
|
"status.mention": "Nevn @{name}",
|
||||||
"status.mute_conversation": "Demp samtale",
|
"status.mute_conversation": "Demp samtale",
|
||||||
"status.open": "Utvid denne statusen",
|
"status.open": "Utvid denne statusen",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "Fremhev",
|
"status.reblog": "Fremhev",
|
||||||
"status.reblogged_by": "Fremhevd av {name}",
|
"status.reblogged_by": "Fremhevd av {name}",
|
||||||
"status.reply": "Svar",
|
"status.reply": "Svar",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "Vis mindre",
|
"status.show_less": "Vis mindre",
|
||||||
"status.show_more": "Vis mer",
|
"status.show_more": "Vis mer",
|
||||||
"status.unmute_conversation": "Ikke demp samtale",
|
"status.unmute_conversation": "Ikke demp samtale",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "Komponer",
|
"tabs_bar.compose": "Komponer",
|
||||||
"tabs_bar.federated_timeline": "Felles",
|
"tabs_bar.federated_timeline": "Felles",
|
||||||
"tabs_bar.home": "Hjem",
|
"tabs_bar.home": "Hjem",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "Mencionar",
|
"status.mention": "Mencionar",
|
||||||
"status.mute_conversation": "Rescondre la conversacion",
|
"status.mute_conversation": "Rescondre la conversacion",
|
||||||
"status.open": "Desplegar aqueste estatut",
|
"status.open": "Desplegar aqueste estatut",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "Partejar",
|
"status.reblog": "Partejar",
|
||||||
"status.reblogged_by": "{name} a partejat :",
|
"status.reblogged_by": "{name} a partejat :",
|
||||||
"status.reply": "Respondre",
|
"status.reply": "Respondre",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "Tornar plegar",
|
"status.show_less": "Tornar plegar",
|
||||||
"status.show_more": "Desplegar",
|
"status.show_more": "Desplegar",
|
||||||
"status.unmute_conversation": "Conversacions amb silenci levat",
|
"status.unmute_conversation": "Conversacions amb silenci levat",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "Compausar",
|
"tabs_bar.compose": "Compausar",
|
||||||
"tabs_bar.federated_timeline": "Flux public global",
|
"tabs_bar.federated_timeline": "Flux public global",
|
||||||
"tabs_bar.home": "Acuèlh",
|
"tabs_bar.home": "Acuèlh",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "Wspomnij o @{name}",
|
"status.mention": "Wspomnij o @{name}",
|
||||||
"status.mute_conversation": "Wycisz konwersację",
|
"status.mute_conversation": "Wycisz konwersację",
|
||||||
"status.open": "Rozszerz ten status",
|
"status.open": "Rozszerz ten status",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "Podbij",
|
"status.reblog": "Podbij",
|
||||||
"status.reblogged_by": "{name} podbił",
|
"status.reblogged_by": "{name} podbił",
|
||||||
"status.reply": "Odpowiedz",
|
"status.reply": "Odpowiedz",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "Pokaż mniej",
|
"status.show_less": "Pokaż mniej",
|
||||||
"status.show_more": "Pokaż więcej",
|
"status.show_more": "Pokaż więcej",
|
||||||
"status.unmute_conversation": "Cofnij wyciszenie konwersacji",
|
"status.unmute_conversation": "Cofnij wyciszenie konwersacji",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "Napisz",
|
"tabs_bar.compose": "Napisz",
|
||||||
"tabs_bar.federated_timeline": "Globalne",
|
"tabs_bar.federated_timeline": "Globalne",
|
||||||
"tabs_bar.home": "Strona główna",
|
"tabs_bar.home": "Strona główna",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "Mencionar @{name}",
|
"status.mention": "Mencionar @{name}",
|
||||||
"status.mute_conversation": "Mute conversation",
|
"status.mute_conversation": "Mute conversation",
|
||||||
"status.open": "Expandir",
|
"status.open": "Expandir",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "Partilhar",
|
"status.reblog": "Partilhar",
|
||||||
"status.reblogged_by": "{name} partilhou",
|
"status.reblogged_by": "{name} partilhou",
|
||||||
"status.reply": "Responder",
|
"status.reply": "Responder",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "Mostrar menos",
|
"status.show_less": "Mostrar menos",
|
||||||
"status.show_more": "Mostrar mais",
|
"status.show_more": "Mostrar mais",
|
||||||
"status.unmute_conversation": "Unmute conversation",
|
"status.unmute_conversation": "Unmute conversation",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "Criar",
|
"tabs_bar.compose": "Criar",
|
||||||
"tabs_bar.federated_timeline": "Global",
|
"tabs_bar.federated_timeline": "Global",
|
||||||
"tabs_bar.home": "Home",
|
"tabs_bar.home": "Home",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "Mencionar @{name}",
|
"status.mention": "Mencionar @{name}",
|
||||||
"status.mute_conversation": "Mute conversation",
|
"status.mute_conversation": "Mute conversation",
|
||||||
"status.open": "Expandir",
|
"status.open": "Expandir",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "Partilhar",
|
"status.reblog": "Partilhar",
|
||||||
"status.reblogged_by": "{name} partilhou",
|
"status.reblogged_by": "{name} partilhou",
|
||||||
"status.reply": "Responder",
|
"status.reply": "Responder",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "Mostrar menos",
|
"status.show_less": "Mostrar menos",
|
||||||
"status.show_more": "Mostrar mais",
|
"status.show_more": "Mostrar mais",
|
||||||
"status.unmute_conversation": "Unmute conversation",
|
"status.unmute_conversation": "Unmute conversation",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "Criar",
|
"tabs_bar.compose": "Criar",
|
||||||
"tabs_bar.federated_timeline": "Global",
|
"tabs_bar.federated_timeline": "Global",
|
||||||
"tabs_bar.home": "Home",
|
"tabs_bar.home": "Home",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "Упомянуть @{name}",
|
"status.mention": "Упомянуть @{name}",
|
||||||
"status.mute_conversation": "Заглушить тред",
|
"status.mute_conversation": "Заглушить тред",
|
||||||
"status.open": "Развернуть статус",
|
"status.open": "Развернуть статус",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "Продвинуть",
|
"status.reblog": "Продвинуть",
|
||||||
"status.reblogged_by": "{name} продвинул(а)",
|
"status.reblogged_by": "{name} продвинул(а)",
|
||||||
"status.reply": "Ответить",
|
"status.reply": "Ответить",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "Свернуть",
|
"status.show_less": "Свернуть",
|
||||||
"status.show_more": "Развернуть",
|
"status.show_more": "Развернуть",
|
||||||
"status.unmute_conversation": "Снять глушение с треда",
|
"status.unmute_conversation": "Снять глушение с треда",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "Написать",
|
"tabs_bar.compose": "Написать",
|
||||||
"tabs_bar.federated_timeline": "Глобальная",
|
"tabs_bar.federated_timeline": "Глобальная",
|
||||||
"tabs_bar.home": "Главная",
|
"tabs_bar.home": "Главная",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "Mention @{name}",
|
"status.mention": "Mention @{name}",
|
||||||
"status.mute_conversation": "Mute conversation",
|
"status.mute_conversation": "Mute conversation",
|
||||||
"status.open": "Expand this status",
|
"status.open": "Expand this status",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "Boost",
|
"status.reblog": "Boost",
|
||||||
"status.reblogged_by": "{name} boosted",
|
"status.reblogged_by": "{name} boosted",
|
||||||
"status.reply": "Reply",
|
"status.reply": "Reply",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "Show less",
|
"status.show_less": "Show less",
|
||||||
"status.show_more": "Show more",
|
"status.show_more": "Show more",
|
||||||
"status.unmute_conversation": "Unmute conversation",
|
"status.unmute_conversation": "Unmute conversation",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "Compose",
|
"tabs_bar.compose": "Compose",
|
||||||
"tabs_bar.federated_timeline": "Federated",
|
"tabs_bar.federated_timeline": "Federated",
|
||||||
"tabs_bar.home": "Home",
|
"tabs_bar.home": "Home",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "Bahset @{name}",
|
"status.mention": "Bahset @{name}",
|
||||||
"status.mute_conversation": "Mute conversation",
|
"status.mute_conversation": "Mute conversation",
|
||||||
"status.open": "Bu gönderiyi genişlet",
|
"status.open": "Bu gönderiyi genişlet",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "Boost'la",
|
"status.reblog": "Boost'la",
|
||||||
"status.reblogged_by": "{name} boost etti",
|
"status.reblogged_by": "{name} boost etti",
|
||||||
"status.reply": "Cevapla",
|
"status.reply": "Cevapla",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "Daha azı",
|
"status.show_less": "Daha azı",
|
||||||
"status.show_more": "Daha fazlası",
|
"status.show_more": "Daha fazlası",
|
||||||
"status.unmute_conversation": "Unmute conversation",
|
"status.unmute_conversation": "Unmute conversation",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "Oluştur",
|
"tabs_bar.compose": "Oluştur",
|
||||||
"tabs_bar.federated_timeline": "Federe",
|
"tabs_bar.federated_timeline": "Federe",
|
||||||
"tabs_bar.home": "Ana sayfa",
|
"tabs_bar.home": "Ana sayfa",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "Згадати",
|
"status.mention": "Згадати",
|
||||||
"status.mute_conversation": "Заглушити діалог",
|
"status.mute_conversation": "Заглушити діалог",
|
||||||
"status.open": "Розгорнути допис",
|
"status.open": "Розгорнути допис",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "Передмухнути",
|
"status.reblog": "Передмухнути",
|
||||||
"status.reblogged_by": "{name} передмухнув(-ла)",
|
"status.reblogged_by": "{name} передмухнув(-ла)",
|
||||||
"status.reply": "Відповісти",
|
"status.reply": "Відповісти",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "Згорнути",
|
"status.show_less": "Згорнути",
|
||||||
"status.show_more": "Розгорнути",
|
"status.show_more": "Розгорнути",
|
||||||
"status.unmute_conversation": "Зняти глушення з діалогу",
|
"status.unmute_conversation": "Зняти глушення з діалогу",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "Написати",
|
"tabs_bar.compose": "Написати",
|
||||||
"tabs_bar.federated_timeline": "Глобальна",
|
"tabs_bar.federated_timeline": "Глобальна",
|
||||||
"tabs_bar.home": "Головна",
|
"tabs_bar.home": "Головна",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "提及 @{name}",
|
"status.mention": "提及 @{name}",
|
||||||
"status.mute_conversation": "Mute conversation",
|
"status.mute_conversation": "Mute conversation",
|
||||||
"status.open": "展开嘟文",
|
"status.open": "展开嘟文",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "转嘟",
|
"status.reblog": "转嘟",
|
||||||
"status.reblogged_by": "{name} 转嘟",
|
"status.reblogged_by": "{name} 转嘟",
|
||||||
"status.reply": "回应",
|
"status.reply": "回应",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "减少显示",
|
"status.show_less": "减少显示",
|
||||||
"status.show_more": "显示更多",
|
"status.show_more": "显示更多",
|
||||||
"status.unmute_conversation": "Unmute conversation",
|
"status.unmute_conversation": "Unmute conversation",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "撰写",
|
"tabs_bar.compose": "撰写",
|
||||||
"tabs_bar.federated_timeline": "跨站",
|
"tabs_bar.federated_timeline": "跨站",
|
||||||
"tabs_bar.home": "主页",
|
"tabs_bar.home": "主页",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "提及 @{name}",
|
"status.mention": "提及 @{name}",
|
||||||
"status.mute_conversation": "Mute conversation",
|
"status.mute_conversation": "Mute conversation",
|
||||||
"status.open": "展開文章",
|
"status.open": "展開文章",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "轉推",
|
"status.reblog": "轉推",
|
||||||
"status.reblogged_by": "{name} 轉推",
|
"status.reblogged_by": "{name} 轉推",
|
||||||
"status.reply": "回應",
|
"status.reply": "回應",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "減少顯示",
|
"status.show_less": "減少顯示",
|
||||||
"status.show_more": "顯示更多",
|
"status.show_more": "顯示更多",
|
||||||
"status.unmute_conversation": "Unmute conversation",
|
"status.unmute_conversation": "Unmute conversation",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "撰寫",
|
"tabs_bar.compose": "撰寫",
|
||||||
"tabs_bar.federated_timeline": "跨站",
|
"tabs_bar.federated_timeline": "跨站",
|
||||||
"tabs_bar.home": "主頁",
|
"tabs_bar.home": "主頁",
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
"status.mention": "提到 @{name}",
|
"status.mention": "提到 @{name}",
|
||||||
"status.mute_conversation": "消音對話",
|
"status.mute_conversation": "消音對話",
|
||||||
"status.open": "展開這個狀態",
|
"status.open": "展開這個狀態",
|
||||||
|
"status.pin": "Pin on profile",
|
||||||
"status.reblog": "轉推",
|
"status.reblog": "轉推",
|
||||||
"status.reblogged_by": "{name} 轉推了",
|
"status.reblogged_by": "{name} 轉推了",
|
||||||
"status.reply": "回應",
|
"status.reply": "回應",
|
||||||
@ -179,6 +180,7 @@
|
|||||||
"status.show_less": "看少點",
|
"status.show_less": "看少點",
|
||||||
"status.show_more": "看更多",
|
"status.show_more": "看更多",
|
||||||
"status.unmute_conversation": "不消音對話",
|
"status.unmute_conversation": "不消音對話",
|
||||||
|
"status.unpin": "Unpin from profile",
|
||||||
"tabs_bar.compose": "編輯",
|
"tabs_bar.compose": "編輯",
|
||||||
"tabs_bar.federated_timeline": "聯盟",
|
"tabs_bar.federated_timeline": "聯盟",
|
||||||
"tabs_bar.home": "家",
|
"tabs_bar.home": "家",
|
||||||
|
@ -7,6 +7,8 @@ import {
|
|||||||
FAVOURITE_SUCCESS,
|
FAVOURITE_SUCCESS,
|
||||||
FAVOURITE_FAIL,
|
FAVOURITE_FAIL,
|
||||||
UNFAVOURITE_SUCCESS,
|
UNFAVOURITE_SUCCESS,
|
||||||
|
PIN_SUCCESS,
|
||||||
|
UNPIN_SUCCESS,
|
||||||
} from '../actions/interactions';
|
} from '../actions/interactions';
|
||||||
import {
|
import {
|
||||||
STATUS_FETCH_SUCCESS,
|
STATUS_FETCH_SUCCESS,
|
||||||
@ -114,6 +116,8 @@ export default function statuses(state = initialState, action) {
|
|||||||
case UNREBLOG_SUCCESS:
|
case UNREBLOG_SUCCESS:
|
||||||
case FAVOURITE_SUCCESS:
|
case FAVOURITE_SUCCESS:
|
||||||
case UNFAVOURITE_SUCCESS:
|
case UNFAVOURITE_SUCCESS:
|
||||||
|
case PIN_SUCCESS:
|
||||||
|
case UNPIN_SUCCESS:
|
||||||
return normalizeStatus(state, action.response);
|
return normalizeStatus(state, action.response);
|
||||||
case FAVOURITE_REQUEST:
|
case FAVOURITE_REQUEST:
|
||||||
return state.setIn([action.status.get('id'), 'favourited'], true);
|
return state.setIn([action.status.get('id'), 'favourited'], true);
|
||||||
|
@ -77,6 +77,10 @@ class Account < ApplicationRecord
|
|||||||
has_many :mentions, inverse_of: :account, dependent: :destroy
|
has_many :mentions, inverse_of: :account, dependent: :destroy
|
||||||
has_many :notifications, inverse_of: :account, dependent: :destroy
|
has_many :notifications, inverse_of: :account, dependent: :destroy
|
||||||
|
|
||||||
|
# Pinned statuses
|
||||||
|
has_many :status_pins, inverse_of: :account, dependent: :destroy
|
||||||
|
has_many :pinned_statuses, through: :status_pins, class_name: 'Status', source: :status
|
||||||
|
|
||||||
# Media
|
# Media
|
||||||
has_many :media_attachments, dependent: :destroy
|
has_many :media_attachments, dependent: :destroy
|
||||||
|
|
||||||
|
@ -138,4 +138,8 @@ module AccountInteractions
|
|||||||
def reblogged?(status)
|
def reblogged?(status)
|
||||||
status.proper.reblogs.where(account: self).exists?
|
status.proper.reblogs.where(account: self).exists?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pinned?(status)
|
||||||
|
status_pins.where(status: status).exists?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -164,6 +164,10 @@ class Status < ApplicationRecord
|
|||||||
ConversationMute.select('conversation_id').where(conversation_id: conversation_ids).where(account_id: account_id).map { |m| [m.conversation_id, true] }.to_h
|
ConversationMute.select('conversation_id').where(conversation_id: conversation_ids).where(account_id: account_id).map { |m| [m.conversation_id, true] }.to_h
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pins_map(status_ids, account_id)
|
||||||
|
StatusPin.select('status_id').where(status_id: status_ids).where(account_id: account_id).map { |p| [p.status_id, true] }.to_h
|
||||||
|
end
|
||||||
|
|
||||||
def reload_stale_associations!(cached_items)
|
def reload_stale_associations!(cached_items)
|
||||||
account_ids = []
|
account_ids = []
|
||||||
|
|
||||||
|
16
app/models/status_pin.rb
Normal file
16
app/models/status_pin.rb
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: status_pins
|
||||||
|
#
|
||||||
|
# id :integer not null, primary key
|
||||||
|
# account_id :integer not null
|
||||||
|
# status_id :integer not null
|
||||||
|
#
|
||||||
|
|
||||||
|
class StatusPin < ApplicationRecord
|
||||||
|
belongs_to :account, required: true
|
||||||
|
belongs_to :status, required: true
|
||||||
|
|
||||||
|
validates_with StatusPinValidator
|
||||||
|
end
|
@ -1,19 +1,24 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class StatusRelationshipsPresenter
|
class StatusRelationshipsPresenter
|
||||||
attr_reader :reblogs_map, :favourites_map, :mutes_map
|
attr_reader :reblogs_map, :favourites_map, :mutes_map, :pins_map
|
||||||
|
|
||||||
def initialize(statuses, current_account_id = nil, reblogs_map: {}, favourites_map: {}, mutes_map: {})
|
def initialize(statuses, current_account_id = nil, options = {})
|
||||||
if current_account_id.nil?
|
if current_account_id.nil?
|
||||||
@reblogs_map = {}
|
@reblogs_map = {}
|
||||||
@favourites_map = {}
|
@favourites_map = {}
|
||||||
@mutes_map = {}
|
@mutes_map = {}
|
||||||
|
@pins_map = {}
|
||||||
else
|
else
|
||||||
status_ids = statuses.compact.flat_map { |s| [s.id, s.reblog_of_id] }.uniq
|
statuses = statuses.compact
|
||||||
conversation_ids = statuses.compact.map(&:conversation_id).compact.uniq
|
status_ids = statuses.flat_map { |s| [s.id, s.reblog_of_id] }.uniq
|
||||||
@reblogs_map = Status.reblogs_map(status_ids, current_account_id).merge(reblogs_map)
|
conversation_ids = statuses.map(&:conversation_id).compact.uniq
|
||||||
@favourites_map = Status.favourites_map(status_ids, current_account_id).merge(favourites_map)
|
pinnable_status_ids = statuses.map(&:proper).select { |s| s.account_id == current_account_id && %w(public unlisted).include?(s.visibility) }.map(&:id)
|
||||||
@mutes_map = Status.mutes_map(conversation_ids, current_account_id).merge(mutes_map)
|
|
||||||
|
@reblogs_map = Status.reblogs_map(status_ids, current_account_id).merge(options[:reblogs_map] || {})
|
||||||
|
@favourites_map = Status.favourites_map(status_ids, current_account_id).merge(options[:favourites_map] || {})
|
||||||
|
@mutes_map = Status.mutes_map(conversation_ids, current_account_id).merge(options[:mutes_map] || {})
|
||||||
|
@pins_map = Status.pins_map(pinnable_status_ids, current_account_id).merge(options[:pins_map] || {})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -8,6 +8,7 @@ class REST::StatusSerializer < ActiveModel::Serializer
|
|||||||
attribute :favourited, if: :current_user?
|
attribute :favourited, if: :current_user?
|
||||||
attribute :reblogged, if: :current_user?
|
attribute :reblogged, if: :current_user?
|
||||||
attribute :muted, if: :current_user?
|
attribute :muted, if: :current_user?
|
||||||
|
attribute :pinned, if: :pinnable?
|
||||||
|
|
||||||
belongs_to :reblog, serializer: REST::StatusSerializer
|
belongs_to :reblog, serializer: REST::StatusSerializer
|
||||||
belongs_to :application
|
belongs_to :application
|
||||||
@ -57,6 +58,21 @@ class REST::StatusSerializer < ActiveModel::Serializer
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pinned
|
||||||
|
if instance_options && instance_options[:relationships]
|
||||||
|
instance_options[:relationships].pins_map[object.id] || false
|
||||||
|
else
|
||||||
|
current_user.account.pinned?(object)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def pinnable?
|
||||||
|
current_user? &&
|
||||||
|
current_user.account_id == object.account_id &&
|
||||||
|
!object.reblog? &&
|
||||||
|
%w(public unlisted).include?(object.visibility)
|
||||||
|
end
|
||||||
|
|
||||||
class ApplicationSerializer < ActiveModel::Serializer
|
class ApplicationSerializer < ActiveModel::Serializer
|
||||||
attributes :name, :website
|
attributes :name, :website
|
||||||
end
|
end
|
||||||
|
9
app/validators/status_pin_validator.rb
Normal file
9
app/validators/status_pin_validator.rb
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class StatusPinValidator < ActiveModel::Validator
|
||||||
|
def validate(pin)
|
||||||
|
pin.errors.add(:status, I18n.t('statuses.pin_errors.reblog')) if pin.status.reblog?
|
||||||
|
pin.errors.add(:status, I18n.t('statuses.pin_errors.ownership')) if pin.account_id != pin.status.account_id
|
||||||
|
pin.errors.add(:status, I18n.t('statuses.pin_errors.private')) unless %w(public unlisted).include?(pin.status.visibility)
|
||||||
|
end
|
||||||
|
end
|
@ -30,6 +30,9 @@
|
|||||||
= render 'nothing_here'
|
= render 'nothing_here'
|
||||||
- else
|
- else
|
||||||
.activity-stream.with-header
|
.activity-stream.with-header
|
||||||
|
- if params[:page].to_i.zero?
|
||||||
|
= render partial: 'stream_entries/status', collection: @pinned_statuses, as: :status, locals: { pinned: true }
|
||||||
|
|
||||||
= render partial: 'stream_entries/status', collection: @statuses, as: :status
|
= render partial: 'stream_entries/status', collection: @statuses, as: :status
|
||||||
|
|
||||||
- if @statuses.size == 20
|
- if @statuses.size == 20
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
:ruby
|
:ruby
|
||||||
|
pinned ||= false
|
||||||
include_threads ||= false
|
include_threads ||= false
|
||||||
is_predecessor ||= false
|
is_predecessor ||= false
|
||||||
is_successor ||= false
|
is_successor ||= false
|
||||||
@ -25,6 +26,12 @@
|
|||||||
= link_to TagManager.instance.url_for(status.account), class: 'status__display-name muted' do
|
= link_to TagManager.instance.url_for(status.account), class: 'status__display-name muted' do
|
||||||
%strong.emojify= display_name(status.account)
|
%strong.emojify= display_name(status.account)
|
||||||
= t('stream_entries.reblogged')
|
= t('stream_entries.reblogged')
|
||||||
|
- elsif pinned
|
||||||
|
.pre-header
|
||||||
|
.pre-header__icon
|
||||||
|
= fa_icon('thumb-tack fw')
|
||||||
|
%span
|
||||||
|
= t('stream_entries.pinned')
|
||||||
|
|
||||||
= render (centered ? 'stream_entries/detailed_status' : 'stream_entries/simple_status'), status: status.proper
|
= render (centered ? 'stream_entries/detailed_status' : 'stream_entries/simple_status'), status: status.proper
|
||||||
|
|
||||||
|
@ -434,6 +434,10 @@ en:
|
|||||||
statuses:
|
statuses:
|
||||||
open_in_web: Open in web
|
open_in_web: Open in web
|
||||||
over_character_limit: character limit of %{max} exceeded
|
over_character_limit: character limit of %{max} exceeded
|
||||||
|
pin_errors:
|
||||||
|
ownership: Someone else's toot cannot be pinned
|
||||||
|
private: Non-public toot cannot be pinned
|
||||||
|
reblog: A boost cannot be pinned
|
||||||
show_more: Show more
|
show_more: Show more
|
||||||
visibilities:
|
visibilities:
|
||||||
private: Followers-only
|
private: Followers-only
|
||||||
@ -444,6 +448,7 @@ en:
|
|||||||
unlisted_long: Everyone can see, but not listed on public timelines
|
unlisted_long: Everyone can see, but not listed on public timelines
|
||||||
stream_entries:
|
stream_entries:
|
||||||
click_to_show: Click to show
|
click_to_show: Click to show
|
||||||
|
pinned: Pinned toot
|
||||||
reblogged: boosted
|
reblogged: boosted
|
||||||
sensitive_content: Sensitive content
|
sensitive_content: Sensitive content
|
||||||
terms:
|
terms:
|
||||||
|
@ -162,6 +162,9 @@ Rails.application.routes.draw do
|
|||||||
|
|
||||||
resource :mute, only: :create
|
resource :mute, only: :create
|
||||||
post :unmute, to: 'mutes#destroy'
|
post :unmute, to: 'mutes#destroy'
|
||||||
|
|
||||||
|
resource :pin, only: :create
|
||||||
|
post :unpin, to: 'pins#destroy'
|
||||||
end
|
end
|
||||||
|
|
||||||
member do
|
member do
|
||||||
@ -175,6 +178,7 @@ Rails.application.routes.draw do
|
|||||||
resource :public, only: :show, controller: :public
|
resource :public, only: :show, controller: :public
|
||||||
resources :tag, only: :show
|
resources :tag, only: :show
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :streaming, only: [:index]
|
resources :streaming, only: [:index]
|
||||||
|
|
||||||
get '/search', to: 'search#index', as: :search
|
get '/search', to: 'search#index', as: :search
|
||||||
@ -210,6 +214,7 @@ Rails.application.routes.draw do
|
|||||||
resource :search, only: :show, controller: :search
|
resource :search, only: :show, controller: :search
|
||||||
resources :relationships, only: :index
|
resources :relationships, only: :index
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :accounts, only: [:show] do
|
resources :accounts, only: [:show] do
|
||||||
resources :statuses, only: :index, controller: 'accounts/statuses'
|
resources :statuses, only: :index, controller: 'accounts/statuses'
|
||||||
resources :followers, only: :index, controller: 'accounts/follower_accounts'
|
resources :followers, only: :index, controller: 'accounts/follower_accounts'
|
||||||
|
10
db/migrate/20170823162448_create_status_pins.rb
Normal file
10
db/migrate/20170823162448_create_status_pins.rb
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
class CreateStatusPins < ActiveRecord::Migration[5.1]
|
||||||
|
def change
|
||||||
|
create_table :status_pins do |t|
|
||||||
|
t.belongs_to :account, foreign_key: { on_delete: :cascade }, null: false
|
||||||
|
t.belongs_to :status, foreign_key: { on_delete: :cascade }, null: false
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :status_pins, [:account_id, :status_id], unique: true
|
||||||
|
end
|
||||||
|
end
|
12
db/schema.rb
12
db/schema.rb
@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20170720000000) do
|
ActiveRecord::Schema.define(version: 20170823162448) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
@ -282,6 +282,14 @@ ActiveRecord::Schema.define(version: 20170720000000) do
|
|||||||
t.index ["thing_type", "thing_id", "var"], name: "index_settings_on_thing_type_and_thing_id_and_var", unique: true
|
t.index ["thing_type", "thing_id", "var"], name: "index_settings_on_thing_type_and_thing_id_and_var", unique: true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "status_pins", force: :cascade do |t|
|
||||||
|
t.bigint "account_id", null: false
|
||||||
|
t.bigint "status_id", null: false
|
||||||
|
t.index ["account_id", "status_id"], name: "index_status_pins_on_account_id_and_status_id", unique: true
|
||||||
|
t.index ["account_id"], name: "index_status_pins_on_account_id"
|
||||||
|
t.index ["status_id"], name: "index_status_pins_on_status_id"
|
||||||
|
end
|
||||||
|
|
||||||
create_table "statuses", force: :cascade do |t|
|
create_table "statuses", force: :cascade do |t|
|
||||||
t.string "uri"
|
t.string "uri"
|
||||||
t.integer "account_id", null: false
|
t.integer "account_id", null: false
|
||||||
@ -430,6 +438,8 @@ ActiveRecord::Schema.define(version: 20170720000000) do
|
|||||||
add_foreign_key "reports", "accounts", on_delete: :cascade
|
add_foreign_key "reports", "accounts", on_delete: :cascade
|
||||||
add_foreign_key "session_activations", "oauth_access_tokens", column: "access_token_id", on_delete: :cascade
|
add_foreign_key "session_activations", "oauth_access_tokens", column: "access_token_id", on_delete: :cascade
|
||||||
add_foreign_key "session_activations", "users", on_delete: :cascade
|
add_foreign_key "session_activations", "users", on_delete: :cascade
|
||||||
|
add_foreign_key "status_pins", "accounts", on_delete: :cascade
|
||||||
|
add_foreign_key "status_pins", "statuses", on_delete: :cascade
|
||||||
add_foreign_key "statuses", "accounts", column: "in_reply_to_account_id", on_delete: :nullify
|
add_foreign_key "statuses", "accounts", column: "in_reply_to_account_id", on_delete: :nullify
|
||||||
add_foreign_key "statuses", "accounts", on_delete: :cascade
|
add_foreign_key "statuses", "accounts", on_delete: :cascade
|
||||||
add_foreign_key "statuses", "statuses", column: "in_reply_to_id", on_delete: :nullify
|
add_foreign_key "statuses", "statuses", column: "in_reply_to_id", on_delete: :nullify
|
||||||
|
@ -18,9 +18,8 @@ describe Api::V1::Accounts::StatusesController do
|
|||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
expect(response.headers['Link'].links.size).to eq(2)
|
expect(response.headers['Link'].links.size).to eq(2)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
describe 'GET #index with only media' do
|
context 'with only media' do
|
||||||
it 'returns http success' do
|
it 'returns http success' do
|
||||||
get :index, params: { account_id: user.account.id, only_media: true }
|
get :index, params: { account_id: user.account.id, only_media: true }
|
||||||
|
|
||||||
@ -28,11 +27,28 @@ describe Api::V1::Accounts::StatusesController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'GET #index with exclude replies' do
|
context 'with exclude replies' do
|
||||||
|
before do
|
||||||
|
Fabricate(:status, account: user.account, thread: Fabricate(:status))
|
||||||
|
end
|
||||||
|
|
||||||
it 'returns http success' do
|
it 'returns http success' do
|
||||||
get :index, params: { account_id: user.account.id, exclude_replies: true }
|
get :index, params: { account_id: user.account.id, exclude_replies: true }
|
||||||
|
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with only pinned' do
|
||||||
|
before do
|
||||||
|
Fabricate(:status_pin, account: user.account, status: Fabricate(:status, account: user.account))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns http success' do
|
||||||
|
get :index, params: { account_id: user.account.id, pinned: true }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
57
spec/controllers/api/v1/statuses/pins_controller_spec.rb
Normal file
57
spec/controllers/api/v1/statuses/pins_controller_spec.rb
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Api::V1::Statuses::PinsController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
|
||||||
|
let(:app) { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') }
|
||||||
|
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'write', application: app) }
|
||||||
|
|
||||||
|
context 'with an oauth token' do
|
||||||
|
before do
|
||||||
|
allow(controller).to receive(:doorkeeper_token) { token }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST #create' do
|
||||||
|
let(:status) { Fabricate(:status, account: user.account) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
post :create, params: { status_id: status.id }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns http success' do
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates the pinned attribute' do
|
||||||
|
expect(user.account.pinned?(status)).to be true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'return json with updated attributes' do
|
||||||
|
hash_body = body_as_json
|
||||||
|
|
||||||
|
expect(hash_body[:id]).to eq status.id
|
||||||
|
expect(hash_body[:pinned]).to be true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST #destroy' do
|
||||||
|
let(:status) { Fabricate(:status, account: user.account) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
Fabricate(:status_pin, status: status, account: user.account)
|
||||||
|
post :destroy, params: { status_id: status.id }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns http success' do
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates the pinned attribute' do
|
||||||
|
expect(user.account.pinned?(status)).to be false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
4
spec/fabricators/status_pin_fabricator.rb
Normal file
4
spec/fabricators/status_pin_fabricator.rb
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
Fabricator(:status_pin) do
|
||||||
|
account
|
||||||
|
status
|
||||||
|
end
|
41
spec/models/status_pin_spec.rb
Normal file
41
spec/models/status_pin_spec.rb
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe StatusPin, type: :model do
|
||||||
|
describe 'validations' do
|
||||||
|
it 'allows pins of own statuses' do
|
||||||
|
account = Fabricate(:account)
|
||||||
|
status = Fabricate(:status, account: account)
|
||||||
|
|
||||||
|
expect(StatusPin.new(account: account, status: status).save).to be true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not allow pins of statuses by someone else' do
|
||||||
|
account = Fabricate(:account)
|
||||||
|
status = Fabricate(:status)
|
||||||
|
|
||||||
|
expect(StatusPin.new(account: account, status: status).save).to be false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not allow pins of reblogs' do
|
||||||
|
account = Fabricate(:account)
|
||||||
|
status = Fabricate(:status, account: account)
|
||||||
|
reblog = Fabricate(:status, reblog: status)
|
||||||
|
|
||||||
|
expect(StatusPin.new(account: account, status: reblog).save).to be false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not allow pins of private statuses' do
|
||||||
|
account = Fabricate(:account)
|
||||||
|
status = Fabricate(:status, account: account, visibility: :private)
|
||||||
|
|
||||||
|
expect(StatusPin.new(account: account, status: status).save).to be false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not allow pins of direct statuses' do
|
||||||
|
account = Fabricate(:account)
|
||||||
|
status = Fabricate(:status, account: account, visibility: :direct)
|
||||||
|
|
||||||
|
expect(StatusPin.new(account: account, status: status).save).to be false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user