Add status reference menu
This commit is contained in:
parent
c656f41b35
commit
44a987810b
8 changed files with 84 additions and 3 deletions
|
@ -62,6 +62,7 @@ export const COMPOSE_LANGUAGE_CHANGE = 'COMPOSE_LANGUAGE_CHANGE';
|
||||||
|
|
||||||
export const COMPOSE_EMOJI_INSERT = 'COMPOSE_EMOJI_INSERT';
|
export const COMPOSE_EMOJI_INSERT = 'COMPOSE_EMOJI_INSERT';
|
||||||
export const COMPOSE_EXPIRATION_INSERT = 'COMPOSE_EXPIRATION_INSERT';
|
export const COMPOSE_EXPIRATION_INSERT = 'COMPOSE_EXPIRATION_INSERT';
|
||||||
|
export const COMPOSE_REFERENCE_INSERT = 'COMPOSE_REFERENCE_INSERT';
|
||||||
|
|
||||||
export const COMPOSE_UPLOAD_CHANGE_REQUEST = 'COMPOSE_UPLOAD_UPDATE_REQUEST';
|
export const COMPOSE_UPLOAD_CHANGE_REQUEST = 'COMPOSE_UPLOAD_UPDATE_REQUEST';
|
||||||
export const COMPOSE_UPLOAD_CHANGE_SUCCESS = 'COMPOSE_UPLOAD_UPDATE_SUCCESS';
|
export const COMPOSE_UPLOAD_CHANGE_SUCCESS = 'COMPOSE_UPLOAD_UPDATE_SUCCESS';
|
||||||
|
@ -770,6 +771,14 @@ export function insertExpirationCompose(position, data) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function insertReferenceCompose(position, url) {
|
||||||
|
return {
|
||||||
|
type: COMPOSE_REFERENCE_INSERT,
|
||||||
|
position,
|
||||||
|
url,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function changeComposing(value) {
|
export function changeComposing(value) {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_COMPOSING_CHANGE,
|
type: COMPOSE_COMPOSING_CHANGE,
|
||||||
|
|
|
@ -49,6 +49,7 @@ const messages = defineMessages({
|
||||||
admin_status: { id: 'status.admin_status', defaultMessage: 'Open this post in the moderation interface' },
|
admin_status: { id: 'status.admin_status', defaultMessage: 'Open this post in the moderation interface' },
|
||||||
admin_domain: { id: 'status.admin_domain', defaultMessage: 'Open moderation interface for {domain}' },
|
admin_domain: { id: 'status.admin_domain', defaultMessage: 'Open moderation interface for {domain}' },
|
||||||
copy: { id: 'status.copy', defaultMessage: 'Copy link to post' },
|
copy: { id: 'status.copy', defaultMessage: 'Copy link to post' },
|
||||||
|
reference: { id: 'status.reference', defaultMessage: 'Add reference' },
|
||||||
hide: { id: 'status.hide', defaultMessage: 'Hide post' },
|
hide: { id: 'status.hide', defaultMessage: 'Hide post' },
|
||||||
blockDomain: { id: 'account.block_domain', defaultMessage: 'Block domain {domain}' },
|
blockDomain: { id: 'account.block_domain', defaultMessage: 'Block domain {domain}' },
|
||||||
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' },
|
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' },
|
||||||
|
@ -251,6 +252,10 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
navigator.clipboard.writeText(url);
|
navigator.clipboard.writeText(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleReference = () => {
|
||||||
|
this.props.onReference(this.props.status);
|
||||||
|
};
|
||||||
|
|
||||||
handleHideClick = () => {
|
handleHideClick = () => {
|
||||||
this.props.onFilter();
|
this.props.onFilter();
|
||||||
};
|
};
|
||||||
|
@ -289,6 +294,11 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
menu.push(null);
|
menu.push(null);
|
||||||
|
|
||||||
menu.push({ text: intl.formatMessage(status.get('reblogged') ? messages.cancelReblog : messages.reblog), action: this.handleReblogForceModalClick });
|
menu.push({ text: intl.formatMessage(status.get('reblogged') ? messages.cancelReblog : messages.reblog), action: this.handleReblogForceModalClick });
|
||||||
|
|
||||||
|
if (publicStatus) {
|
||||||
|
menu.push({ text: intl.formatMessage(messages.reference), action: this.handleReference });
|
||||||
|
}
|
||||||
|
|
||||||
menu.push({ text: intl.formatMessage(status.get('bookmarked') ? messages.removeBookmark : messages.bookmark), action: this.handleBookmarkClick });
|
menu.push({ text: intl.formatMessage(status.get('bookmarked') ? messages.removeBookmark : messages.bookmark), action: this.handleBookmarkClick });
|
||||||
|
|
||||||
if (writtenByMe && pinnableStatus) {
|
if (writtenByMe && pinnableStatus) {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
replyCompose,
|
replyCompose,
|
||||||
mentionCompose,
|
mentionCompose,
|
||||||
directCompose,
|
directCompose,
|
||||||
|
insertReferenceCompose,
|
||||||
} from '../actions/compose';
|
} from '../actions/compose';
|
||||||
import {
|
import {
|
||||||
blockDomain,
|
blockDomain,
|
||||||
|
@ -192,6 +193,10 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onReference (status) {
|
||||||
|
dispatch(insertReferenceCompose(0, status.get('url')));
|
||||||
|
},
|
||||||
|
|
||||||
onTranslate (status) {
|
onTranslate (status) {
|
||||||
if (status.get('translation')) {
|
if (status.get('translation')) {
|
||||||
dispatch(undoStatusTranslation(status.get('id'), status.get('poll')));
|
dispatch(undoStatusTranslation(status.get('id'), status.get('poll')));
|
||||||
|
|
|
@ -42,6 +42,7 @@ const messages = defineMessages({
|
||||||
admin_status: { id: 'status.admin_status', defaultMessage: 'Open this post in the moderation interface' },
|
admin_status: { id: 'status.admin_status', defaultMessage: 'Open this post in the moderation interface' },
|
||||||
admin_domain: { id: 'status.admin_domain', defaultMessage: 'Open moderation interface for {domain}' },
|
admin_domain: { id: 'status.admin_domain', defaultMessage: 'Open moderation interface for {domain}' },
|
||||||
copy: { id: 'status.copy', defaultMessage: 'Copy link to post' },
|
copy: { id: 'status.copy', defaultMessage: 'Copy link to post' },
|
||||||
|
reference: { id: 'status.reference', defaultMessage: 'Add reference' },
|
||||||
blockDomain: { id: 'account.block_domain', defaultMessage: 'Block domain {domain}' },
|
blockDomain: { id: 'account.block_domain', defaultMessage: 'Block domain {domain}' },
|
||||||
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' },
|
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' },
|
||||||
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
|
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
|
||||||
|
@ -69,6 +70,7 @@ class ActionBar extends PureComponent {
|
||||||
onReblogForceModal: PropTypes.func.isRequired,
|
onReblogForceModal: PropTypes.func.isRequired,
|
||||||
onFavourite: PropTypes.func.isRequired,
|
onFavourite: PropTypes.func.isRequired,
|
||||||
onEmojiReact: PropTypes.func.isRequired,
|
onEmojiReact: PropTypes.func.isRequired,
|
||||||
|
onReference: PropTypes.func.isRequired,
|
||||||
onBookmark: PropTypes.func.isRequired,
|
onBookmark: PropTypes.func.isRequired,
|
||||||
onDelete: PropTypes.func.isRequired,
|
onDelete: PropTypes.func.isRequired,
|
||||||
onEdit: PropTypes.func.isRequired,
|
onEdit: PropTypes.func.isRequired,
|
||||||
|
@ -190,6 +192,10 @@ class ActionBar extends PureComponent {
|
||||||
navigator.clipboard.writeText(url);
|
navigator.clipboard.writeText(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleReference = () => {
|
||||||
|
this.props.onReference(this.props.status);
|
||||||
|
};
|
||||||
|
|
||||||
handleEmojiPick = (data) => {
|
handleEmojiPick = (data) => {
|
||||||
this.props.onEmojiReact(this.props.status, data);
|
this.props.onEmojiReact(this.props.status, data);
|
||||||
};
|
};
|
||||||
|
@ -227,6 +233,11 @@ class ActionBar extends PureComponent {
|
||||||
|
|
||||||
menu.push(null);
|
menu.push(null);
|
||||||
menu.push({ text: intl.formatMessage(messages.reblog), action: this.handleReblogForceModalClick });
|
menu.push({ text: intl.formatMessage(messages.reblog), action: this.handleReblogForceModalClick });
|
||||||
|
|
||||||
|
if (publicStatus) {
|
||||||
|
menu.push({ text: intl.formatMessage(messages.reference), action: this.handleReference });
|
||||||
|
}
|
||||||
|
|
||||||
menu.push(null);
|
menu.push(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ import {
|
||||||
replyCompose,
|
replyCompose,
|
||||||
mentionCompose,
|
mentionCompose,
|
||||||
directCompose,
|
directCompose,
|
||||||
|
insertReferenceCompose,
|
||||||
} from '../../actions/compose';
|
} from '../../actions/compose';
|
||||||
import {
|
import {
|
||||||
blockDomain,
|
blockDomain,
|
||||||
|
@ -356,6 +357,10 @@ class Status extends ImmutablePureComponent {
|
||||||
this.handleReblogClick(status, e, true);
|
this.handleReblogClick(status, e, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleReference = (status) => {
|
||||||
|
this.props.dispatch(insertReferenceCompose(0, status.get('url')));
|
||||||
|
};
|
||||||
|
|
||||||
handleBookmarkClick = (status) => {
|
handleBookmarkClick = (status) => {
|
||||||
if (status.get('bookmarked')) {
|
if (status.get('bookmarked')) {
|
||||||
this.props.dispatch(unbookmark(status));
|
this.props.dispatch(unbookmark(status));
|
||||||
|
@ -717,6 +722,7 @@ class Status extends ImmutablePureComponent {
|
||||||
onEmojiReact={this.handleEmojiReact}
|
onEmojiReact={this.handleEmojiReact}
|
||||||
onReblog={this.handleReblogClick}
|
onReblog={this.handleReblogClick}
|
||||||
onReblogForceModal={this.handleReblogForceModalClick}
|
onReblogForceModal={this.handleReblogForceModalClick}
|
||||||
|
onReference={this.handleReference}
|
||||||
onBookmark={this.handleBookmarkClick}
|
onBookmark={this.handleBookmarkClick}
|
||||||
onDelete={this.handleDeleteClick}
|
onDelete={this.handleDeleteClick}
|
||||||
onEdit={this.handleEditClick}
|
onEdit={this.handleEditClick}
|
||||||
|
|
|
@ -419,6 +419,7 @@
|
||||||
"notification.poll": "A poll you have voted in has ended",
|
"notification.poll": "A poll you have voted in has ended",
|
||||||
"notification.reblog": "{name} boosted your post",
|
"notification.reblog": "{name} boosted your post",
|
||||||
"notification.status": "{name} just posted",
|
"notification.status": "{name} just posted",
|
||||||
|
"notification.status_reference": "{name} refered your post",
|
||||||
"notification.update": "{name} edited a post",
|
"notification.update": "{name} edited a post",
|
||||||
"notifications.clear": "Clear notifications",
|
"notifications.clear": "Clear notifications",
|
||||||
"notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?",
|
"notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?",
|
||||||
|
|
|
@ -36,6 +36,7 @@ import {
|
||||||
COMPOSE_COMPOSING_CHANGE,
|
COMPOSE_COMPOSING_CHANGE,
|
||||||
COMPOSE_EMOJI_INSERT,
|
COMPOSE_EMOJI_INSERT,
|
||||||
COMPOSE_EXPIRATION_INSERT,
|
COMPOSE_EXPIRATION_INSERT,
|
||||||
|
COMPOSE_REFERENCE_INSERT,
|
||||||
COMPOSE_UPLOAD_CHANGE_REQUEST,
|
COMPOSE_UPLOAD_CHANGE_REQUEST,
|
||||||
COMPOSE_UPLOAD_CHANGE_SUCCESS,
|
COMPOSE_UPLOAD_CHANGE_SUCCESS,
|
||||||
COMPOSE_UPLOAD_CHANGE_FAIL,
|
COMPOSE_UPLOAD_CHANGE_FAIL,
|
||||||
|
@ -238,6 +239,41 @@ const insertExpiration = (state, position, data) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const insertReference = (state, url) => {
|
||||||
|
const oldText = state.get('text');
|
||||||
|
|
||||||
|
if (oldText.indexOf(`BT ${url}`) >= 0) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
let newLine = '\n\n';
|
||||||
|
if (oldText.length === 0) newLine = '';
|
||||||
|
else if (oldText[oldText.length - 1] === '\n') {
|
||||||
|
if (oldText.length === 1 || oldText[oldText.length - 2] === '\n') {
|
||||||
|
newLine = '';
|
||||||
|
} else {
|
||||||
|
newLine = '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldText.length > 0) {
|
||||||
|
const lastLine = oldText.slice(oldText.lastIndexOf('\n') + 1, oldText.length - 1);
|
||||||
|
if (lastLine.startsWith('BT ')) {
|
||||||
|
newLine = '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const referenceText = `${newLine}BT ${url}`;
|
||||||
|
const text = `${oldText}${referenceText}`;
|
||||||
|
|
||||||
|
return state.merge({
|
||||||
|
text,
|
||||||
|
focusDate: new Date(),
|
||||||
|
caretPosition: text.length - referenceText.length,
|
||||||
|
idempotencyKey: uuid(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const privacyPreference = (a, b) => {
|
const privacyPreference = (a, b) => {
|
||||||
const order = ['public', 'public_unlisted', 'unlisted', 'login', 'private', 'direct'];
|
const order = ['public', 'public_unlisted', 'unlisted', 'login', 'private', 'direct'];
|
||||||
return order[Math.max(order.indexOf(a), order.indexOf(b), 0)];
|
return order[Math.max(order.indexOf(a), order.indexOf(b), 0)];
|
||||||
|
@ -476,6 +512,8 @@ export default function compose(state = initialState, action) {
|
||||||
return insertEmoji(state, action.position, action.emoji, action.needsSpace);
|
return insertEmoji(state, action.position, action.emoji, action.needsSpace);
|
||||||
case COMPOSE_EXPIRATION_INSERT:
|
case COMPOSE_EXPIRATION_INSERT:
|
||||||
return insertExpiration(state, action.position, action.data);
|
return insertExpiration(state, action.position, action.data);
|
||||||
|
case COMPOSE_REFERENCE_INSERT:
|
||||||
|
return insertReference(state, action.url);
|
||||||
case COMPOSE_UPLOAD_CHANGE_SUCCESS:
|
case COMPOSE_UPLOAD_CHANGE_SUCCESS:
|
||||||
return state
|
return state
|
||||||
.set('is_changing_upload', false)
|
.set('is_changing_upload', false)
|
||||||
|
|
|
@ -4,7 +4,7 @@ class ProcessReferencesService < BaseService
|
||||||
include Payloadable
|
include Payloadable
|
||||||
|
|
||||||
DOMAIN = ENV['WEB_DOMAIN'] || ENV.fetch('LOCAL_DOMAIN', nil)
|
DOMAIN = ENV['WEB_DOMAIN'] || ENV.fetch('LOCAL_DOMAIN', nil)
|
||||||
REFURL_EXP = /(RT|QT|BT|RN)((:)? +|:)(#{URI::DEFAULT_PARSER.make_regexp(%w(http https))})/
|
REFURL_EXP = /(RT|QT|BT|RN|RE)((:)? +|:)(#{URI::DEFAULT_PARSER.make_regexp(%w(http https))})/
|
||||||
STATUSID_EXP = %r{(http|https)://#{DOMAIN}/@[a-zA-Z0-9]+/([0-9]{16,})}
|
STATUSID_EXP = %r{(http|https)://#{DOMAIN}/@[a-zA-Z0-9]+/([0-9]{16,})}
|
||||||
|
|
||||||
def call(status, reference_parameters, save_records: true, urls: nil)
|
def call(status, reference_parameters, save_records: true, urls: nil)
|
||||||
|
@ -69,9 +69,10 @@ class ProcessReferencesService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_notifications!
|
def create_notifications!
|
||||||
return if @added_objects.empty?
|
local_reference_objects = @added_objects.filter { |ref| ref.target_status.account.local? }
|
||||||
|
return if local_reference_objects.empty?
|
||||||
|
|
||||||
LocalNotificationWorker.push_bulk(@added_objects) do |ref|
|
LocalNotificationWorker.push_bulk(local_reference_objects) do |ref|
|
||||||
[ref.target_status.account_id, ref.id, 'StatusReference', 'status_reference']
|
[ref.target_status.account_id, ref.id, 'StatusReference', 'status_reference']
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue