Adding full Article support
This creates a new column in the `statuses` table which keeps track of activity_pub_type, so in the case of a Note it will be blank (the default) and it will be a string "Article" if the received remote object is an AP Article. There is now a bunch of special case code in the formatters and sanitizers to handle Articles differently, as well as on the clientside.
This commit is contained in:
parent
b3e65978b4
commit
0436aa9984
@ -409,7 +409,7 @@ class Status extends ImmutablePureComponent {
|
||||
|
||||
return (
|
||||
<HotKeys handlers={handlers}>
|
||||
<div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id'), read: unread === false, focusable: !this.props.muted })} tabIndex={this.props.muted ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText)} ref={this.handleRef}>
|
||||
<div className={classNames('status__wrapper', `status__wrapper-type-${status.get('activity_pub_type') || 'none'}` , `status__wrapper-${status.get('visibility')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id'), read: unread === false, focusable: !this.props.muted })} tabIndex={this.props.muted ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText)} ref={this.handleRef}>
|
||||
{prepend}
|
||||
|
||||
<div className={classNames('status', `status-${status.get('visibility')}`, { 'status-reply': !!status.get('in_reply_to_id'), muted: this.props.muted, read: unread === false })} data-id={status.get('id')}>
|
||||
|
@ -170,6 +170,12 @@ export default class StatusContent extends React.PureComponent {
|
||||
</button>
|
||||
);
|
||||
|
||||
const readArticleButton = (
|
||||
<button className='status__content__read-more-button' onClick={this.props.onClick} key='read-more'>
|
||||
<FormattedMessage id='status.read_article' defaultMessage='Read article' /><Icon id='angle-right' fixedWidth />
|
||||
</button>
|
||||
);
|
||||
|
||||
if (status.get('spoiler_text').length > 0) {
|
||||
let mentionsPlaceholder = '';
|
||||
|
||||
@ -185,21 +191,26 @@ export default class StatusContent extends React.PureComponent {
|
||||
mentionsPlaceholder = <div>{mentionLinks}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
const output = [
|
||||
<div className={classNames} ref={this.setRef} tabIndex='0' style={directionStyle} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}>
|
||||
<p style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}>
|
||||
<span dangerouslySetInnerHTML={spoilerContent} lang={status.get('language')} />
|
||||
{' '}
|
||||
<button tabIndex='0' className={`status__content__spoiler-link ${hidden ? 'status__content__spoiler-link--show-more' : 'status__content__spoiler-link--show-less'}`} onClick={this.handleSpoilerClick}>{toggleText}</button>
|
||||
{status.get('activity_pub_type') === 'Article' ? '' : <div><button tabIndex='0' className={`status__content__spoiler-link ${hidden ? 'status__content__spoiler-link--show-more' : 'status__content__spoiler-link--show-less'}`} onClick={this.handleSpoilerClick}>{toggleText}</button></div>}
|
||||
</p>
|
||||
|
||||
{mentionsPlaceholder}
|
||||
|
||||
<div tabIndex={!hidden ? 0 : null} className={`status__content__text ${!hidden ? 'status__content__text--visible' : ''}`} style={directionStyle} dangerouslySetInnerHTML={content} lang={status.get('language')} />
|
||||
|
||||
{!hidden && !!status.get('poll') && <PollContainer pollId={status.get('poll')} />}
|
||||
</div>
|
||||
);
|
||||
</div>,
|
||||
];
|
||||
|
||||
if (status.get('activity_pub_type') === 'Article' && !this.props.expanded) {
|
||||
output.push(readArticleButton);
|
||||
}
|
||||
|
||||
return output;
|
||||
} else if (this.props.onClick) {
|
||||
const output = [
|
||||
<div
|
||||
|
@ -213,7 +213,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
|
||||
<DisplayName account={status.get('account')} localDomain={this.props.domain} />
|
||||
</a>
|
||||
|
||||
<StatusContent status={status} expanded={!status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} />
|
||||
<StatusContent status={status} expanded={status.get('activity_pub_type') === 'Article' || !status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} />
|
||||
|
||||
{media}
|
||||
|
||||
|
@ -773,6 +773,7 @@
|
||||
ul,
|
||||
ol {
|
||||
margin-left: 1em;
|
||||
margin-bottom: 1em;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
@ -840,6 +841,10 @@
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.article-type img {
|
||||
max-width: 95%;
|
||||
}
|
||||
}
|
||||
|
||||
.status__content.status__content--collapsed {
|
||||
|
@ -60,7 +60,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||
account: @account,
|
||||
text: text_from_content || '',
|
||||
language: detected_language,
|
||||
spoiler_text: converted_object_type? ? '' : (text_from_summary || ''),
|
||||
spoiler_text: converted_object_type? ? '' : (text_from_summary || (@object['type'] == 'Article' && text_from_name) || ''),
|
||||
created_at: @object['published'],
|
||||
override_timestamps: @options[:override_timestamps],
|
||||
reply: @object['inReplyTo'].present?,
|
||||
@ -70,6 +70,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||
conversation: conversation_from_uri(@object['conversation']),
|
||||
media_attachment_ids: process_attachments.take(4).map(&:id),
|
||||
poll: process_poll,
|
||||
activity_pub_type: @object['type']
|
||||
}
|
||||
end
|
||||
end
|
||||
|
@ -92,6 +92,7 @@ class Formatter
|
||||
|
||||
def format_article(text)
|
||||
text = text.gsub(/>\n+</, "><")
|
||||
text = "<span class='article-type'>#{text}</span>"
|
||||
text.html_safe # rubocop:disable Rails/OutputSafety
|
||||
end
|
||||
|
||||
|
@ -49,6 +49,9 @@ class Sanitize
|
||||
'rel' => 'nofollow noopener',
|
||||
'target' => '_blank',
|
||||
},
|
||||
'span' => {
|
||||
'class' => 'article-type',
|
||||
},
|
||||
},
|
||||
|
||||
protocols: {
|
||||
|
@ -4,7 +4,7 @@ class REST::StatusSerializer < ActiveModel::Serializer
|
||||
attributes :id, :created_at, :in_reply_to_id, :in_reply_to_account_id,
|
||||
:sensitive, :spoiler_text, :visibility, :language,
|
||||
:uri, :url, :replies_count, :reblogs_count,
|
||||
:favourites_count, :local_only
|
||||
:favourites_count, :local_only, :activity_pub_type
|
||||
|
||||
attribute :favourited, if: :current_user?
|
||||
attribute :reblogged, if: :current_user?
|
||||
@ -61,6 +61,10 @@ class REST::StatusSerializer < ActiveModel::Serializer
|
||||
OStatus::TagManager.instance.uri_for(object)
|
||||
end
|
||||
|
||||
def activity_pub_type
|
||||
object.activity_pub_type.to_s
|
||||
end
|
||||
|
||||
def content
|
||||
Formatter.instance.format(object)
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user