diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/controllers/application_controller.rb | 1 | ||||
-rw-r--r-- | app/javascript/mastodon/components/status.js | 9 | ||||
-rw-r--r-- | app/javascript/mastodon/features/emoji/emoji_mart_search_light.js | 22 | ||||
-rw-r--r-- | app/javascript/mastodon/features/status/components/card.js | 18 | ||||
-rw-r--r-- | app/javascript/mastodon/features/status/containers/card_container.js | 2 | ||||
-rw-r--r-- | app/javascript/mastodon/reducers/index.js | 2 | ||||
-rw-r--r-- | app/javascript/mastodon/reducers/statuses.js | 3 | ||||
-rw-r--r-- | app/javascript/styles/mastodon/components.scss | 29 | ||||
-rw-r--r-- | app/models/media_attachment.rb | 6 | ||||
-rw-r--r-- | app/models/status.rb | 10 | ||||
-rw-r--r-- | app/models/status_stat.rb | 8 | ||||
-rw-r--r-- | app/serializers/rest/status_serializer.rb | 2 | ||||
-rw-r--r-- | app/services/fetch_link_card_service.rb | 1 |
13 files changed, 91 insertions, 22 deletions
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index dca6c5a5a..983b116c9 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -201,6 +201,7 @@ class ApplicationController < ActionController::Base def respond_with_error(code) respond_to do |format| format.any { head code } + format.html do set_locale use_pack 'error' diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js index 0b23e51f8..9fa8cc008 100644 --- a/app/javascript/mastodon/components/status.js +++ b/app/javascript/mastodon/components/status.js @@ -9,6 +9,7 @@ import DisplayName from './display_name'; import StatusContent from './status_content'; import StatusActionBar from './status_action_bar'; import AttachmentList from './attachment_list'; +import Card from '../features/status/components/card'; import { injectIntl, FormattedMessage } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { MediaGallery, Video } from '../features/ui/util/async-components'; @@ -256,6 +257,14 @@ class Status extends ImmutablePureComponent { </Bundle> ); } + } else if (status.get('spoiler_text').length === 0 && status.get('card')) { + media = ( + <Card + onOpenMedia={this.props.onOpenMedia} + card={status.get('card')} + compact + /> + ); } if (otherAccounts) { diff --git a/app/javascript/mastodon/features/emoji/emoji_mart_search_light.js b/app/javascript/mastodon/features/emoji/emoji_mart_search_light.js index 36351ec02..164fdcc0b 100644 --- a/app/javascript/mastodon/features/emoji/emoji_mart_search_light.js +++ b/app/javascript/mastodon/features/emoji/emoji_mart_search_light.js @@ -2,7 +2,7 @@ // https://github.com/missive/emoji-mart/blob/5f2ffcc/src/utils/emoji-index.js import data from './emoji_mart_data_light'; -import { getData, getSanitizedData, intersect } from './emoji_utils'; +import { getData, getSanitizedData, uniq, intersect } from './emoji_utils'; let originalPool = {}; let index = {}; @@ -103,7 +103,7 @@ function search(value, { emojisToShowFilter, maxResults, include, exclude, custo } } - allResults = values.map((value) => { + const searchValue = (value) => { let aPool = pool, aIndex = index, length = 0; @@ -150,15 +150,23 @@ function search(value, { emojisToShowFilter, maxResults, include, exclude, custo } return aIndex.results; - }).filter(a => a); + }; - if (allResults.length > 1) { - results = intersect.apply(null, allResults); - } else if (allResults.length) { - results = allResults[0]; + if (values.length > 1) { + results = searchValue(value); } else { results = []; } + + allResults = values.map(searchValue).filter(a => a); + + if (allResults.length > 1) { + allResults = intersect.apply(null, allResults); + } else if (allResults.length) { + allResults = allResults[0]; + } + + results = uniq(results.concat(allResults)); } if (results) { diff --git a/app/javascript/mastodon/features/status/components/card.js b/app/javascript/mastodon/features/status/components/card.js index b52f3c4fa..9a87f7a3f 100644 --- a/app/javascript/mastodon/features/status/components/card.js +++ b/app/javascript/mastodon/features/status/components/card.js @@ -59,10 +59,12 @@ export default class Card extends React.PureComponent { card: ImmutablePropTypes.map, maxDescription: PropTypes.number, onOpenMedia: PropTypes.func.isRequired, + compact: PropTypes.boolean, }; static defaultProps = { maxDescription: 50, + compact: false, }; state = { @@ -131,25 +133,25 @@ export default class Card extends React.PureComponent { } render () { - const { card, maxDescription } = this.props; - const { width, embedded } = this.state; + const { card, maxDescription, compact } = this.props; + const { width, embedded } = this.state; if (card === null) { return null; } const provider = card.get('provider_name').length === 0 ? decodeIDNA(getHostname(card.get('url'))) : card.get('provider_name'); - const horizontal = card.get('width') > card.get('height') && (card.get('width') + 100 >= width) || card.get('type') !== 'link'; - const className = classnames('status-card', { horizontal }); + const horizontal = (!compact && card.get('width') > card.get('height') && (card.get('width') + 100 >= width)) || card.get('type') !== 'link' || embedded; const interactive = card.get('type') !== 'link'; + const className = classnames('status-card', { horizontal, compact, interactive }); const title = interactive ? <a className='status-card__title' href={card.get('url')} title={card.get('title')} rel='noopener' target='_blank'><strong>{card.get('title')}</strong></a> : <strong className='status-card__title' title={card.get('title')}>{card.get('title')}</strong>; - const ratio = card.get('width') / card.get('height'); + const ratio = compact ? 16 / 9 : card.get('width') / card.get('height'); const height = card.get('width') > card.get('height') ? (width / ratio) : (width * ratio); const description = ( <div className='status-card__content'> {title} - {!horizontal && <p className='status-card__description'>{trim(card.get('description') || '', maxDescription)}</p>} + {!(horizontal || compact) && <p className='status-card__description'>{trim(card.get('description') || '', maxDescription)}</p>} <span className='status-card__host'>{provider}</span> </div> ); @@ -174,7 +176,7 @@ export default class Card extends React.PureComponent { <div className='status-card__actions'> <div> <button onClick={this.handleEmbedClick}><i className={`fa fa-${iconVariant}`} /></button> - <a href={card.get('url')} target='_blank' rel='noopener'><i className='fa fa-external-link' /></a> + {horizontal && <a href={card.get('url')} target='_blank' rel='noopener'><i className='fa fa-external-link' /></a>} </div> </div> </div> @@ -184,7 +186,7 @@ export default class Card extends React.PureComponent { return ( <div className={className} ref={this.setRef}> {embed} - {description} + {!compact && description} </div> ); } else if (card.get('image')) { diff --git a/app/javascript/mastodon/features/status/containers/card_container.js b/app/javascript/mastodon/features/status/containers/card_container.js index a97404de1..6170d9fd8 100644 --- a/app/javascript/mastodon/features/status/containers/card_container.js +++ b/app/javascript/mastodon/features/status/containers/card_container.js @@ -2,7 +2,7 @@ import { connect } from 'react-redux'; import Card from '../components/card'; const mapStateToProps = (state, { statusId }) => ({ - card: state.getIn(['cards', statusId], null), + card: state.getIn(['statuses', statusId, 'card'], null), }); export default connect(mapStateToProps)(Card); diff --git a/app/javascript/mastodon/reducers/index.js b/app/javascript/mastodon/reducers/index.js index e98566e26..2c98af1db 100644 --- a/app/javascript/mastodon/reducers/index.js +++ b/app/javascript/mastodon/reducers/index.js @@ -14,7 +14,6 @@ import relationships from './relationships'; import settings from './settings'; import push_notifications from './push_notifications'; import status_lists from './status_lists'; -import cards from './cards'; import mutes from './mutes'; import reports from './reports'; import contexts from './contexts'; @@ -46,7 +45,6 @@ const reducers = { relationships, settings, push_notifications, - cards, mutes, reports, contexts, diff --git a/app/javascript/mastodon/reducers/statuses.js b/app/javascript/mastodon/reducers/statuses.js index 6e3d830da..2c58969f3 100644 --- a/app/javascript/mastodon/reducers/statuses.js +++ b/app/javascript/mastodon/reducers/statuses.js @@ -10,6 +10,7 @@ import { STATUS_REVEAL, STATUS_HIDE, } from '../actions/statuses'; +import { STATUS_CARD_FETCH_SUCCESS } from '../actions/cards'; import { TIMELINE_DELETE } from '../actions/timelines'; import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer'; import { Map as ImmutableMap, fromJS } from 'immutable'; @@ -65,6 +66,8 @@ export default function statuses(state = initialState, action) { }); case TIMELINE_DELETE: return deleteStatus(state, action.id, action.references); + case STATUS_CARD_FETCH_SUCCESS: + return state.setIn([action.id, 'card'], fromJS(action.card)); default: return state; } diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index f8eb37c58..f778ba06b 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1669,6 +1669,7 @@ a.account__display-name { padding: 4px 0; border-radius: 4px; box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4); + z-index: 9999; ul { list-style: none; @@ -2560,6 +2561,9 @@ a.status-card { display: block; margin-top: 5px; font-size: 13px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .status-card__image { @@ -2584,6 +2588,31 @@ a.status-card { } } +.status-card.compact { + border-color: lighten($ui-base-color, 4%); + + &.interactive { + border: 0; + } + + .status-card__content { + padding: 8px; + padding-top: 10px; + } + + .status-card__title { + white-space: nowrap; + } + + .status-card__image { + flex: 0 0 60px; + } +} + +a.status-card.compact:hover { + background-color: lighten($ui-base-color, 4%); +} + .status-card__image-image { border-radius: 4px 0 0 4px; display: block; diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb index 3e3cbdaed..0f787ebc4 100644 --- a/app/models/media_attachment.rb +++ b/app/models/media_attachment.rb @@ -148,6 +148,7 @@ class MediaAttachment < ApplicationRecord "#{x},#{y}" end + after_commit :reset_parent_cache, on: :update before_create :prepare_description, unless: :local? before_create :set_shortcode before_post_process :set_type_and_extension @@ -252,4 +253,9 @@ class MediaAttachment < ApplicationRecord bitrate: movie.bitrate, } end + + def reset_parent_cache + return if status_id.nil? + Rails.cache.delete("statuses/#{status_id}") + end end diff --git a/app/models/status.rb b/app/models/status.rb index 438863589..f67a05b3c 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -94,6 +94,7 @@ class Status < ApplicationRecord :conversation, :status_stat, :tags, + :preview_cards, :stream_entry, active_mentions: :account, reblog: [ @@ -101,6 +102,7 @@ class Status < ApplicationRecord :application, :stream_entry, :tags, + :preview_cards, :media_attachments, :conversation, :status_stat, @@ -168,6 +170,10 @@ class Status < ApplicationRecord reblog end + def preview_card + preview_cards.first + end + def title if destroyed? "#{account.acct} deleted status" @@ -241,10 +247,6 @@ class Status < ApplicationRecord before_validation :set_local class << self - def cache_ids - left_outer_joins(:status_stat).select('statuses.id, greatest(statuses.updated_at, status_stats.updated_at) AS updated_at') - end - def selectable_visibilities visibilities.keys - %w(direct limited) end diff --git a/app/models/status_stat.rb b/app/models/status_stat.rb index 9d358776b..024c467e7 100644 --- a/app/models/status_stat.rb +++ b/app/models/status_stat.rb @@ -14,4 +14,12 @@ class StatusStat < ApplicationRecord belongs_to :status, inverse_of: :status_stat + + after_commit :reset_parent_cache + + private + + def reset_parent_cache + Rails.cache.delete("statuses/#{status_id}") + end end diff --git a/app/serializers/rest/status_serializer.rb b/app/serializers/rest/status_serializer.rb index 07839c5ae..8a61c1056 100644 --- a/app/serializers/rest/status_serializer.rb +++ b/app/serializers/rest/status_serializer.rb @@ -21,6 +21,8 @@ class REST::StatusSerializer < ActiveModel::Serializer has_many :tags has_many :emojis, serializer: REST::CustomEmojiSerializer + has_one :preview_card, key: :card, serializer: REST::PreviewCardSerializer + def id object.id.to_s end diff --git a/app/services/fetch_link_card_service.rb b/app/services/fetch_link_card_service.rb index 4551aa7e0..462e5ee13 100644 --- a/app/services/fetch_link_card_service.rb +++ b/app/services/fetch_link_card_service.rb @@ -63,6 +63,7 @@ class FetchLinkCardService < BaseService def attach_card @status.preview_cards << @card + Rails.cache.delete(@status) end def parse_urls |