@@ -7,14 +7,17 @@ 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 | ||||
@statuses = filtered_statuses.paginate_by_max_id(20, params[:max_id], params[:since_id]) | |||||
@statuses = cache_collection(@statuses, Status) | |||||
@next_url = next_url unless @statuses.empty? | |||||
@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 = cache_collection(@statuses, Status) | |||||
@next_url = next_url unless @statuses.empty? | |||||
end | end | ||||
format.atom do | format.atom do | ||||
@@ -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!(no_replies_scope) unless request.path.ends_with?('/with_replies') | |||||
statuses.merge!(only_media_scope) if media_requested? | |||||
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 | ||||
@@ -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 = [] | ||||
@@ -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 | |||||
conversation_ids = statuses.compact.map(&:conversation_id).compact.uniq | |||||
@reblogs_map = Status.reblogs_map(status_ids, current_account_id).merge(reblogs_map) | |||||
@favourites_map = Status.favourites_map(status_ids, current_account_id).merge(favourites_map) | |||||
@mutes_map = Status.mutes_map(conversation_ids, current_account_id).merge(mutes_map) | |||||
statuses = statuses.compact | |||||
status_ids = statuses.flat_map { |s| [s.id, s.reblog_of_id] }.uniq | |||||
conversation_ids = statuses.map(&:conversation_id).compact.uniq | |||||
pinnable_status_ids = statuses.map(&:proper).select { |s| s.account_id == current_account_id && %w(public unlisted).include?(s.visibility) }.map(&:id) | |||||
@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 | ||||
@@ -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,7 +178,8 @@ 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' | ||||
@@ -245,7 +250,7 @@ Rails.application.routes.draw do | |||||
root 'home#index' | root 'home#index' | ||||
match '*unmatched_route', | match '*unmatched_route', | ||||
via: :all, | |||||
to: 'application#raise_not_found', | |||||
format: false | |||||
via: :all, | |||||
to: 'application#raise_not_found', | |||||
format: false | |||||
end | end |
@@ -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 |
@@ -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,21 +18,37 @@ 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 | |||||
it 'returns http success' do | |||||
get :index, params: { account_id: user.account.id, only_media: true } | |||||
context 'with only media' do | |||||
it 'returns http success' do | |||||
get :index, params: { account_id: user.account.id, only_media: true } | |||||
expect(response).to have_http_status(:success) | |||||
expect(response).to have_http_status(:success) | |||||
end | |||||
end | end | ||||
end | |||||
describe 'GET #index with exclude replies' do | |||||
it 'returns http success' do | |||||
get :index, params: { account_id: user.account.id, exclude_replies: true } | |||||
context 'with exclude replies' do | |||||
before do | |||||
Fabricate(:status, account: user.account, thread: Fabricate(:status)) | |||||
end | |||||
expect(response).to have_http_status(:success) | |||||
it 'returns http success' do | |||||
get :index, params: { account_id: user.account.id, exclude_replies: true } | |||||
expect(response).to have_http_status(:success) | |||||
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 | ||||
end | end |
@@ -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 |
@@ -0,0 +1,4 @@ | |||||
Fabricator(:status_pin) do | |||||
account | |||||
status | |||||
end |
@@ -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 |