Merge commit 'f826a95f6e
' into kb_migration
This commit is contained in:
commit
4b65740722
211 changed files with 628 additions and 599 deletions
|
@ -289,6 +289,7 @@ RSpec/LetSetup:
|
||||||
- 'spec/controllers/oauth/tokens_controller_spec.rb'
|
- 'spec/controllers/oauth/tokens_controller_spec.rb'
|
||||||
- 'spec/controllers/settings/imports_controller_spec.rb'
|
- 'spec/controllers/settings/imports_controller_spec.rb'
|
||||||
- 'spec/lib/activitypub/activity/delete_spec.rb'
|
- 'spec/lib/activitypub/activity/delete_spec.rb'
|
||||||
|
- 'spec/lib/vacuum/applications_vacuum_spec.rb'
|
||||||
- 'spec/lib/vacuum/preview_cards_vacuum_spec.rb'
|
- 'spec/lib/vacuum/preview_cards_vacuum_spec.rb'
|
||||||
- 'spec/models/account_spec.rb'
|
- 'spec/models/account_spec.rb'
|
||||||
- 'spec/models/account_statuses_cleanup_policy_spec.rb'
|
- 'spec/models/account_statuses_cleanup_policy_spec.rb'
|
||||||
|
|
20
CHANGELOG.md
20
CHANGELOG.md
|
@ -2,6 +2,26 @@
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## [4.1.5] - 2023-07-21
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add check preventing Sidekiq workers from running with Makara configured ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25850))
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Change request timeout handling to use a longer deadline ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26055))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix moderation interface for remote instances with a .zip TLD ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25885))
|
||||||
|
- Fix remote accounts being possibly persisted to database with incomplete protocol values ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25886))
|
||||||
|
- Fix trending publishers table not rendering correctly on narrow screens ([vmstan](https://github.com/mastodon/mastodon/pull/25945))
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
- Fix CSP headers being unintentionally wide ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26105))
|
||||||
|
|
||||||
## [4.1.4] - 2023-07-07
|
## [4.1.4] - 2023-07-07
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
10
Gemfile.lock
10
Gemfile.lock
|
@ -103,7 +103,7 @@ GEM
|
||||||
attr_required (1.0.1)
|
attr_required (1.0.1)
|
||||||
awrence (1.2.1)
|
awrence (1.2.1)
|
||||||
aws-eventstream (1.2.0)
|
aws-eventstream (1.2.0)
|
||||||
aws-partitions (1.786.0)
|
aws-partitions (1.791.0)
|
||||||
aws-sdk-core (3.178.0)
|
aws-sdk-core (3.178.0)
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
aws-partitions (~> 1, >= 1.651.0)
|
aws-partitions (~> 1, >= 1.651.0)
|
||||||
|
@ -112,7 +112,7 @@ GEM
|
||||||
aws-sdk-kms (1.71.0)
|
aws-sdk-kms (1.71.0)
|
||||||
aws-sdk-core (~> 3, >= 3.177.0)
|
aws-sdk-core (~> 3, >= 3.177.0)
|
||||||
aws-sigv4 (~> 1.1)
|
aws-sigv4 (~> 1.1)
|
||||||
aws-sdk-s3 (1.130.0)
|
aws-sdk-s3 (1.131.0)
|
||||||
aws-sdk-core (~> 3, >= 3.177.0)
|
aws-sdk-core (~> 3, >= 3.177.0)
|
||||||
aws-sdk-kms (~> 1)
|
aws-sdk-kms (~> 1)
|
||||||
aws-sigv4 (~> 1.6)
|
aws-sigv4 (~> 1.6)
|
||||||
|
@ -144,7 +144,7 @@ GEM
|
||||||
blurhash (0.1.7)
|
blurhash (0.1.7)
|
||||||
bootsnap (1.16.0)
|
bootsnap (1.16.0)
|
||||||
msgpack (~> 1.2)
|
msgpack (~> 1.2)
|
||||||
brakeman (6.0.0)
|
brakeman (6.0.1)
|
||||||
browser (5.3.1)
|
browser (5.3.1)
|
||||||
brpoplpush-redis_script (0.1.3)
|
brpoplpush-redis_script (0.1.3)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.5)
|
concurrent-ruby (~> 1.0, >= 1.0.5)
|
||||||
|
@ -307,7 +307,7 @@ GEM
|
||||||
activesupport (>= 5.1)
|
activesupport (>= 5.1)
|
||||||
haml (>= 4.0.6)
|
haml (>= 4.0.6)
|
||||||
railties (>= 5.1)
|
railties (>= 5.1)
|
||||||
haml_lint (0.48.0)
|
haml_lint (0.49.1)
|
||||||
haml (>= 4.0, < 6.2)
|
haml (>= 4.0, < 6.2)
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
rainbow
|
rainbow
|
||||||
|
@ -569,7 +569,7 @@ GEM
|
||||||
rake (13.0.6)
|
rake (13.0.6)
|
||||||
rdf (3.2.11)
|
rdf (3.2.11)
|
||||||
link_header (~> 0.0, >= 0.0.8)
|
link_header (~> 0.0, >= 0.0.8)
|
||||||
rdf-normalize (0.6.0)
|
rdf-normalize (0.6.1)
|
||||||
rdf (~> 3.2)
|
rdf (~> 3.2)
|
||||||
redcarpet (3.6.0)
|
redcarpet (3.6.0)
|
||||||
redis (4.8.1)
|
redis (4.8.1)
|
||||||
|
|
|
@ -108,7 +108,7 @@ class Auth::SessionsController < Devise::SessionsController
|
||||||
end
|
end
|
||||||
|
|
||||||
def home_paths(resource)
|
def home_paths(resource)
|
||||||
paths = [about_path]
|
paths = [about_path, '/explore']
|
||||||
|
|
||||||
paths << short_account_path(username: resource.account) if single_user_mode? && resource.is_a?(User)
|
paths << short_account_path(username: resource.account) if single_user_mode? && resource.is_a?(User)
|
||||||
|
|
||||||
|
|
|
@ -297,7 +297,7 @@ export default class Dropdown extends PureComponent {
|
||||||
onKeyPress: this.handleKeyPress,
|
onKeyPress: this.handleKeyPress,
|
||||||
}) : (
|
}) : (
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={icon}
|
icon={!open ? icon : 'close'}
|
||||||
title={title}
|
title={title}
|
||||||
active={open}
|
active={open}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
|
|
@ -114,7 +114,7 @@ export default class IntersectionObserverArticle extends Component {
|
||||||
aria-setsize={listLength}
|
aria-setsize={listLength}
|
||||||
style={{ height: `${this.height || cachedHeight}px`, opacity: 0, overflow: 'hidden' }}
|
style={{ height: `${this.height || cachedHeight}px`, opacity: 0, overflow: 'hidden' }}
|
||||||
data-id={id}
|
data-id={id}
|
||||||
tabIndex={0}
|
tabIndex={-1}
|
||||||
>
|
>
|
||||||
{children && cloneElement(children, { hidden: true })}
|
{children && cloneElement(children, { hidden: true })}
|
||||||
</article>
|
</article>
|
||||||
|
@ -122,7 +122,7 @@ export default class IntersectionObserverArticle extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<article ref={this.handleRef} aria-posinset={index + 1} aria-setsize={listLength} data-id={id} tabIndex={0}>
|
<article ref={this.handleRef} aria-posinset={index + 1} aria-setsize={listLength} data-id={id} tabIndex={-1}>
|
||||||
{children && cloneElement(children, { hidden: false })}
|
{children && cloneElement(children, { hidden: false })}
|
||||||
</article>
|
</article>
|
||||||
);
|
);
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { debounce } from 'lodash';
|
||||||
|
|
||||||
import { Blurhash } from 'mastodon/components/blurhash';
|
import { Blurhash } from 'mastodon/components/blurhash';
|
||||||
|
|
||||||
import { autoPlayGif, cropImages, displayMedia, displayMediaExpand, useBlurhash } from '../initial_state';
|
import { autoPlayGif, displayMedia, displayMediaExpand, useBlurhash } from '../initial_state';
|
||||||
|
|
||||||
import { IconButton } from './icon_button';
|
import { IconButton } from './icon_button';
|
||||||
|
|
||||||
|
@ -225,7 +225,6 @@ class MediaGallery extends PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
sensitive: PropTypes.bool,
|
sensitive: PropTypes.bool,
|
||||||
standalone: PropTypes.bool,
|
|
||||||
media: ImmutablePropTypes.list.isRequired,
|
media: ImmutablePropTypes.list.isRequired,
|
||||||
lang: PropTypes.string,
|
lang: PropTypes.string,
|
||||||
size: PropTypes.object,
|
size: PropTypes.object,
|
||||||
|
@ -239,10 +238,6 @@ class MediaGallery extends PureComponent {
|
||||||
onToggleVisibility: PropTypes.func,
|
onToggleVisibility: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
standalone: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
visible: this.props.visible !== undefined ? this.props.visible : (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all'),
|
visible: this.props.visible !== undefined ? this.props.visible : (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all'),
|
||||||
width: this.props.defaultWidth,
|
width: this.props.defaultWidth,
|
||||||
|
@ -311,7 +306,7 @@ class MediaGallery extends PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { media, lang, intl, sensitive, defaultWidth, standalone, autoplay } = this.props;
|
const { media, lang, intl, sensitive, defaultWidth, autoplay } = this.props;
|
||||||
const { visible } = this.state;
|
const { visible } = this.state;
|
||||||
const width = this.state.width || defaultWidth;
|
const width = this.state.width || defaultWidth;
|
||||||
|
|
||||||
|
@ -319,10 +314,10 @@ class MediaGallery extends PureComponent {
|
||||||
|
|
||||||
const style = {};
|
const style = {};
|
||||||
|
|
||||||
if (this.isFullSizeEligible() && (standalone || !cropImages)) {
|
if (this.isFullSizeEligible()) {
|
||||||
style.aspectRatio = `${this.props.media.getIn([0, 'meta', 'small', 'aspect'])}`;
|
style.aspectRatio = `${this.props.media.getIn([0, 'meta', 'small', 'aspect'])}`;
|
||||||
} else {
|
} else {
|
||||||
style.aspectRatio = '16 / 9';
|
style.aspectRatio = '3 / 2';
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxSize = displayMediaExpand ? 16 : 4;
|
const maxSize = displayMediaExpand ? 16 : 4;
|
||||||
|
@ -330,7 +325,7 @@ class MediaGallery extends PureComponent {
|
||||||
const size = media.take(maxSize).size;
|
const size = media.take(maxSize).size;
|
||||||
const uncached = media.every(attachment => attachment.get('type') === 'unknown');
|
const uncached = media.every(attachment => attachment.get('type') === 'unknown');
|
||||||
|
|
||||||
if (standalone && this.isFullSizeEligible()) {
|
if (this.isFullSizeEligible()) {
|
||||||
children = <Item standalone autoplay={autoplay} onClick={this.handleClick} attachment={media.get(0)} lang={lang} displayWidth={width} visible={visible} />;
|
children = <Item standalone autoplay={autoplay} onClick={this.handleClick} attachment={media.get(0)} lang={lang} displayWidth={width} visible={visible} />;
|
||||||
} else {
|
} else {
|
||||||
children = media.take(maxSize).map((attachment, i) => <Item key={attachment.get('id')} autoplay={autoplay} onClick={this.handleClick} attachment={attachment} index={i} lang={lang} size={size} displayWidth={width} visible={visible || uncached} />);
|
children = media.take(maxSize).map((attachment, i) => <Item key={attachment.get('id')} autoplay={autoplay} onClick={this.handleClick} attachment={attachment} index={i} lang={lang} size={size} displayWidth={width} visible={visible || uncached} />);
|
||||||
|
|
|
@ -12,6 +12,7 @@ class PictureInPicturePlaceholder extends PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
|
aspectRatio: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleClick = () => {
|
handleClick = () => {
|
||||||
|
@ -20,8 +21,10 @@ class PictureInPicturePlaceholder extends PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
const { aspectRatio } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='picture-in-picture-placeholder' role='button' tabIndex={0} onClick={this.handleClick}>
|
<div className='picture-in-picture-placeholder' style={{ aspectRatio }} role='button' tabIndex={0} onClick={this.handleClick}>
|
||||||
<Icon id='window-restore' />
|
<Icon id='window-restore' />
|
||||||
<FormattedMessage id='picture_in_picture.restore' defaultMessage='Put it back' />
|
<FormattedMessage id='picture_in_picture.restore' defaultMessage='Put it back' />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -19,7 +19,6 @@ import Bundle from '../features/ui/components/bundle';
|
||||||
import { MediaGallery, Video, Audio } from '../features/ui/util/async-components';
|
import { MediaGallery, Video, Audio } from '../features/ui/util/async-components';
|
||||||
import { displayMedia } from '../initial_state';
|
import { displayMedia } from '../initial_state';
|
||||||
|
|
||||||
import AttachmentList from './attachment_list';
|
|
||||||
import { Avatar } from './avatar';
|
import { Avatar } from './avatar';
|
||||||
import { AvatarOverlay } from './avatar_overlay';
|
import { AvatarOverlay } from './avatar_overlay';
|
||||||
import { DisplayName } from './display_name';
|
import { DisplayName } from './display_name';
|
||||||
|
@ -198,17 +197,35 @@ class Status extends ImmutablePureComponent {
|
||||||
this.props.onTranslate(this._properStatus());
|
this.props.onTranslate(this._properStatus());
|
||||||
};
|
};
|
||||||
|
|
||||||
renderLoadingMediaGallery () {
|
getAttachmentAspectRatio () {
|
||||||
return <div className='media-gallery' style={{ height: '110px' }} />;
|
const attachments = this._properStatus().get('media_attachments');
|
||||||
|
|
||||||
|
if (attachments.getIn([0, 'type']) === 'video') {
|
||||||
|
return `${attachments.getIn([0, 'meta', 'original', 'width'])} / ${attachments.getIn([0, 'meta', 'original', 'height'])}`;
|
||||||
|
} else if (attachments.getIn([0, 'type']) === 'audio') {
|
||||||
|
return '16 / 9';
|
||||||
|
} else {
|
||||||
|
return (attachments.size === 1 && attachments.getIn([0, 'meta', 'small', 'aspect'])) ? attachments.getIn([0, 'meta', 'small', 'aspect']) : '3 / 2'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLoadingVideoPlayer () {
|
renderLoadingMediaGallery = () => {
|
||||||
return <div className='video-player' style={{ height: '110px' }} />;
|
return (
|
||||||
}
|
<div className='media-gallery' style={{ aspectRatio: this.getAttachmentAspectRatio() }} />
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
renderLoadingAudioPlayer () {
|
renderLoadingVideoPlayer = () => {
|
||||||
return <div className='audio-player' style={{ height: '110px' }} />;
|
return (
|
||||||
}
|
<div className='video-player' style={{ aspectRatio: this.getAttachmentAspectRatio() }} />
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
renderLoadingAudioPlayer = () => {
|
||||||
|
return (
|
||||||
|
<div className='audio-player' style={{ aspectRatio: this.getAttachmentAspectRatio() }} />
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
handleOpenVideo = (options) => {
|
handleOpenVideo = (options) => {
|
||||||
const status = this._properStatus();
|
const status = this._properStatus();
|
||||||
|
@ -445,18 +462,11 @@ class Status extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pictureInPicture.get('inUse')) {
|
if (pictureInPicture.get('inUse')) {
|
||||||
media = <PictureInPicturePlaceholder />;
|
media = <PictureInPicturePlaceholder aspectRatio={this.getAttachmentAspectRatio()} />;
|
||||||
} else if (status.get('media_attachments').size > 0) {
|
} else if (status.get('media_attachments').size > 0) {
|
||||||
const language = status.getIn(['translation', 'language']) || status.get('language');
|
const language = status.getIn(['translation', 'language']) || status.get('language');
|
||||||
|
|
||||||
if (this.props.muted) {
|
if (status.getIn(['media_attachments', 0, 'type']) === 'audio') {
|
||||||
media = (
|
|
||||||
<AttachmentList
|
|
||||||
compact
|
|
||||||
media={status.get('media_attachments')}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else if (status.getIn(['media_attachments', 0, 'type']) === 'audio') {
|
|
||||||
const attachment = status.getIn(['media_attachments', 0]);
|
const attachment = status.getIn(['media_attachments', 0]);
|
||||||
const description = attachment.getIn(['translation', 'description']) || attachment.get('description');
|
const description = attachment.getIn(['translation', 'description']) || attachment.get('description');
|
||||||
|
|
||||||
|
@ -494,11 +504,11 @@ class Status extends ImmutablePureComponent {
|
||||||
<Component
|
<Component
|
||||||
preview={attachment.get('preview_url')}
|
preview={attachment.get('preview_url')}
|
||||||
frameRate={attachment.getIn(['meta', 'original', 'frame_rate'])}
|
frameRate={attachment.getIn(['meta', 'original', 'frame_rate'])}
|
||||||
|
aspectRatio={`${attachment.getIn(['meta', 'original', 'width'])} / ${attachment.getIn(['meta', 'original', 'height'])}`}
|
||||||
blurhash={attachment.get('blurhash')}
|
blurhash={attachment.get('blurhash')}
|
||||||
src={attachment.get('url')}
|
src={attachment.get('url')}
|
||||||
alt={description}
|
alt={description}
|
||||||
lang={language}
|
lang={language}
|
||||||
inline
|
|
||||||
sensitive={status.get('sensitive')}
|
sensitive={status.get('sensitive')}
|
||||||
onOpenVideo={this.handleOpenVideo}
|
onOpenVideo={this.handleOpenVideo}
|
||||||
deployPictureInPicture={pictureInPicture.get('available') ? this.handleDeployPictureInPicture : undefined}
|
deployPictureInPicture={pictureInPicture.get('available') ? this.handleDeployPictureInPicture : undefined}
|
||||||
|
@ -527,7 +537,7 @@ class Status extends ImmutablePureComponent {
|
||||||
</Bundle>
|
</Bundle>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (status.get('spoiler_text').length === 0 && status.get('card') && !this.props.muted) {
|
} else if (status.get('spoiler_text').length === 0 && status.get('card')) {
|
||||||
media = (
|
media = (
|
||||||
<Card
|
<Card
|
||||||
onOpenMedia={this.handleOpenMedia}
|
onOpenMedia={this.handleOpenMedia}
|
||||||
|
|
|
@ -34,7 +34,7 @@ const messages = defineMessages({
|
||||||
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' },
|
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' },
|
||||||
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
|
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
|
||||||
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
|
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
|
||||||
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
|
favourite: { id: 'status.favourite', defaultMessage: 'Favorite' },
|
||||||
emojiReaction: { id: 'status.emoji_reaction', defaultMessage: 'Stamp' },
|
emojiReaction: { id: 'status.emoji_reaction', defaultMessage: 'Stamp' },
|
||||||
bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' },
|
bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' },
|
||||||
removeBookmark: { id: 'status.remove_bookmark', defaultMessage: 'Remove bookmark' },
|
removeBookmark: { id: 'status.remove_bookmark', defaultMessage: 'Remove bookmark' },
|
||||||
|
|
|
@ -57,7 +57,7 @@ const messages = defineMessages({
|
||||||
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
|
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
|
||||||
deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
|
deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
|
||||||
redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
|
redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
|
||||||
redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' },
|
redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favorites and boosts will be lost, and replies to the original post will be orphaned.' },
|
||||||
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
|
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
|
||||||
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
|
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
|
||||||
editConfirm: { id: 'confirmations.edit.confirm', defaultMessage: 'Edit' },
|
editConfirm: { id: 'confirmations.edit.confirm', defaultMessage: 'Edit' },
|
||||||
|
|
|
@ -48,7 +48,7 @@ const messages = defineMessages({
|
||||||
pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned posts' },
|
pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned posts' },
|
||||||
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
|
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
|
||||||
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
|
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
|
||||||
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
|
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favorites' },
|
||||||
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
|
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
|
||||||
followed_tags: { id: 'navigation_bar.followed_tags', defaultMessage: 'Followed hashtags' },
|
followed_tags: { id: 'navigation_bar.followed_tags', defaultMessage: 'Followed hashtags' },
|
||||||
blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
|
blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
|
||||||
|
|
|
@ -470,6 +470,7 @@ class Audio extends PureComponent {
|
||||||
const progress = Math.min((currentTime / duration) * 100, 100);
|
const progress = Math.min((currentTime / duration) * 100, 100);
|
||||||
|
|
||||||
let warning;
|
let warning;
|
||||||
|
|
||||||
if (sensitive) {
|
if (sensitive) {
|
||||||
warning = <FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' />;
|
warning = <FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' />;
|
||||||
} else {
|
} else {
|
||||||
|
@ -515,7 +516,10 @@ class Audio extends PureComponent {
|
||||||
|
|
||||||
<div className={classNames('spoiler-button', { 'spoiler-button--hidden': revealed || editable })}>
|
<div className={classNames('spoiler-button', { 'spoiler-button--hidden': revealed || editable })}>
|
||||||
<button type='button' className='spoiler-button__overlay' onClick={this.toggleReveal}>
|
<button type='button' className='spoiler-button__overlay' onClick={this.toggleReveal}>
|
||||||
<span className='spoiler-button__overlay__label'>{warning}</span>
|
<span className='spoiler-button__overlay__label'>
|
||||||
|
{warning}
|
||||||
|
<span className='spoiler-button__overlay__action'><FormattedMessage id='status.media.show' defaultMessage='Click to show' /></span>
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,6 @@ class Bookmarks extends ImmutablePureComponent {
|
||||||
onClick={this.handleHeaderClick}
|
onClick={this.handleHeaderClick}
|
||||||
pinned={pinned}
|
pinned={pinned}
|
||||||
multiColumn={multiColumn}
|
multiColumn={multiColumn}
|
||||||
showBackButton
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<StatusList
|
<StatusList
|
||||||
|
|
|
@ -13,7 +13,7 @@ const messages = defineMessages({
|
||||||
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
|
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
|
||||||
reaction_deck: { id: 'navigation_bar.reaction_deck', defaultMessage: 'Reaction deck' },
|
reaction_deck: { id: 'navigation_bar.reaction_deck', defaultMessage: 'Reaction deck' },
|
||||||
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
|
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
|
||||||
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
|
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favorites' },
|
||||||
emoji_reactions: { id: 'navigation_bar.emoji_reactions', defaultMessage: 'Stamps' },
|
emoji_reactions: { id: 'navigation_bar.emoji_reactions', defaultMessage: 'Stamps' },
|
||||||
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
|
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
|
||||||
followed_tags: { id: 'navigation_bar.followed_tags', defaultMessage: 'Followed hashtags' },
|
followed_tags: { id: 'navigation_bar.followed_tags', defaultMessage: 'Followed hashtags' },
|
||||||
|
|
|
@ -8,7 +8,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
|
||||||
import { Avatar } from '../../../components/avatar';
|
import { Avatar } from '../../../components/avatar';
|
||||||
import { IconButton } from '../../../components/icon_button';
|
|
||||||
|
|
||||||
import ActionBar from './action_bar';
|
import ActionBar from './action_bar';
|
||||||
|
|
||||||
|
@ -21,23 +20,27 @@ export default class NavigationBar extends ImmutablePureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
const username = this.props.account.get('acct')
|
||||||
return (
|
return (
|
||||||
<div className='navigation-bar'>
|
<div className='navigation-bar'>
|
||||||
<Link to={`/@${this.props.account.get('acct')}`}>
|
<Link to={`/@${username}`}>
|
||||||
<span style={{ display: 'none' }}>{this.props.account.get('acct')}</span>
|
<span style={{ display: 'none' }}>{username}</span>
|
||||||
<Avatar account={this.props.account} size={46} />
|
<Avatar account={this.props.account} size={46} />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className='navigation-bar__profile'>
|
<div className='navigation-bar__profile'>
|
||||||
<Link to={`/@${this.props.account.get('acct')}`}>
|
<span>
|
||||||
<strong className='navigation-bar__profile-account'>@{this.props.account.get('acct')}</strong>
|
<Link to={`/@${username}`}>
|
||||||
|
<strong className='navigation-bar__profile-account'>@{username}</strong>
|
||||||
</Link>
|
</Link>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span>
|
||||||
<a href='/settings/profile' className='navigation-bar__profile-edit'><FormattedMessage id='navigation_bar.edit_profile' defaultMessage='Edit profile' /></a>
|
<a href='/settings/profile' className='navigation-bar__profile-edit'><FormattedMessage id='navigation_bar.edit_profile' defaultMessage='Edit profile' /></a>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='navigation-bar__actions'>
|
<div className='navigation-bar__actions'>
|
||||||
<IconButton className='close' title='' icon='close' onClick={this.props.onClose} />
|
|
||||||
<ActionBar account={this.props.account} onLogout={this.props.onLogout} />
|
<ActionBar account={this.props.account} onLogout={this.props.onLogout} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import { Blurhash } from 'mastodon/components/blurhash';
|
import { Blurhash } from 'mastodon/components/blurhash';
|
||||||
import { accountsCountRenderer } from 'mastodon/components/hashtag';
|
import { accountsCountRenderer } from 'mastodon/components/hashtag';
|
||||||
|
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
||||||
import { ShortNumber } from 'mastodon/components/short_number';
|
import { ShortNumber } from 'mastodon/components/short_number';
|
||||||
import { Skeleton } from 'mastodon/components/skeleton';
|
import { Skeleton } from 'mastodon/components/skeleton';
|
||||||
|
|
||||||
|
@ -13,10 +16,14 @@ export default class Story extends PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
url: PropTypes.string,
|
url: PropTypes.string,
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
|
lang: PropTypes.string,
|
||||||
publisher: PropTypes.string,
|
publisher: PropTypes.string,
|
||||||
|
publishedAt: PropTypes.string,
|
||||||
|
author: PropTypes.string,
|
||||||
sharedTimes: PropTypes.number,
|
sharedTimes: PropTypes.number,
|
||||||
thumbnail: PropTypes.string,
|
thumbnail: PropTypes.string,
|
||||||
blurhash: PropTypes.string,
|
blurhash: PropTypes.string,
|
||||||
|
expanded: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
@ -26,16 +33,16 @@ export default class Story extends PureComponent {
|
||||||
handleImageLoad = () => this.setState({ thumbnailLoaded: true });
|
handleImageLoad = () => this.setState({ thumbnailLoaded: true });
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { url, title, publisher, sharedTimes, thumbnail, blurhash } = this.props;
|
const { expanded, url, title, lang, publisher, author, publishedAt, sharedTimes, thumbnail, blurhash } = this.props;
|
||||||
|
|
||||||
const { thumbnailLoaded } = this.state;
|
const { thumbnailLoaded } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a className='story' href={url} target='blank' rel='noopener'>
|
<a className={classNames('story', { expanded })} href={url} target='blank' rel='noopener'>
|
||||||
<div className='story__details'>
|
<div className='story__details'>
|
||||||
<div className='story__details__publisher'>{publisher ? publisher : <Skeleton width={50} />}</div>
|
<div className='story__details__publisher'>{publisher ? <span lang={lang}>{publisher}</span> : <Skeleton width={50} />}{publishedAt && <> · <RelativeTimestamp timestamp={publishedAt} /></>}</div>
|
||||||
<div className='story__details__title'>{title ? title : <Skeleton />}</div>
|
<div className='story__details__title' lang={lang}>{title ? title : <Skeleton />}</div>
|
||||||
<div className='story__details__shared'>{typeof sharedTimes === 'number' ? <ShortNumber value={sharedTimes} renderer={accountsCountRenderer} /> : <Skeleton width={100} />}</div>
|
<div className='story__details__shared'>{author && <><FormattedMessage id='link_preview.author' defaultMessage='By {name}' values={{ name: <strong>{author}</strong> }} /> · </>}{typeof sharedTimes === 'number' ? <ShortNumber value={sharedTimes} renderer={accountsCountRenderer} /> : <Skeleton width={100} />}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='story__thumbnail'>
|
<div className='story__thumbnail'>
|
||||||
|
|
|
@ -55,12 +55,16 @@ class Links extends PureComponent {
|
||||||
<div className='explore__links'>
|
<div className='explore__links'>
|
||||||
{banner}
|
{banner}
|
||||||
|
|
||||||
{isLoading ? (<LoadingIndicator />) : links.map(link => (
|
{isLoading ? (<LoadingIndicator />) : links.map((link, i) => (
|
||||||
<Story
|
<Story
|
||||||
key={link.get('id')}
|
key={link.get('id')}
|
||||||
|
expanded={i === 0}
|
||||||
|
lang={link.get('language')}
|
||||||
url={link.get('url')}
|
url={link.get('url')}
|
||||||
title={link.get('title')}
|
title={link.get('title')}
|
||||||
publisher={link.get('provider_name')}
|
publisher={link.get('provider_name')}
|
||||||
|
publishedAt={link.get('published_at')}
|
||||||
|
author={link.get('author_name')}
|
||||||
sharedTimes={link.getIn(['history', 0, 'accounts']) * 1 + link.getIn(['history', 1, 'accounts']) * 1}
|
sharedTimes={link.getIn(['history', 0, 'accounts']) * 1 + link.getIn(['history', 1, 'accounts']) * 1}
|
||||||
thumbnail={link.get('image')}
|
thumbnail={link.get('image')}
|
||||||
blurhash={link.get('blurhash')}
|
blurhash={link.get('blurhash')}
|
||||||
|
|
|
@ -47,7 +47,7 @@ class Statuses extends PureComponent {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DismissableBanner id='explore/statuses'>
|
<DismissableBanner id='explore/statuses'>
|
||||||
<FormattedMessage id='dismissable_banner.explore_statuses' defaultMessage='These are posts from across the social web that are gaining traction today. Newer posts with more boosts and favourites are ranked higher.' />
|
<FormattedMessage id='dismissable_banner.explore_statuses' defaultMessage='These are posts from across the social web that are gaining traction today. Newer posts with more boosts and favorites are ranked higher.' />
|
||||||
</DismissableBanner>
|
</DismissableBanner>
|
||||||
|
|
||||||
<StatusList
|
<StatusList
|
||||||
|
|
|
@ -18,7 +18,7 @@ import Column from 'mastodon/features/ui/components/column';
|
||||||
import { getStatusList } from 'mastodon/selectors';
|
import { getStatusList } from 'mastodon/selectors';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
heading: { id: 'column.favourites', defaultMessage: 'Favourites' },
|
heading: { id: 'column.favourites', defaultMessage: 'Favorites' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
|
@ -74,7 +74,7 @@ class Favourites extends ImmutablePureComponent {
|
||||||
const { intl, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props;
|
const { intl, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props;
|
||||||
const pinned = !!columnId;
|
const pinned = !!columnId;
|
||||||
|
|
||||||
const emptyMessage = <FormattedMessage id='empty_column.favourited_statuses' defaultMessage="You don't have any favourite posts yet. When you favourite one, it will show up here." />;
|
const emptyMessage = <FormattedMessage id='empty_column.favourited_statuses' defaultMessage="You don't have any favorite posts yet. When you favorite one, it will show up here." />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.heading)}>
|
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.heading)}>
|
||||||
|
@ -86,7 +86,6 @@ class Favourites extends ImmutablePureComponent {
|
||||||
onClick={this.handleHeaderClick}
|
onClick={this.handleHeaderClick}
|
||||||
pinned={pinned}
|
pinned={pinned}
|
||||||
multiColumn={multiColumn}
|
multiColumn={multiColumn}
|
||||||
showBackButton
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<StatusList
|
<StatusList
|
||||||
|
|
|
@ -61,7 +61,7 @@ class Favourites extends ImmutablePureComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const emptyMessage = <FormattedMessage id='empty_column.favourites' defaultMessage='No one has favourited this post yet. When someone does, they will show up here.' />;
|
const emptyMessage = <FormattedMessage id='empty_column.favourites' defaultMessage='No one has favorited this post yet. When someone does, they will show up here.' />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column bindToDocument={!multiColumn}>
|
<Column bindToDocument={!multiColumn}>
|
||||||
|
|
|
@ -32,7 +32,7 @@ const messages = defineMessages({
|
||||||
bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
|
bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
|
||||||
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
|
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
|
||||||
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
|
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
|
||||||
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
|
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favorites' },
|
||||||
blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
|
blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
|
||||||
domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Blocked domains' },
|
domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Blocked domains' },
|
||||||
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
|
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
|
||||||
|
|
|
@ -116,8 +116,8 @@ class InteractionModal extends PureComponent {
|
||||||
break;
|
break;
|
||||||
case 'favourite':
|
case 'favourite':
|
||||||
icon = <Icon id='star' />;
|
icon = <Icon id='star' />;
|
||||||
title = <FormattedMessage id='interaction_modal.title.favourite' defaultMessage="Favourite {name}'s post" values={{ name }} />;
|
title = <FormattedMessage id='interaction_modal.title.favourite' defaultMessage="Favorite {name}'s post" values={{ name }} />;
|
||||||
actionDescription = <FormattedMessage id='interaction_modal.description.favourite' defaultMessage='With an account on Mastodon, you can favourite this post to let the author know you appreciate it and save it for later.' />;
|
actionDescription = <FormattedMessage id='interaction_modal.description.favourite' defaultMessage='With an account on Mastodon, you can favorite this post to let the author know you appreciate it and save it for later.' />;
|
||||||
break;
|
break;
|
||||||
case 'emoji_reaction':
|
case 'emoji_reaction':
|
||||||
icon = <Icon id='smile-o' />;
|
icon = <Icon id='smile-o' />;
|
||||||
|
@ -163,7 +163,7 @@ class InteractionModal extends PureComponent {
|
||||||
|
|
||||||
<div className='interaction-modal__choices__choice'>
|
<div className='interaction-modal__choices__choice'>
|
||||||
<h3><FormattedMessage id='interaction_modal.on_another_server' defaultMessage='On a different server' /></h3>
|
<h3><FormattedMessage id='interaction_modal.on_another_server' defaultMessage='On a different server' /></h3>
|
||||||
<p><FormattedMessage id='interaction_modal.other_server_instructions' defaultMessage='Copy and paste this URL into the search field of your favourite Mastodon app or the web interface of your Mastodon server.' /></p>
|
<p><FormattedMessage id='interaction_modal.other_server_instructions' defaultMessage='Copy and paste this URL into the search field of your favorite Mastodon app or the web interface of your Mastodon server.' /></p>
|
||||||
<Copypaste value={url} />
|
<Copypaste value={url} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -54,7 +54,7 @@ class KeyboardShortcuts extends ImmutablePureComponent {
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><kbd>f</kbd></td>
|
<td><kbd>f</kbd></td>
|
||||||
<td><FormattedMessage id='keyboard_shortcuts.favourite' defaultMessage='to favourite' /></td>
|
<td><FormattedMessage id='keyboard_shortcuts.favourite' defaultMessage='to favorite' /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><kbd>b</kbd></td>
|
<td><kbd>b</kbd></td>
|
||||||
|
@ -138,7 +138,7 @@ class KeyboardShortcuts extends ImmutablePureComponent {
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><kbd>g</kbd>+<kbd>f</kbd></td>
|
<td><kbd>g</kbd>+<kbd>f</kbd></td>
|
||||||
<td><FormattedMessage id='keyboard_shortcuts.favourites' defaultMessage='to open favourites list' /></td>
|
<td><FormattedMessage id='keyboard_shortcuts.favourites' defaultMessage='to open favorites list' /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><kbd>g</kbd>+<kbd>e</kbd></td>
|
<td><kbd>g</kbd>+<kbd>e</kbd></td>
|
||||||
|
|
|
@ -65,7 +65,7 @@ class Lists extends ImmutablePureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.heading)}>
|
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.heading)}>
|
||||||
<ColumnHeader title={intl.formatMessage(messages.heading)} icon='list-ul' multiColumn={multiColumn} showBackButton />
|
<ColumnHeader title={intl.formatMessage(messages.heading)} icon='list-ul' multiColumn={multiColumn} />
|
||||||
|
|
||||||
<NewListForm />
|
<NewListForm />
|
||||||
|
|
||||||
|
|
|
@ -109,7 +109,7 @@ export default class ColumnSettings extends PureComponent {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div role='group' aria-labelledby='notifications-favourite'>
|
<div role='group' aria-labelledby='notifications-favourite'>
|
||||||
<span id='notifications-favourite' className='column-settings__section'><FormattedMessage id='notifications.column_settings.favourite' defaultMessage='Favourites:' /></span>
|
<span id='notifications-favourite' className='column-settings__section'><FormattedMessage id='notifications.column_settings.favourite' defaultMessage='Favorites:' /></span>
|
||||||
|
|
||||||
<div className='column-settings__row'>
|
<div className='column-settings__row'>
|
||||||
<SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'favourite']} onChange={onChange} label={alertStr} />
|
<SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'favourite']} onChange={onChange} label={alertStr} />
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { Icon } from 'mastodon/components/icon';
|
||||||
|
|
||||||
const tooltips = defineMessages({
|
const tooltips = defineMessages({
|
||||||
mentions: { id: 'notifications.filter.mentions', defaultMessage: 'Mentions' },
|
mentions: { id: 'notifications.filter.mentions', defaultMessage: 'Mentions' },
|
||||||
favourites: { id: 'notifications.filter.favourites', defaultMessage: 'Favourites' },
|
favourites: { id: 'notifications.filter.favourites', defaultMessage: 'Favorites' },
|
||||||
emojiReactions: { id: 'notifications.filter.emoji_reactions', defaultMessage: 'Stamps' },
|
emojiReactions: { id: 'notifications.filter.emoji_reactions', defaultMessage: 'Stamps' },
|
||||||
boosts: { id: 'notifications.filter.boosts', defaultMessage: 'Boosts' },
|
boosts: { id: 'notifications.filter.boosts', defaultMessage: 'Boosts' },
|
||||||
status_references: { id: 'notifications.filter.status_references', defaultMessage: 'Status references' },
|
status_references: { id: 'notifications.filter.status_references', defaultMessage: 'Status references' },
|
||||||
|
|
|
@ -21,7 +21,7 @@ import FollowRequestContainer from '../containers/follow_request_container';
|
||||||
import Report from './report';
|
import Report from './report';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
favourite: { id: 'notification.favourite', defaultMessage: '{name} favourited your status' },
|
favourite: { id: 'notification.favourite', defaultMessage: '{name} favorited your status' },
|
||||||
emojiReaction: { id: 'notification.emoji_reaction', defaultMessage: '{name} reacted your status with emoji' },
|
emojiReaction: { id: 'notification.emoji_reaction', defaultMessage: '{name} reacted your status with emoji' },
|
||||||
follow: { id: 'notification.follow', defaultMessage: '{name} followed you' },
|
follow: { id: 'notification.follow', defaultMessage: '{name} followed you' },
|
||||||
ownPoll: { id: 'notification.own_poll', defaultMessage: 'Your poll has ended' },
|
ownPoll: { id: 'notification.own_poll', defaultMessage: 'Your poll has ended' },
|
||||||
|
@ -201,7 +201,7 @@ class Notification extends ImmutablePureComponent {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span title={notification.get('created_at')}>
|
<span title={notification.get('created_at')}>
|
||||||
<FormattedMessage id='notification.favourite' defaultMessage='{name} favourited your status' values={{ name: link }} />
|
<FormattedMessage id='notification.favourite' defaultMessage='{name} favorited your status' values={{ name: link }} />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ const messages = defineMessages({
|
||||||
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' },
|
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' },
|
||||||
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
|
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
|
||||||
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
|
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
|
||||||
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
|
favourite: { id: 'status.favourite', defaultMessage: 'Favorite' },
|
||||||
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
|
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
|
||||||
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
|
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
|
||||||
open: { id: 'status.open', defaultMessage: 'Expand this status' },
|
open: { id: 'status.open', defaultMessage: 'Expand this status' },
|
||||||
|
|
|
@ -26,7 +26,7 @@ const messages = defineMessages({
|
||||||
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' },
|
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' },
|
||||||
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
|
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
|
||||||
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
|
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
|
||||||
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
|
favourite: { id: 'status.favourite', defaultMessage: 'Favorite' },
|
||||||
bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' },
|
bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' },
|
||||||
more: { id: 'status.more', defaultMessage: 'More' },
|
more: { id: 'status.more', defaultMessage: 'More' },
|
||||||
mute: { id: 'status.mute', defaultMessage: 'Mute @{name}' },
|
mute: { id: 'status.mute', defaultMessage: 'Mute @{name}' },
|
||||||
|
|
|
@ -12,6 +12,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
import { Blurhash } from 'mastodon/components/blurhash';
|
import { Blurhash } from 'mastodon/components/blurhash';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
|
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
||||||
import { useBlurhash } from 'mastodon/initial_state';
|
import { useBlurhash } from 'mastodon/initial_state';
|
||||||
|
|
||||||
const IDNA_PREFIX = 'xn--';
|
const IDNA_PREFIX = 'xn--';
|
||||||
|
@ -57,14 +58,9 @@ export default class Card extends PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
card: ImmutablePropTypes.map,
|
card: ImmutablePropTypes.map,
|
||||||
onOpenMedia: PropTypes.func.isRequired,
|
onOpenMedia: PropTypes.func.isRequired,
|
||||||
compact: PropTypes.bool,
|
|
||||||
sensitive: PropTypes.bool,
|
sensitive: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
compact: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
previewLoaded: false,
|
previewLoaded: false,
|
||||||
embedded: false,
|
embedded: false,
|
||||||
|
@ -148,7 +144,7 @@ export default class Card extends PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { card, compact } = this.props;
|
const { card } = this.props;
|
||||||
const { embedded, revealed } = this.state;
|
const { embedded, revealed } = this.state;
|
||||||
|
|
||||||
if (card === null) {
|
if (card === null) {
|
||||||
|
@ -156,29 +152,27 @@ export default class Card extends PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = card.get('provider_name').length === 0 ? decodeIDNA(getHostname(card.get('url'))) : card.get('provider_name');
|
const provider = card.get('provider_name').length === 0 ? decodeIDNA(getHostname(card.get('url'))) : card.get('provider_name');
|
||||||
const horizontal = (!compact && card.get('width') > card.get('height')) || card.get('type') !== 'link' || embedded;
|
|
||||||
const interactive = card.get('type') !== 'link';
|
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 noreferrer' target='_blank'><strong>{card.get('title')}</strong></a> : <strong className='status-card__title' title={card.get('title')}>{card.get('title')}</strong>;
|
|
||||||
const language = card.get('language') || '';
|
const language = card.get('language') || '';
|
||||||
|
|
||||||
const description = (
|
const description = (
|
||||||
<div className='status-card__content' lang={language}>
|
<div className='status-card__content'>
|
||||||
{title}
|
<span className='status-card__host'>
|
||||||
{!(horizontal || compact) && <p className='status-card__description' title={card.get('description')}>{card.get('description')}</p>}
|
<span lang={language}>{provider}</span>
|
||||||
<span className='status-card__host'>{provider}</span>
|
{card.get('published_at') && <> · <RelativeTimestamp timestamp={card.get('published_at')} /></>}
|
||||||
|
</span>
|
||||||
|
<strong className='status-card__title' title={card.get('title')} lang={language}>{card.get('title')}</strong>
|
||||||
|
{card.get('author_name').length > 0 && <span className='status-card__author'><FormattedMessage id='link_preview.author' defaultMessage='By {name}' values={{ name: <strong>{card.get('author_name')}</strong> }} /></span>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const thumbnailStyle = {
|
const thumbnailStyle = {
|
||||||
visibility: revealed ? null : 'hidden',
|
visibility: revealed ? null : 'hidden',
|
||||||
|
aspectRatio: `${card.get('width')} / ${card.get('height')}`
|
||||||
};
|
};
|
||||||
|
|
||||||
if (horizontal) {
|
let embed;
|
||||||
thumbnailStyle.aspectRatio = (compact && !embedded) ? '16 / 9' : `${card.get('width')} / ${card.get('height')}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
let embed = '';
|
|
||||||
let canvas = (
|
let canvas = (
|
||||||
<Blurhash
|
<Blurhash
|
||||||
className={classnames('status-card__image-preview', {
|
className={classnames('status-card__image-preview', {
|
||||||
|
@ -188,12 +182,18 @@ export default class Card extends PureComponent {
|
||||||
dummy={!useBlurhash}
|
dummy={!useBlurhash}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
let thumbnail = <img src={card.get('image')} alt='' style={thumbnailStyle} onLoad={this.handleImageLoad} className='status-card__image-image' />;
|
let thumbnail = <img src={card.get('image')} alt='' style={thumbnailStyle} onLoad={this.handleImageLoad} className='status-card__image-image' />;
|
||||||
|
|
||||||
let spoilerButton = (
|
let spoilerButton = (
|
||||||
<button type='button' onClick={this.handleReveal} className='spoiler-button__overlay'>
|
<button type='button' onClick={this.handleReveal} className='spoiler-button__overlay'>
|
||||||
<span className='spoiler-button__overlay__label'><FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' /></span>
|
<span className='spoiler-button__overlay__label'>
|
||||||
|
<FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' />
|
||||||
|
<span className='spoiler-button__overlay__action'><FormattedMessage id='status.media.show' defaultMessage='Click to show' /></span>
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
|
||||||
spoilerButton = (
|
spoilerButton = (
|
||||||
<div className={classnames('spoiler-button', { 'spoiler-button--minified': revealed })}>
|
<div className={classnames('spoiler-button', { 'spoiler-button--minified': revealed })}>
|
||||||
{spoilerButton}
|
{spoilerButton}
|
||||||
|
@ -219,19 +219,20 @@ export default class Card extends PureComponent {
|
||||||
<div className='status-card__actions'>
|
<div className='status-card__actions'>
|
||||||
<div>
|
<div>
|
||||||
<button type='button' onClick={this.handleEmbedClick}><Icon id={iconVariant} /></button>
|
<button type='button' onClick={this.handleEmbedClick}><Icon id={iconVariant} /></button>
|
||||||
{horizontal && <a href={card.get('url')} target='_blank' rel='noopener noreferrer'><Icon id='external-link' /></a>}
|
<a href={card.get('url')} target='_blank' rel='noopener noreferrer'><Icon id='external-link' /></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!revealed && spoilerButton}
|
{!revealed && spoilerButton}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className} ref={this.setRef} onClick={revealed ? null : this.handleReveal} role={revealed ? 'button' : null}>
|
<div className='status-card' ref={this.setRef} onClick={revealed ? null : this.handleReveal} role={revealed ? 'button' : null}>
|
||||||
{embed}
|
{embed}
|
||||||
{!compact && description}
|
<a href={card.get('url')} target='_blank' rel='noopener noreferrer'>{description}</a>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else if (card.get('image')) {
|
} else if (card.get('image')) {
|
||||||
|
@ -243,14 +244,14 @@ export default class Card extends PureComponent {
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
embed = (
|
embed = (
|
||||||
<div className='status-card__image'>
|
<div className='status-card__image' style={{ aspectRatio: '1.9 / 1' }}>
|
||||||
<Icon id='file-text' />
|
<Icon id='file-text' />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a href={card.get('url')} className={className} target='_blank' rel='noopener noreferrer' ref={this.setRef}>
|
<a href={card.get('url')} className='status-card' target='_blank' rel='noopener noreferrer' ref={this.setRef}>
|
||||||
{embed}
|
{embed}
|
||||||
{description}
|
{description}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -122,8 +122,30 @@ class DetailedStatus extends ImmutablePureComponent {
|
||||||
onTranslate(status);
|
onTranslate(status);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_properStatus () {
|
||||||
|
const { status } = this.props;
|
||||||
|
|
||||||
|
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
|
||||||
|
return status.get('reblog');
|
||||||
|
} else {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getAttachmentAspectRatio () {
|
||||||
|
const attachments = this._properStatus().get('media_attachments');
|
||||||
|
|
||||||
|
if (attachments.getIn([0, 'type']) === 'video') {
|
||||||
|
return `${attachments.getIn([0, 'meta', 'original', 'width'])} / ${attachments.getIn([0, 'meta', 'original', 'height'])}`;
|
||||||
|
} else if (attachments.getIn([0, 'type']) === 'audio') {
|
||||||
|
return '16 / 9';
|
||||||
|
} else {
|
||||||
|
return (attachments.size === 1 && attachments.getIn([0, 'meta', 'small', 'aspect'])) ? attachments.getIn([0, 'meta', 'small', 'aspect']) : '3 / 2'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status;
|
const status = this._properStatus();
|
||||||
const outerStyle = { boxSizing: 'border-box' };
|
const outerStyle = { boxSizing: 'border-box' };
|
||||||
const { intl, compact, pictureInPicture } = this.props;
|
const { intl, compact, pictureInPicture } = this.props;
|
||||||
|
|
||||||
|
@ -147,7 +169,7 @@ class DetailedStatus extends ImmutablePureComponent {
|
||||||
const language = status.getIn(['translation', 'language']) || status.get('language');
|
const language = status.getIn(['translation', 'language']) || status.get('language');
|
||||||
|
|
||||||
if (pictureInPicture.get('inUse')) {
|
if (pictureInPicture.get('inUse')) {
|
||||||
media = <PictureInPicturePlaceholder />;
|
media = <PictureInPicturePlaceholder aspectRatio={this.getAttachmentAspectRatio()} />;
|
||||||
} else if (status.get('media_attachments').size > 0) {
|
} else if (status.get('media_attachments').size > 0) {
|
||||||
if (status.getIn(['media_attachments', 0, 'type']) === 'audio') {
|
if (status.getIn(['media_attachments', 0, 'type']) === 'audio') {
|
||||||
const attachment = status.getIn(['media_attachments', 0]);
|
const attachment = status.getIn(['media_attachments', 0]);
|
||||||
|
@ -178,13 +200,13 @@ class DetailedStatus extends ImmutablePureComponent {
|
||||||
<Video
|
<Video
|
||||||
preview={attachment.get('preview_url')}
|
preview={attachment.get('preview_url')}
|
||||||
frameRate={attachment.getIn(['meta', 'original', 'frame_rate'])}
|
frameRate={attachment.getIn(['meta', 'original', 'frame_rate'])}
|
||||||
|
aspectRatio={`${attachment.getIn(['meta', 'original', 'width'])} / ${attachment.getIn(['meta', 'original', 'height'])}`}
|
||||||
blurhash={attachment.get('blurhash')}
|
blurhash={attachment.get('blurhash')}
|
||||||
src={attachment.get('url')}
|
src={attachment.get('url')}
|
||||||
alt={description}
|
alt={description}
|
||||||
lang={language}
|
lang={language}
|
||||||
width={300}
|
width={300}
|
||||||
height={150}
|
height={150}
|
||||||
inline
|
|
||||||
onOpenVideo={this.handleOpenVideo}
|
onOpenVideo={this.handleOpenVideo}
|
||||||
sensitive={status.get('sensitive')}
|
sensitive={status.get('sensitive')}
|
||||||
visible={this.props.showMedia}
|
visible={this.props.showMedia}
|
||||||
|
|
|
@ -38,7 +38,7 @@ const messages = defineMessages({
|
||||||
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
|
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
|
||||||
deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
|
deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
|
||||||
redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
|
redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
|
||||||
redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' },
|
redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favorites and boosts will be lost, and replies to the original post will be orphaned.' },
|
||||||
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
|
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
|
||||||
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
|
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
|
||||||
});
|
});
|
||||||
|
|
|
@ -75,7 +75,7 @@ const messages = defineMessages({
|
||||||
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
|
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
|
||||||
deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
|
deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
|
||||||
redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
|
redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
|
||||||
redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' },
|
redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favorites and boosts will be lost, and replies to the original post will be orphaned.' },
|
||||||
revealAll: { id: 'status.show_more_all', defaultMessage: 'Show more for all' },
|
revealAll: { id: 'status.show_more_all', defaultMessage: 'Show more for all' },
|
||||||
hideAll: { id: 'status.show_less_all', defaultMessage: 'Show less for all' },
|
hideAll: { id: 'status.show_less_all', defaultMessage: 'Show less for all' },
|
||||||
statusTitleWithAttachments: { id: 'status.title.with_attachments', defaultMessage: '{user} posted {attachmentCount, plural, one {an attachment} other {# attachments}}' },
|
statusTitleWithAttachments: { id: 'status.title.with_attachments', defaultMessage: '{user} posted {attachmentCount, plural, one {an attachment} other {# attachments}}' },
|
||||||
|
@ -208,9 +208,9 @@ class Status extends ImmutablePureComponent {
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
status: ImmutablePropTypes.map,
|
status: ImmutablePropTypes.map,
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
ancestorsIds: ImmutablePropTypes.list,
|
ancestorsIds: ImmutablePropTypes.list.isRequired,
|
||||||
descendantsIds: ImmutablePropTypes.list,
|
descendantsIds: ImmutablePropTypes.list.isRequired,
|
||||||
referenceIds: ImmutablePropTypes.list,
|
referenceIds: ImmutablePropTypes.list.isRequired,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
askReplyConfirmation: PropTypes.bool,
|
askReplyConfirmation: PropTypes.bool,
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn: PropTypes.bool,
|
||||||
|
@ -237,14 +237,9 @@ class Status extends ImmutablePureComponent {
|
||||||
|
|
||||||
UNSAFE_componentWillReceiveProps (nextProps) {
|
UNSAFE_componentWillReceiveProps (nextProps) {
|
||||||
if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
|
if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
|
||||||
this._scrolledIntoView = false;
|
|
||||||
this.props.dispatch(fetchStatus(nextProps.params.statusId));
|
this.props.dispatch(fetchStatus(nextProps.params.statusId));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextProps.params.statusId && nextProps.ancestorsIds.size > this.props.ancestorsIds.size) {
|
|
||||||
this._scrolledIntoView = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nextProps.status && nextProps.status.get('id') !== this.state.loadedStatusId) {
|
if (nextProps.status && nextProps.status.get('id') !== this.state.loadedStatusId) {
|
||||||
this.setState({ showMedia: defaultMediaVisibility(nextProps.status), loadedStatusId: nextProps.status.get('id') });
|
this.setState({ showMedia: defaultMediaVisibility(nextProps.status), loadedStatusId: nextProps.status.get('id') });
|
||||||
}
|
}
|
||||||
|
@ -625,20 +620,23 @@ class Status extends ImmutablePureComponent {
|
||||||
this.node = c;
|
this.node = c;
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidUpdate () {
|
componentDidUpdate (prevProps) {
|
||||||
if (this._scrolledIntoView) {
|
const { status, ancestorsIds, multiColumn } = this.props;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { status, ancestorsIds } = this.props;
|
|
||||||
|
|
||||||
if (status && ancestorsIds && ancestorsIds.size > 0) {
|
|
||||||
const element = this.node.querySelectorAll('.focusable')[ancestorsIds.size - 1];
|
|
||||||
|
|
||||||
|
if (status && (ancestorsIds.size > prevProps.ancestorsIds.size || prevProps.status?.get('id') !== status.get('id'))) {
|
||||||
window.requestAnimationFrame(() => {
|
window.requestAnimationFrame(() => {
|
||||||
element.scrollIntoView(true);
|
this.node?.querySelector('.detailed-status__wrapper')?.scrollIntoView(true);
|
||||||
|
|
||||||
|
// In the single-column interface, `scrollIntoView` will put the post behind the header,
|
||||||
|
// so compensate for that.
|
||||||
|
if (!multiColumn) {
|
||||||
|
const offset = document.querySelector('.column-header__wrapper')?.getBoundingClientRect()?.bottom;
|
||||||
|
if (offset) {
|
||||||
|
const scrollingElement = document.scrollingElement || document.body;
|
||||||
|
scrollingElement.scrollBy(0, -offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
this._scrolledIntoView = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -172,6 +172,7 @@ class MediaModal extends ImmutablePureComponent {
|
||||||
width={image.get('width')}
|
width={image.get('width')}
|
||||||
height={image.get('height')}
|
height={image.get('height')}
|
||||||
frameRate={image.getIn(['meta', 'original', 'frame_rate'])}
|
frameRate={image.getIn(['meta', 'original', 'frame_rate'])}
|
||||||
|
aspectRatio={`${image.getIn(['meta', 'original', 'width'])} / ${image.getIn(['meta', 'original', 'height'])}`}
|
||||||
currentTime={currentTime || 0}
|
currentTime={currentTime || 0}
|
||||||
autoPlay={autoPlay || false}
|
autoPlay={autoPlay || false}
|
||||||
volume={volume || 1}
|
volume={volume || 1}
|
||||||
|
|
|
@ -24,7 +24,7 @@ const messages = defineMessages({
|
||||||
local: { id: 'column.local', defaultMessage: 'Local' },
|
local: { id: 'column.local', defaultMessage: 'Local' },
|
||||||
firehose: { id: 'column.firehose', defaultMessage: 'Live feeds' },
|
firehose: { id: 'column.firehose', defaultMessage: 'Live feeds' },
|
||||||
direct: { id: 'navigation_bar.direct', defaultMessage: 'Private mentions' },
|
direct: { id: 'navigation_bar.direct', defaultMessage: 'Private mentions' },
|
||||||
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
|
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favorites' },
|
||||||
bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
|
bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
|
||||||
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
|
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
|
||||||
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
|
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
|
||||||
|
|
|
@ -35,7 +35,7 @@ const SignInBanner = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='sign-in-banner'>
|
<div className='sign-in-banner'>
|
||||||
<p><FormattedMessage id='sign_in_banner.text' defaultMessage='Login to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.' /></p>
|
<p><FormattedMessage id='sign_in_banner.text' defaultMessage='Login to follow profiles or hashtags, favorite, share and reply to posts. You can also interact from your account on a different server.' /></p>
|
||||||
{signupButton}
|
{signupButton}
|
||||||
<a href='/auth/sign_in' className='button button--block button-tertiary'><FormattedMessage id='sign_in_banner.sign_in' defaultMessage='Login' /></a>
|
<a href='/auth/sign_in' className='button button--block button-tertiary'><FormattedMessage id='sign_in_banner.sign_in' defaultMessage='Login' /></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -49,6 +49,7 @@ class VideoModal extends ImmutablePureComponent {
|
||||||
<Video
|
<Video
|
||||||
preview={media.get('preview_url')}
|
preview={media.get('preview_url')}
|
||||||
frameRate={media.getIn(['meta', 'original', 'frame_rate'])}
|
frameRate={media.getIn(['meta', 'original', 'frame_rate'])}
|
||||||
|
aspectRatio={`${media.getIn(['meta', 'original', 'width'])} / ${media.getIn(['meta', 'original', 'height'])}`}
|
||||||
blurhash={media.get('blurhash')}
|
blurhash={media.get('blurhash')}
|
||||||
src={media.get('url')}
|
src={media.get('url')}
|
||||||
currentTime={options.startTime}
|
currentTime={options.startTime}
|
||||||
|
|
|
@ -105,6 +105,7 @@ class Video extends PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
preview: PropTypes.string,
|
preview: PropTypes.string,
|
||||||
frameRate: PropTypes.string,
|
frameRate: PropTypes.string,
|
||||||
|
aspectRatio: PropTypes.string,
|
||||||
src: PropTypes.string.isRequired,
|
src: PropTypes.string.isRequired,
|
||||||
alt: PropTypes.string,
|
alt: PropTypes.string,
|
||||||
lang: PropTypes.string,
|
lang: PropTypes.string,
|
||||||
|
@ -113,7 +114,6 @@ class Video extends PureComponent {
|
||||||
onOpenVideo: PropTypes.func,
|
onOpenVideo: PropTypes.func,
|
||||||
onCloseVideo: PropTypes.func,
|
onCloseVideo: PropTypes.func,
|
||||||
detailed: PropTypes.bool,
|
detailed: PropTypes.bool,
|
||||||
inline: PropTypes.bool,
|
|
||||||
editable: PropTypes.bool,
|
editable: PropTypes.bool,
|
||||||
alwaysVisible: PropTypes.bool,
|
alwaysVisible: PropTypes.bool,
|
||||||
visible: PropTypes.bool,
|
visible: PropTypes.bool,
|
||||||
|
@ -500,14 +500,9 @@ class Video extends PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { preview, src, inline, onOpenVideo, onCloseVideo, intl, alt, lang, detailed, sensitive, editable, blurhash, autoFocus } = this.props;
|
const { preview, src, aspectRatio, onOpenVideo, onCloseVideo, intl, alt, lang, detailed, sensitive, editable, blurhash, autoFocus } = this.props;
|
||||||
const { currentTime, duration, volume, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
|
const { currentTime, duration, volume, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
|
||||||
const progress = Math.min((currentTime / duration) * 100, 100);
|
const progress = Math.min((currentTime / duration) * 100, 100);
|
||||||
const playerStyle = {};
|
|
||||||
|
|
||||||
if (inline) {
|
|
||||||
playerStyle.aspectRatio = '16 / 9';
|
|
||||||
}
|
|
||||||
|
|
||||||
let preload;
|
let preload;
|
||||||
|
|
||||||
|
@ -527,11 +522,13 @@ class Video extends PureComponent {
|
||||||
warning = <FormattedMessage id='status.media_hidden' defaultMessage='Media hidden' />;
|
warning = <FormattedMessage id='status.media_hidden' defaultMessage='Media hidden' />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The outer wrapper is necessary to avoid reflowing the layout when going into full screen
|
||||||
return (
|
return (
|
||||||
|
<div style={{ aspectRatio }}>
|
||||||
<div
|
<div
|
||||||
role='menuitem'
|
role='menuitem'
|
||||||
className={classNames('video-player', { inactive: !revealed, detailed, inline: inline && !fullscreen, fullscreen, editable })}
|
className={classNames('video-player', { inactive: !revealed, detailed, fullscreen, editable })}
|
||||||
style={playerStyle}
|
style={{ aspectRatio }}
|
||||||
ref={this.setPlayerRef}
|
ref={this.setPlayerRef}
|
||||||
onMouseEnter={this.handleMouseEnter}
|
onMouseEnter={this.handleMouseEnter}
|
||||||
onMouseLeave={this.handleMouseLeave}
|
onMouseLeave={this.handleMouseLeave}
|
||||||
|
@ -565,12 +562,15 @@ class Video extends PureComponent {
|
||||||
onLoadedData={this.handleLoadedData}
|
onLoadedData={this.handleLoadedData}
|
||||||
onProgress={this.handleProgress}
|
onProgress={this.handleProgress}
|
||||||
onVolumeChange={this.handleVolumeChange}
|
onVolumeChange={this.handleVolumeChange}
|
||||||
style={{ ...playerStyle, width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
/>}
|
/>}
|
||||||
|
|
||||||
<div className={classNames('spoiler-button', { 'spoiler-button--hidden': revealed || editable })}>
|
<div className={classNames('spoiler-button', { 'spoiler-button--hidden': revealed || editable })}>
|
||||||
<button type='button' className='spoiler-button__overlay' onClick={this.toggleReveal}>
|
<button type='button' className='spoiler-button__overlay' onClick={this.toggleReveal}>
|
||||||
<span className='spoiler-button__overlay__label'>{warning}</span>
|
<span className='spoiler-button__overlay__label'>
|
||||||
|
{warning}
|
||||||
|
<span className='spoiler-button__overlay__action'><FormattedMessage id='status.media.show' defaultMessage='Click to show' /></span>
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -620,6 +620,7 @@ class Video extends PureComponent {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,6 @@
|
||||||
* @property {boolean} activity_api_enabled
|
* @property {boolean} activity_api_enabled
|
||||||
* @property {string} admin
|
* @property {string} admin
|
||||||
* @property {boolean=} boost_modal
|
* @property {boolean=} boost_modal
|
||||||
* @property {boolean} crop_images
|
|
||||||
* @property {boolean=} delete_modal
|
* @property {boolean=} delete_modal
|
||||||
* @property {boolean=} disable_swiping
|
* @property {boolean=} disable_swiping
|
||||||
* @property {string=} disabled_account_id
|
* @property {string=} disabled_account_id
|
||||||
|
@ -114,7 +113,6 @@ const getMeta = (prop) => initialState?.meta && initialState.meta[prop];
|
||||||
export const activityApiEnabled = getMeta('activity_api_enabled');
|
export const activityApiEnabled = getMeta('activity_api_enabled');
|
||||||
export const autoPlayGif = getMeta('auto_play_gif');
|
export const autoPlayGif = getMeta('auto_play_gif');
|
||||||
export const boostModal = getMeta('boost_modal');
|
export const boostModal = getMeta('boost_modal');
|
||||||
export const cropImages = getMeta('crop_images');
|
|
||||||
export const deleteModal = getMeta('delete_modal');
|
export const deleteModal = getMeta('delete_modal');
|
||||||
export const disableSwiping = getMeta('disable_swiping');
|
export const disableSwiping = getMeta('disable_swiping');
|
||||||
export const disabledAccountId = getMeta('disabled_account_id');
|
export const disabledAccountId = getMeta('disabled_account_id');
|
||||||
|
|
|
@ -116,7 +116,7 @@
|
||||||
"column.direct": "Private mentions",
|
"column.direct": "Private mentions",
|
||||||
"column.directory": "Browse profiles",
|
"column.directory": "Browse profiles",
|
||||||
"column.domain_blocks": "Blocked domains",
|
"column.domain_blocks": "Blocked domains",
|
||||||
"column.favourites": "Favourites",
|
"column.favourites": "Favorites",
|
||||||
"column.firehose": "Live feeds",
|
"column.firehose": "Live feeds",
|
||||||
"column.follow_requests": "Follow requests",
|
"column.follow_requests": "Follow requests",
|
||||||
"column.home": "Home",
|
"column.home": "Home",
|
||||||
|
@ -189,7 +189,7 @@
|
||||||
"confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.",
|
"confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.",
|
||||||
"confirmations.mute.message": "Are you sure you want to mute {name}?",
|
"confirmations.mute.message": "Are you sure you want to mute {name}?",
|
||||||
"confirmations.redraft.confirm": "Delete & redraft",
|
"confirmations.redraft.confirm": "Delete & redraft",
|
||||||
"confirmations.redraft.message": "Are you sure you want to delete this post and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.",
|
"confirmations.redraft.message": "Are you sure you want to delete this post and re-draft it? Favorites and boosts will be lost, and replies to the original post will be orphaned.",
|
||||||
"confirmations.reply.confirm": "Reply",
|
"confirmations.reply.confirm": "Reply",
|
||||||
"confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
|
"confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
|
||||||
"confirmations.unfollow.confirm": "Unfollow",
|
"confirmations.unfollow.confirm": "Unfollow",
|
||||||
|
@ -210,7 +210,7 @@
|
||||||
"dismissable_banner.community_timeline": "These are the most recent public posts from people whose accounts are hosted by {domain}.",
|
"dismissable_banner.community_timeline": "These are the most recent public posts from people whose accounts are hosted by {domain}.",
|
||||||
"dismissable_banner.dismiss": "Dismiss",
|
"dismissable_banner.dismiss": "Dismiss",
|
||||||
"dismissable_banner.explore_links": "These are news stories being shared the most on the social web today. Newer news stories posted by more different people are ranked higher.",
|
"dismissable_banner.explore_links": "These are news stories being shared the most on the social web today. Newer news stories posted by more different people are ranked higher.",
|
||||||
"dismissable_banner.explore_statuses": "These are posts from across the social web that are gaining traction today. Newer posts with more boosts and favourites are ranked higher.",
|
"dismissable_banner.explore_statuses": "These are posts from across the social web that are gaining traction today. Newer posts with more boosts and favorites are ranked higher.",
|
||||||
"dismissable_banner.explore_tags": "These are hashtags that are gaining traction on the social web today. Hashtags that are used by more different people are ranked higher.",
|
"dismissable_banner.explore_tags": "These are hashtags that are gaining traction on the social web today. Hashtags that are used by more different people are ranked higher.",
|
||||||
"dismissable_banner.public_timeline": "These are the most recent public posts from people on the social web that people on {domain} follow.",
|
"dismissable_banner.public_timeline": "These are the most recent public posts from people on the social web that people on {domain} follow.",
|
||||||
"embed.instructions": "Embed this post on your website by copying the code below.",
|
"embed.instructions": "Embed this post on your website by copying the code below.",
|
||||||
|
@ -239,8 +239,8 @@
|
||||||
"empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
|
"empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
|
||||||
"empty_column.domain_blocks": "There are no blocked domains yet.",
|
"empty_column.domain_blocks": "There are no blocked domains yet.",
|
||||||
"empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
|
"empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
|
||||||
"empty_column.favourited_statuses": "You don't have any favourite posts yet. When you favourite one, it will show up here.",
|
"empty_column.favourited_statuses": "You don't have any favorite posts yet. When you favorite one, it will show up here.",
|
||||||
"empty_column.favourites": "No one has favourited this post yet. When someone does, they will show up here.",
|
"empty_column.favourites": "No one has favorited this post yet. When someone does, they will show up here.",
|
||||||
"empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
|
"empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
|
||||||
"empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
|
"empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
|
||||||
"empty_column.hashtag": "There is nothing in this hashtag yet.",
|
"empty_column.hashtag": "There is nothing in this hashtag yet.",
|
||||||
|
@ -315,15 +315,15 @@
|
||||||
"home.explore_prompt.title": "This is your home base within Mastodon.",
|
"home.explore_prompt.title": "This is your home base within Mastodon.",
|
||||||
"home.hide_announcements": "Hide announcements",
|
"home.hide_announcements": "Hide announcements",
|
||||||
"home.show_announcements": "Show announcements",
|
"home.show_announcements": "Show announcements",
|
||||||
"interaction_modal.description.favourite": "With an account on Mastodon, you can favourite this post to let the author know you appreciate it and save it for later.",
|
"interaction_modal.description.favourite": "With an account on Mastodon, you can favorite this post to let the author know you appreciate it and save it for later.",
|
||||||
"interaction_modal.description.follow": "With an account on Mastodon, you can follow {name} to receive their posts in your home feed.",
|
"interaction_modal.description.follow": "With an account on Mastodon, you can follow {name} to receive their posts in your home feed.",
|
||||||
"interaction_modal.description.reblog": "With an account on Mastodon, you can boost this post to share it with your own followers.",
|
"interaction_modal.description.reblog": "With an account on Mastodon, you can boost this post to share it with your own followers.",
|
||||||
"interaction_modal.description.reply": "With an account on Mastodon, you can respond to this post.",
|
"interaction_modal.description.reply": "With an account on Mastodon, you can respond to this post.",
|
||||||
"interaction_modal.on_another_server": "On a different server",
|
"interaction_modal.on_another_server": "On a different server",
|
||||||
"interaction_modal.on_this_server": "On this server",
|
"interaction_modal.on_this_server": "On this server",
|
||||||
"interaction_modal.other_server_instructions": "Copy and paste this URL into the search field of your favourite Mastodon app or the web interface of your Mastodon server.",
|
"interaction_modal.other_server_instructions": "Copy and paste this URL into the search field of your favorite Mastodon app or the web interface of your Mastodon server.",
|
||||||
"interaction_modal.preamble": "Since Mastodon is decentralized, you can use your existing account hosted by another Mastodon server or compatible platform if you don't have an account on this one.",
|
"interaction_modal.preamble": "Since Mastodon is decentralized, you can use your existing account hosted by another Mastodon server or compatible platform if you don't have an account on this one.",
|
||||||
"interaction_modal.title.favourite": "Favourite {name}'s post",
|
"interaction_modal.title.favourite": "Favorite {name}'s post",
|
||||||
"interaction_modal.title.follow": "Follow {name}",
|
"interaction_modal.title.follow": "Follow {name}",
|
||||||
"interaction_modal.title.reblog": "Boost {name}'s post",
|
"interaction_modal.title.reblog": "Boost {name}'s post",
|
||||||
"interaction_modal.title.reply": "Reply to {name}'s post",
|
"interaction_modal.title.reply": "Reply to {name}'s post",
|
||||||
|
@ -339,8 +339,8 @@
|
||||||
"keyboard_shortcuts.direct": "to open private mentions column",
|
"keyboard_shortcuts.direct": "to open private mentions column",
|
||||||
"keyboard_shortcuts.down": "Move down in the list",
|
"keyboard_shortcuts.down": "Move down in the list",
|
||||||
"keyboard_shortcuts.enter": "Open post",
|
"keyboard_shortcuts.enter": "Open post",
|
||||||
"keyboard_shortcuts.favourite": "Favourite post",
|
"keyboard_shortcuts.favourite": "Favorite post",
|
||||||
"keyboard_shortcuts.favourites": "Open favourites list",
|
"keyboard_shortcuts.favourites": "Open favorites list",
|
||||||
"keyboard_shortcuts.federated": "Open federated timeline",
|
"keyboard_shortcuts.federated": "Open federated timeline",
|
||||||
"keyboard_shortcuts.heading": "Keyboard shortcuts",
|
"keyboard_shortcuts.heading": "Keyboard shortcuts",
|
||||||
"keyboard_shortcuts.home": "Open home timeline",
|
"keyboard_shortcuts.home": "Open home timeline",
|
||||||
|
@ -371,6 +371,7 @@
|
||||||
"lightbox.previous": "Previous",
|
"lightbox.previous": "Previous",
|
||||||
"limited_account_hint.action": "Show profile anyway",
|
"limited_account_hint.action": "Show profile anyway",
|
||||||
"limited_account_hint.title": "This profile has been hidden by the moderators of {domain}.",
|
"limited_account_hint.title": "This profile has been hidden by the moderators of {domain}.",
|
||||||
|
"link_preview.author": "By {name}",
|
||||||
"lists.account.add": "Add to list",
|
"lists.account.add": "Add to list",
|
||||||
"lists.account.remove": "Remove from list",
|
"lists.account.remove": "Remove from list",
|
||||||
"lists.antennas": "Related antennas",
|
"lists.antennas": "Related antennas",
|
||||||
|
@ -405,7 +406,7 @@
|
||||||
"navigation_bar.domain_blocks": "Blocked domains",
|
"navigation_bar.domain_blocks": "Blocked domains",
|
||||||
"navigation_bar.edit_profile": "Edit profile",
|
"navigation_bar.edit_profile": "Edit profile",
|
||||||
"navigation_bar.explore": "Explore",
|
"navigation_bar.explore": "Explore",
|
||||||
"navigation_bar.favourites": "Favourites",
|
"navigation_bar.favourites": "Favorites",
|
||||||
"navigation_bar.filters": "Muted words",
|
"navigation_bar.filters": "Muted words",
|
||||||
"navigation_bar.follow_requests": "Follow requests",
|
"navigation_bar.follow_requests": "Follow requests",
|
||||||
"navigation_bar.followed_tags": "Followed hashtags",
|
"navigation_bar.followed_tags": "Followed hashtags",
|
||||||
|
@ -424,7 +425,7 @@
|
||||||
"notification.admin.report": "{name} reported {target}",
|
"notification.admin.report": "{name} reported {target}",
|
||||||
"notification.admin.sign_up": "{name} signed up",
|
"notification.admin.sign_up": "{name} signed up",
|
||||||
"notification.emoji_reaction": "{name} reacted your post with emoji",
|
"notification.emoji_reaction": "{name} reacted your post with emoji",
|
||||||
"notification.favourite": "{name} favourited your post",
|
"notification.favourite": "{name} favorited your post",
|
||||||
"notification.follow": "{name} followed you",
|
"notification.follow": "{name} followed you",
|
||||||
"notification.follow_request": "{name} has requested to follow you",
|
"notification.follow_request": "{name} has requested to follow you",
|
||||||
"notification.mention": "{name} mentioned you",
|
"notification.mention": "{name} mentioned you",
|
||||||
|
@ -439,7 +440,7 @@
|
||||||
"notifications.column_settings.admin.report": "New reports:",
|
"notifications.column_settings.admin.report": "New reports:",
|
||||||
"notifications.column_settings.admin.sign_up": "New sign-ups:",
|
"notifications.column_settings.admin.sign_up": "New sign-ups:",
|
||||||
"notifications.column_settings.alert": "Desktop notifications",
|
"notifications.column_settings.alert": "Desktop notifications",
|
||||||
"notifications.column_settings.favourite": "Favourites:",
|
"notifications.column_settings.favourite": "Favorites:",
|
||||||
"notifications.column_settings.filter_bar.advanced": "Display all categories",
|
"notifications.column_settings.filter_bar.advanced": "Display all categories",
|
||||||
"notifications.column_settings.filter_bar.category": "Quick filter bar",
|
"notifications.column_settings.filter_bar.category": "Quick filter bar",
|
||||||
"notifications.column_settings.filter_bar.show_bar": "Show filter bar",
|
"notifications.column_settings.filter_bar.show_bar": "Show filter bar",
|
||||||
|
@ -457,7 +458,7 @@
|
||||||
"notifications.column_settings.update": "Edits:",
|
"notifications.column_settings.update": "Edits:",
|
||||||
"notifications.filter.all": "All",
|
"notifications.filter.all": "All",
|
||||||
"notifications.filter.boosts": "Boosts",
|
"notifications.filter.boosts": "Boosts",
|
||||||
"notifications.filter.favourites": "Favourites",
|
"notifications.filter.favourites": "Favorites",
|
||||||
"notifications.filter.follows": "Follows",
|
"notifications.filter.follows": "Follows",
|
||||||
"notifications.filter.mentions": "Mentions",
|
"notifications.filter.mentions": "Mentions",
|
||||||
"notifications.filter.polls": "Poll results",
|
"notifications.filter.polls": "Poll results",
|
||||||
|
@ -623,7 +624,7 @@
|
||||||
"server_banner.server_stats": "Server stats:",
|
"server_banner.server_stats": "Server stats:",
|
||||||
"sign_in_banner.create_account": "Create account",
|
"sign_in_banner.create_account": "Create account",
|
||||||
"sign_in_banner.sign_in": "Login",
|
"sign_in_banner.sign_in": "Login",
|
||||||
"sign_in_banner.text": "Login to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
|
"sign_in_banner.text": "Login to follow profiles or hashtags, favorite, share and reply to posts. You can also interact from your account on a different server.",
|
||||||
"status.admin_account": "Open moderation interface for @{name}",
|
"status.admin_account": "Open moderation interface for @{name}",
|
||||||
"status.admin_domain": "Open moderation interface for {domain}",
|
"status.admin_domain": "Open moderation interface for {domain}",
|
||||||
"status.admin_status": "Open this post in the moderation interface",
|
"status.admin_status": "Open this post in the moderation interface",
|
||||||
|
@ -643,7 +644,7 @@
|
||||||
"status.emoji_reaction": "Stamp",
|
"status.emoji_reaction": "Stamp",
|
||||||
"status.emoji_reaction.pick": "Pick stamp",
|
"status.emoji_reaction.pick": "Pick stamp",
|
||||||
"status.expiration.add": "Set status expired time",
|
"status.expiration.add": "Set status expired time",
|
||||||
"status.favourite": "Favourite",
|
"status.favourite": "Favorite",
|
||||||
"status.filter": "Filter this post",
|
"status.filter": "Filter this post",
|
||||||
"status.filtered": "Filtered",
|
"status.filtered": "Filtered",
|
||||||
"status.hide": "Hide post",
|
"status.hide": "Hide post",
|
||||||
|
|
|
@ -24,13 +24,16 @@ html {
|
||||||
.column > .scrollable,
|
.column > .scrollable,
|
||||||
.getting-started,
|
.getting-started,
|
||||||
.column-inline-form,
|
.column-inline-form,
|
||||||
.error-column,
|
|
||||||
.regeneration-indicator {
|
.regeneration-indicator {
|
||||||
background: $white;
|
background: $white;
|
||||||
border: 1px solid lighten($ui-base-color, 8%);
|
border: 1px solid lighten($ui-base-color, 8%);
|
||||||
border-top: 0;
|
border-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error-column {
|
||||||
|
border: 1px solid lighten($ui-base-color, 8%);
|
||||||
|
}
|
||||||
|
|
||||||
.column > .scrollable.about {
|
.column > .scrollable.about {
|
||||||
border-top: 1px solid lighten($ui-base-color, 8%);
|
border-top: 1px solid lighten($ui-base-color, 8%);
|
||||||
}
|
}
|
||||||
|
@ -77,6 +80,10 @@ html {
|
||||||
background: $white;
|
background: $white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.column-header {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.column-header__button.active {
|
.column-header__button.active {
|
||||||
color: $ui-highlight-color;
|
color: $ui-highlight-color;
|
||||||
|
|
||||||
|
@ -423,7 +430,7 @@ html {
|
||||||
.column-header__collapsible-inner {
|
.column-header__collapsible-inner {
|
||||||
background: darken($ui-base-color, 4%);
|
background: darken($ui-base-color, 4%);
|
||||||
border: 1px solid lighten($ui-base-color, 8%);
|
border: 1px solid lighten($ui-base-color, 8%);
|
||||||
border-top: 0;
|
border-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard__quick-access,
|
.dashboard__quick-access,
|
||||||
|
|
|
@ -5,7 +5,7 @@ $white: #ffffff;
|
||||||
$classic-base-color: #282c37;
|
$classic-base-color: #282c37;
|
||||||
$classic-primary-color: #9baec8;
|
$classic-primary-color: #9baec8;
|
||||||
$classic-secondary-color: #d9e1e8;
|
$classic-secondary-color: #d9e1e8;
|
||||||
$classic-highlight-color: #6364ff;
|
$classic-highlight-color: #858afa;
|
||||||
|
|
||||||
$blurple-600: #563acc; // Iris
|
$blurple-600: #563acc; // Iris
|
||||||
$blurple-500: #6364ff; // Brand purple
|
$blurple-500: #6364ff; // Brand purple
|
||||||
|
|
|
@ -161,11 +161,22 @@ body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
&:focus {
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: $ui-button-icon-focus-outline;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus:not(:focus-visible) {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:focus {
|
&:focus:not(:focus-visible) {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,6 +74,10 @@
|
||||||
background-color: $ui-button-focus-background-color;
|
background-color: $ui-button-focus-background-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
outline: $ui-button-icon-focus-outline;
|
||||||
|
}
|
||||||
|
|
||||||
&--destructive {
|
&--destructive {
|
||||||
&:active,
|
&:active,
|
||||||
&:focus,
|
&:focus,
|
||||||
|
@ -98,16 +102,6 @@
|
||||||
transition: none;
|
transition: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-moz-focus-inner {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-moz-focus-inner,
|
|
||||||
&:focus,
|
|
||||||
&:active {
|
|
||||||
outline: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.button-secondary {
|
&.button-secondary {
|
||||||
color: $ui-button-secondary-color;
|
color: $ui-button-secondary-color;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
@ -197,8 +191,6 @@
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 100ms ease-in;
|
|
||||||
transition-property: background-color, color;
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
@ -211,12 +203,10 @@
|
||||||
&:focus {
|
&:focus {
|
||||||
color: lighten($action-button-color, 7%);
|
color: lighten($action-button-color, 7%);
|
||||||
background-color: rgba($action-button-color, 0.15);
|
background-color: rgba($action-button-color, 0.15);
|
||||||
transition: all 200ms ease-out;
|
|
||||||
transition-property: background-color, color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus {
|
&:focus-visible {
|
||||||
background-color: rgba($action-button-color, 0.3);
|
outline: $ui-button-icon-focus-outline;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.disabled {
|
&.disabled {
|
||||||
|
@ -225,20 +215,6 @@
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: $highlight-text-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-moz-focus-inner {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-moz-focus-inner,
|
|
||||||
&:focus,
|
|
||||||
&:active {
|
|
||||||
outline: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.inverted {
|
&.inverted {
|
||||||
color: $lighter-text-color;
|
color: $lighter-text-color;
|
||||||
|
|
||||||
|
@ -249,8 +225,8 @@
|
||||||
background-color: rgba($lighter-text-color, 0.15);
|
background-color: rgba($lighter-text-color, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus {
|
&:focus-visible {
|
||||||
background-color: rgba($lighter-text-color, 0.3);
|
outline: $ui-button-icon-focus-outline;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.disabled {
|
&.disabled {
|
||||||
|
@ -261,6 +237,13 @@
|
||||||
&.active {
|
&.active {
|
||||||
color: $highlight-text-color;
|
color: $highlight-text-color;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:active,
|
||||||
|
&:focus {
|
||||||
|
color: $highlight-text-color;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
&.disabled {
|
&.disabled {
|
||||||
color: lighten($highlight-text-color, 13%);
|
color: lighten($highlight-text-color, 13%);
|
||||||
}
|
}
|
||||||
|
@ -269,13 +252,14 @@
|
||||||
|
|
||||||
&.overlayed {
|
&.overlayed {
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
background: rgba($base-overlay-background, 0.6);
|
background: rgba($black, 0.65);
|
||||||
color: rgba($primary-text-color, 0.7);
|
backdrop-filter: blur(10px) saturate(180%) contrast(75%) brightness(70%);
|
||||||
|
color: rgba($white, 0.7);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba($base-overlay-background, 0.9);
|
background: rgba($black, 0.9);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,21 +289,16 @@
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
padding: 0 3px;
|
padding: 0 3px;
|
||||||
line-height: 27px;
|
line-height: 27px;
|
||||||
outline: 0;
|
|
||||||
transition: all 100ms ease-in;
|
|
||||||
transition-property: background-color, color;
|
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:active,
|
&:active,
|
||||||
&:focus {
|
&:focus {
|
||||||
color: darken($lighter-text-color, 7%);
|
color: darken($lighter-text-color, 7%);
|
||||||
background-color: rgba($lighter-text-color, 0.15);
|
background-color: rgba($lighter-text-color, 0.15);
|
||||||
transition: all 200ms ease-out;
|
|
||||||
transition-property: background-color, color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus {
|
&:focus-visible {
|
||||||
background-color: rgba($lighter-text-color, 0.3);
|
outline: $ui-button-icon-focus-outline;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.disabled {
|
&.disabled {
|
||||||
|
@ -330,16 +309,13 @@
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
color: $highlight-text-color;
|
color: $highlight-text-color;
|
||||||
}
|
|
||||||
|
|
||||||
&::-moz-focus-inner {
|
&:hover,
|
||||||
border: 0;
|
&:active,
|
||||||
|
&:focus {
|
||||||
|
color: $highlight-text-color;
|
||||||
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-moz-focus-inner,
|
|
||||||
&:focus,
|
|
||||||
&:active {
|
|
||||||
outline: 0 !important;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -735,7 +711,6 @@ body > [data-popper-placement] {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
|
||||||
.compose-form__publish-button-wrapper {
|
.compose-form__publish-button-wrapper {
|
||||||
overflow: hidden;
|
|
||||||
padding-top: 15px;
|
padding-top: 15px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1433,6 +1408,10 @@ body > [data-popper-placement] {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.scrollable > div:first-child .detailed-status {
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.detailed-status__meta {
|
.detailed-status__meta {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
color: $dark-text-color;
|
color: $dark-text-color;
|
||||||
|
@ -1984,13 +1963,6 @@ a.account__display-name {
|
||||||
.navigation-bar__actions {
|
.navigation-bar__actions {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.icon-button.close {
|
|
||||||
position: absolute;
|
|
||||||
pointer-events: none;
|
|
||||||
transform: scale(0, 1) translate(-100%, 0);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.compose__action-bar .icon-button {
|
.compose__action-bar .icon-button {
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
transform: scale(1, 1) translate(0, 0);
|
transform: scale(1, 1) translate(0, 0);
|
||||||
|
@ -2000,19 +1972,21 @@ a.account__display-name {
|
||||||
}
|
}
|
||||||
|
|
||||||
.navigation-bar__profile {
|
.navigation-bar__profile {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.navigation-bar__profile-account {
|
.navigation-bar__profile-account {
|
||||||
display: block;
|
display: inline;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navigation-bar__profile-edit {
|
.navigation-bar__profile-edit {
|
||||||
|
display: inline;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
@ -2069,7 +2043,7 @@ a.account__display-name {
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
line-height: inherit;
|
line-height: inherit;
|
||||||
|
|
||||||
&:focus {
|
&:focus-visible {
|
||||||
outline: 1px dotted;
|
outline: 1px dotted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3590,12 +3564,10 @@ button.icon-button.active i.fa-retweet {
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-card {
|
.status-card {
|
||||||
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
border: 1px solid lighten($ui-base-color, 8%);
|
color: $darker-text-color;
|
||||||
border-radius: 4px;
|
|
||||||
color: $dark-text-color;
|
|
||||||
margin-top: 14px;
|
margin-top: 14px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -3649,8 +3621,29 @@ button.icon-button.active i.fa-retweet {
|
||||||
a.status-card {
|
a.status-card {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover,
|
||||||
background: lighten($ui-base-color, 8%);
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
.status-card__title,
|
||||||
|
.status-card__host,
|
||||||
|
.status-card__author {
|
||||||
|
color: $highlight-text-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-card a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
.status-card__title,
|
||||||
|
.status-card__host,
|
||||||
|
.status-card__author {
|
||||||
|
color: $highlight-text-color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3676,42 +3669,42 @@ a.status-card {
|
||||||
|
|
||||||
.status-card__title {
|
.status-card__title {
|
||||||
display: block;
|
display: block;
|
||||||
font-weight: 500;
|
font-weight: 700;
|
||||||
margin-bottom: 5px;
|
font-size: 19px;
|
||||||
color: $darker-text-color;
|
line-height: 24px;
|
||||||
|
color: $primary-text-color;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-card__content {
|
.status-card__content {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 14px 14px 14px 8px;
|
padding: 15px 0;
|
||||||
}
|
padding-bottom: 0;
|
||||||
|
|
||||||
.status-card__description {
|
|
||||||
color: $darker-text-color;
|
|
||||||
overflow: hidden;
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-card__host {
|
.status-card__host {
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: 5px;
|
font-size: 14px;
|
||||||
font-size: 13px;
|
margin-bottom: 8px;
|
||||||
overflow: hidden;
|
}
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
.status-card__author {
|
||||||
|
display: block;
|
||||||
|
margin-top: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: $primary-text-color;
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-card__image {
|
.status-card__image {
|
||||||
flex: 0 0 100px;
|
width: 100%;
|
||||||
background: lighten($ui-base-color, 8%);
|
background: lighten($ui-base-color, 8%);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
& > .fa {
|
& > .fa {
|
||||||
font-size: 21px;
|
font-size: 21px;
|
||||||
|
@ -3723,50 +3716,8 @@ a.status-card {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-card.horizontal {
|
|
||||||
display: block;
|
|
||||||
|
|
||||||
.status-card__image {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-card__image-image,
|
|
||||||
.status-card__image-preview {
|
|
||||||
border-radius: 4px 4px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-card__title {
|
|
||||||
white-space: inherit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.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 {
|
.status-card__image-image {
|
||||||
border-radius: 4px 0 0 4px;
|
border-radius: 8px;
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -3777,7 +3728,7 @@ a.status-card.compact:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-card__image-preview {
|
.status-card__image-preview {
|
||||||
border-radius: 4px 0 0 4px;
|
border-radius: 8px;
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -3932,7 +3883,6 @@ a.status-card.compact:hover {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
& > button {
|
& > button {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -3947,6 +3897,10 @@ a.status-card.compact:hover {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
outline: $ui-button-icon-focus-outline;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& > .column-header__back-button {
|
& > .column-header__back-button {
|
||||||
|
@ -3987,10 +3941,18 @@ a.status-card.compact:hover {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
padding: 0 15px;
|
padding: 0 15px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-start-end-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: lighten($darker-text-color, 4%);
|
color: lighten($darker-text-color, 4%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
outline: $ui-button-icon-focus-outline;
|
||||||
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
color: $primary-text-color;
|
color: $primary-text-color;
|
||||||
background: lighten($ui-base-color, 4%);
|
background: lighten($ui-base-color, 4%);
|
||||||
|
@ -4279,6 +4241,7 @@ a.status-card.compact:hover {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
color: $white;
|
||||||
|
|
||||||
&__label {
|
&__label {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -4286,7 +4249,6 @@ a.status-card.compact:hover {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
color: $primary-text-color;
|
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
@ -4648,7 +4610,7 @@ a.status-card.compact:hover {
|
||||||
.emoji-picker-dropdown__menu {
|
.emoji-picker-dropdown__menu {
|
||||||
background: $simple-background-color;
|
background: $simple-background-color;
|
||||||
position: relative;
|
position: relative;
|
||||||
box-shadow: 4px 4px 6px rgba($base-shadow-color, 0.4);
|
box-shadow: var(--dropdown-shadow);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
@ -4807,11 +4769,6 @@ a.status-card.compact:hover {
|
||||||
outline: 0;
|
outline: 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:active,
|
|
||||||
&:focus {
|
|
||||||
outline: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
img {
|
||||||
filter: grayscale(100%);
|
filter: grayscale(100%);
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
|
@ -4827,6 +4784,13 @@ a.status-card.compact:hover {
|
||||||
img {
|
img {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
filter: none;
|
filter: none;
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
img {
|
||||||
|
outline: $ui-button-icon-focus-outline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4839,7 +4803,7 @@ a.status-card.compact:hover {
|
||||||
.privacy-dropdown__dropdown,
|
.privacy-dropdown__dropdown,
|
||||||
.expiration-dropdown__dropdown {
|
.expiration-dropdown__dropdown {
|
||||||
background: $simple-background-color;
|
background: $simple-background-color;
|
||||||
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
|
box-shadow: var(--dropdown-shadow);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
@ -4921,19 +4885,6 @@ a.status-card.compact:hover {
|
||||||
.expiration-dropdown__value {
|
.expiration-dropdown__value {
|
||||||
background: $simple-background-color;
|
background: $simple-background-color;
|
||||||
border-radius: 4px 4px 0 0;
|
border-radius: 4px 4px 0 0;
|
||||||
box-shadow: 0 -4px 4px rgba($base-shadow-color, 0.1);
|
|
||||||
|
|
||||||
.icon-button {
|
|
||||||
transition: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
background: $ui-highlight-color;
|
|
||||||
|
|
||||||
.icon-button {
|
|
||||||
color: $primary-text-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.top .privacy-dropdown__value,
|
&.top .privacy-dropdown__value,
|
||||||
|
@ -4944,7 +4895,7 @@ a.status-card.compact:hover {
|
||||||
.privacy-dropdown__dropdown,
|
.privacy-dropdown__dropdown,
|
||||||
.expiration-dropdown__dropdown {
|
.expiration-dropdown__dropdown {
|
||||||
display: block;
|
display: block;
|
||||||
box-shadow: 2px 4px 6px rgba($base-shadow-color, 0.1);
|
box-shadow: var(--dropdown-shadow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4963,7 +4914,7 @@ a.status-card.compact:hover {
|
||||||
.language-dropdown {
|
.language-dropdown {
|
||||||
&__dropdown {
|
&__dropdown {
|
||||||
background: $simple-background-color;
|
background: $simple-background-color;
|
||||||
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
|
box-shadow: var(--dropdown-shadow);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
@ -5151,7 +5102,6 @@ a.status-card.compact:hover {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 16px;
|
top: 16px;
|
||||||
inset-inline-end: 10px;
|
inset-inline-end: 10px;
|
||||||
z-index: 2;
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: all 100ms linear;
|
transition: all 100ms linear;
|
||||||
|
@ -5290,9 +5240,9 @@ a.status-card.compact:hover {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-modal__container {
|
.video-modal .video-player {
|
||||||
|
max-height: 80vh;
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
max-height: 100vh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.audio-modal__container {
|
.audio-modal__container {
|
||||||
|
@ -6311,7 +6261,7 @@ a.status-card.compact:hover {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-radius: 4px;
|
border-radius: 8px;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 64px;
|
min-height: 64px;
|
||||||
|
@ -6342,7 +6292,7 @@ a.status-card.compact:hover {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: block;
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
border-radius: 4px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&--tall {
|
&--tall {
|
||||||
|
@ -6428,7 +6378,7 @@ a.status-card.compact:hover {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: relative;
|
position: relative;
|
||||||
background: darken($ui-base-color, 8%);
|
background: darken($ui-base-color, 8%);
|
||||||
border-radius: 4px;
|
border-radius: 8px;
|
||||||
padding-bottom: 44px;
|
padding-bottom: 44px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
@ -6495,7 +6445,7 @@ a.status-card.compact:hover {
|
||||||
position: relative;
|
position: relative;
|
||||||
background: $base-shadow-color;
|
background: $base-shadow-color;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
border-radius: 4px;
|
border-radius: 8px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
color: $white;
|
color: $white;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -6512,8 +6462,6 @@ a.status-card.compact:hover {
|
||||||
|
|
||||||
video {
|
video {
|
||||||
display: block;
|
display: block;
|
||||||
max-width: 100vw;
|
|
||||||
max-height: 80vh;
|
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6521,22 +6469,15 @@ a.status-card.compact:hover {
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
aspect-ratio: auto !important;
|
||||||
|
|
||||||
video {
|
video {
|
||||||
max-width: 100% !important;
|
|
||||||
max-height: 100% !important;
|
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.inline {
|
|
||||||
video {
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__controls {
|
&__controls {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
direction: ltr;
|
direction: ltr;
|
||||||
|
@ -8337,6 +8278,7 @@ noscript {
|
||||||
.search__input {
|
.search__input {
|
||||||
border: 1px solid lighten($ui-base-color, 8%);
|
border: 1px solid lighten($ui-base-color, 8%);
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
padding-inline-end: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search__popout {
|
.search__popout {
|
||||||
|
@ -8365,8 +8307,9 @@ noscript {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: $primary-text-color;
|
color: $primary-text-color;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
padding: 15px 0;
|
padding: 15px;
|
||||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||||
|
gap: 15px;
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
|
@ -8375,33 +8318,40 @@ noscript {
|
||||||
&:hover,
|
&:hover,
|
||||||
&:active,
|
&:active,
|
||||||
&:focus {
|
&:focus {
|
||||||
background-color: lighten($ui-base-color, 4%);
|
color: $highlight-text-color;
|
||||||
|
|
||||||
|
.story__details__publisher,
|
||||||
|
.story__details__shared {
|
||||||
|
color: $highlight-text-color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__details {
|
&__details {
|
||||||
padding: 0 15px;
|
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
|
|
||||||
&__publisher {
|
&__publisher {
|
||||||
color: $darker-text-color;
|
color: $darker-text-color;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__title {
|
&__title {
|
||||||
font-size: 19px;
|
font-size: 19px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__shared {
|
&__shared {
|
||||||
color: $darker-text-color;
|
color: $darker-text-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__thumbnail {
|
&__thumbnail {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
margin: 0 15px;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 120px;
|
width: 120px;
|
||||||
height: 120px;
|
height: 120px;
|
||||||
|
@ -8412,7 +8362,7 @@ noscript {
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
border-radius: 4px;
|
border-radius: 8px;
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -8421,7 +8371,7 @@ noscript {
|
||||||
}
|
}
|
||||||
|
|
||||||
&__preview {
|
&__preview {
|
||||||
border-radius: 4px;
|
border-radius: 8px;
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -8437,6 +8387,23 @@ noscript {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.expanded {
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.story__thumbnail {
|
||||||
|
order: 1;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
aspect-ratio: 1.91 / 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.story__details {
|
||||||
|
order: 2;
|
||||||
|
width: 100%;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.server-banner {
|
.server-banner {
|
||||||
|
|
|
@ -105,7 +105,7 @@
|
||||||
&__input {
|
&__input {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
border: 1px solid $ui-primary-color;
|
border: 1px solid $ui-button-background-color;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
width: 18px;
|
width: 18px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
|
@ -121,15 +121,10 @@
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
|
||||||
border-color: $valid-value-color;
|
|
||||||
background: $valid-value-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active,
|
&:active,
|
||||||
&:focus,
|
&:focus,
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: lighten($valid-value-color, 15%);
|
border-color: $ui-button-focus-background-color;
|
||||||
border-width: 4px;
|
border-width: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,6 +236,14 @@
|
||||||
color: $action-button-color;
|
color: $action-button-color;
|
||||||
border-color: $action-button-color;
|
border-color: $action-button-color;
|
||||||
margin-inline-end: 5px;
|
margin-inline-end: 5px;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&.active {
|
||||||
|
border-color: $action-button-color;
|
||||||
|
background-color: $action-button-color;
|
||||||
|
color: $ui-button-color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
|
|
|
@ -5,6 +5,7 @@ $red-600: #b7253d !default; // Deep Carmine
|
||||||
$red-500: #df405a !default; // Cerise
|
$red-500: #df405a !default; // Cerise
|
||||||
$blurple-600: #563acc; // Iris
|
$blurple-600: #563acc; // Iris
|
||||||
$blurple-500: #6364ff; // Brand purple
|
$blurple-500: #6364ff; // Brand purple
|
||||||
|
$blurple-400: #7477fd; // Medium slate blue
|
||||||
$blurple-300: #858afa; // Faded Blue
|
$blurple-300: #858afa; // Faded Blue
|
||||||
$grey-600: #4e4c5a; // Trout
|
$grey-600: #4e4c5a; // Trout
|
||||||
$grey-100: #dadaf3; // Topaz
|
$grey-100: #dadaf3; // Topaz
|
||||||
|
@ -61,6 +62,9 @@ $ui-button-tertiary-focus-color: $white !default;
|
||||||
$ui-button-destructive-background-color: $red-500 !default;
|
$ui-button-destructive-background-color: $red-500 !default;
|
||||||
$ui-button-destructive-focus-background-color: $red-600 !default;
|
$ui-button-destructive-focus-background-color: $red-600 !default;
|
||||||
|
|
||||||
|
$ui-button-icon-focus-outline: solid 2px $blurple-400 !default;
|
||||||
|
$ui-button-icon-hover-background-color: rgba(140, 141, 255, 40%) !default;
|
||||||
|
|
||||||
// Variables for texts
|
// Variables for texts
|
||||||
$primary-text-color: $white !default;
|
$primary-text-color: $white !default;
|
||||||
$darker-text-color: $ui-primary-color !default;
|
$darker-text-color: $ui-primary-color !default;
|
||||||
|
|
|
@ -4,6 +4,8 @@ module ApplicationExtension
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
included do
|
included do
|
||||||
|
has_many :created_users, class_name: 'User', foreign_key: 'created_by_application_id', inverse_of: :created_by_application
|
||||||
|
|
||||||
validates :name, length: { maximum: 60 }
|
validates :name, length: { maximum: 60 }
|
||||||
validates :website, url: true, length: { maximum: 2_000 }, if: :website?
|
validates :website, url: true, length: { maximum: 2_000 }, if: :website?
|
||||||
validates :redirect_uri, length: { maximum: 2_000 }
|
validates :redirect_uri, length: { maximum: 2_000 }
|
||||||
|
|
|
@ -124,6 +124,7 @@ class LinkDetailsExtractor
|
||||||
author_url: author_url || '',
|
author_url: author_url || '',
|
||||||
embed_url: embed_url || '',
|
embed_url: embed_url || '',
|
||||||
language: language,
|
language: language,
|
||||||
|
created_at: published_at.presence || Time.now.utc,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -159,6 +160,10 @@ class LinkDetailsExtractor
|
||||||
html_entities.decode(structured_data&.description || opengraph_tag('og:description') || meta_tag('description'))
|
html_entities.decode(structured_data&.description || opengraph_tag('og:description') || meta_tag('description'))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def published_at
|
||||||
|
structured_data&.date_published || opengraph_tag('article:published_time')
|
||||||
|
end
|
||||||
|
|
||||||
def image
|
def image
|
||||||
valid_url_or_nil(opengraph_tag('og:image'))
|
valid_url_or_nil(opengraph_tag('og:image'))
|
||||||
end
|
end
|
||||||
|
|
|
@ -284,11 +284,11 @@ class Request
|
||||||
end
|
end
|
||||||
|
|
||||||
until socks.empty?
|
until socks.empty?
|
||||||
_, available_socks, = IO.select(nil, socks, nil, Request::TIMEOUT[:connect])
|
_, available_socks, = IO.select(nil, socks, nil, Request::TIMEOUT[:connect_timeout])
|
||||||
|
|
||||||
if available_socks.nil?
|
if available_socks.nil?
|
||||||
socks.each(&:close)
|
socks.each(&:close)
|
||||||
raise HTTP::TimeoutError, "Connect timed out after #{Request::TIMEOUT[:connect]} seconds"
|
raise HTTP::TimeoutError, "Connect timed out after #{Request::TIMEOUT[:connect_timeout]} seconds"
|
||||||
end
|
end
|
||||||
|
|
||||||
available_socks.each do |sock|
|
available_socks.each do |sock|
|
||||||
|
|
10
app/lib/vacuum/applications_vacuum.rb
Normal file
10
app/lib/vacuum/applications_vacuum.rb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Vacuum::ApplicationsVacuum
|
||||||
|
def perform
|
||||||
|
Doorkeeper::Application.where(owner_id: nil)
|
||||||
|
.where.missing(:created_users, :access_tokens, :access_grants)
|
||||||
|
.where(created_at: ...1.day.ago)
|
||||||
|
.in_batches.delete_all
|
||||||
|
end
|
||||||
|
end
|
|
@ -5,7 +5,7 @@ class ApplicationRecord < ActiveRecord::Base
|
||||||
|
|
||||||
include Remotable
|
include Remotable
|
||||||
|
|
||||||
connects_to database: { writing: :primary, reading: :read }
|
connects_to database: { writing: :primary, reading: ENV['DB_REPLICA_NAME'] || ENV['READ_DATABASE_URL'] ? :read : :primary }
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
def update_index(_type_name, *_args, &_block)
|
def update_index(_type_name, *_args, &_block)
|
||||||
|
|
|
@ -151,10 +151,6 @@ module HasUserSettings
|
||||||
settings['web.trends']
|
settings['web.trends']
|
||||||
end
|
end
|
||||||
|
|
||||||
def setting_crop_images
|
|
||||||
settings['web.crop_images']
|
|
||||||
end
|
|
||||||
|
|
||||||
def setting_disable_swiping
|
def setting_disable_swiping
|
||||||
settings['web.disable_swiping']
|
settings['web.disable_swiping']
|
||||||
end
|
end
|
||||||
|
|
|
@ -58,7 +58,8 @@ class Report < ApplicationRecord
|
||||||
|
|
||||||
before_validation :set_uri, only: :create
|
before_validation :set_uri, only: :create
|
||||||
|
|
||||||
after_create_commit :trigger_webhooks
|
after_create_commit :trigger_create_webhooks
|
||||||
|
after_update_commit :trigger_update_webhooks
|
||||||
|
|
||||||
def object_type
|
def object_type
|
||||||
:flag
|
:flag
|
||||||
|
@ -155,7 +156,11 @@ class Report < ApplicationRecord
|
||||||
errors.add(:rule_ids, I18n.t('reports.errors.invalid_rules')) unless rules.size == rule_ids&.size
|
errors.add(:rule_ids, I18n.t('reports.errors.invalid_rules')) unless rules.size == rule_ids&.size
|
||||||
end
|
end
|
||||||
|
|
||||||
def trigger_webhooks
|
def trigger_create_webhooks
|
||||||
TriggerWebhookWorker.perform_async('report.created', 'Report', id)
|
TriggerWebhookWorker.perform_async('report.created', 'Report', id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def trigger_update_webhooks
|
||||||
|
TriggerWebhookWorker.perform_async('report.updated', 'Report', id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -32,7 +32,6 @@ class UserSettings
|
||||||
setting :emoji_reaction_streaming_notify_impl2, default: false
|
setting :emoji_reaction_streaming_notify_impl2, default: false
|
||||||
|
|
||||||
namespace :web do
|
namespace :web do
|
||||||
setting :crop_images, default: true
|
|
||||||
setting :advanced_layout, default: false
|
setting :advanced_layout, default: false
|
||||||
setting :trends, default: true
|
setting :trends, default: true
|
||||||
setting :use_blurhash, default: true
|
setting :use_blurhash, default: true
|
||||||
|
|
|
@ -20,6 +20,7 @@ class Webhook < ApplicationRecord
|
||||||
account.created
|
account.created
|
||||||
account.updated
|
account.updated
|
||||||
report.created
|
report.created
|
||||||
|
report.updated
|
||||||
status.created
|
status.created
|
||||||
status.updated
|
status.updated
|
||||||
).freeze
|
).freeze
|
||||||
|
@ -59,7 +60,7 @@ class Webhook < ApplicationRecord
|
||||||
case event
|
case event
|
||||||
when 'account.approved', 'account.created', 'account.updated'
|
when 'account.approved', 'account.created', 'account.updated'
|
||||||
:manage_users
|
:manage_users
|
||||||
when 'report.created'
|
when 'report.created', 'report.updated'
|
||||||
:manage_reports
|
:manage_reports
|
||||||
when 'status.created', 'status.updated'
|
when 'status.created', 'status.updated'
|
||||||
:view_devops
|
:view_devops
|
||||||
|
|
|
@ -100,7 +100,7 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def name
|
def name
|
||||||
object.suspended? ? '' : object.display_name
|
object.suspended? ? object.username : (object.display_name.presence || object.username)
|
||||||
end
|
end
|
||||||
|
|
||||||
def summary
|
def summary
|
||||||
|
|
|
@ -51,13 +51,11 @@ class InitialStateSerializer < ActiveModel::Serializer
|
||||||
store[:use_blurhash] = object.current_account.user.setting_use_blurhash
|
store[:use_blurhash] = object.current_account.user.setting_use_blurhash
|
||||||
store[:use_pending_items] = object.current_account.user.setting_use_pending_items
|
store[:use_pending_items] = object.current_account.user.setting_use_pending_items
|
||||||
store[:show_trends] = Setting.trends && object.current_account.user.setting_trends
|
store[:show_trends] = Setting.trends && object.current_account.user.setting_trends
|
||||||
store[:crop_images] = object.current_account.user.setting_crop_images
|
|
||||||
else
|
else
|
||||||
store[:auto_play_gif] = Setting.auto_play_gif
|
store[:auto_play_gif] = Setting.auto_play_gif
|
||||||
store[:display_media] = Setting.display_media
|
store[:display_media] = Setting.display_media
|
||||||
store[:reduce_motion] = Setting.reduce_motion
|
store[:reduce_motion] = Setting.reduce_motion
|
||||||
store[:use_blurhash] = Setting.use_blurhash
|
store[:use_blurhash] = Setting.use_blurhash
|
||||||
store[:crop_images] = Setting.crop_images
|
|
||||||
end
|
end
|
||||||
|
|
||||||
store[:disabled_account_id] = object.disabled_account.id.to_s if object.disabled_account
|
store[:disabled_account_id] = object.disabled_account.id.to_s if object.disabled_account
|
||||||
|
|
|
@ -6,7 +6,7 @@ class REST::PreviewCardSerializer < ActiveModel::Serializer
|
||||||
attributes :url, :title, :description, :language, :type,
|
attributes :url, :title, :description, :language, :type,
|
||||||
:author_name, :author_url, :provider_name,
|
:author_name, :author_url, :provider_name,
|
||||||
:provider_url, :html, :width, :height,
|
:provider_url, :html, :width, :height,
|
||||||
:image, :embed_url, :blurhash
|
:image, :embed_url, :blurhash, :published_at
|
||||||
|
|
||||||
def image
|
def image
|
||||||
object.image? ? full_asset_url(object.image.url(:original)) : nil
|
object.image? ? full_asset_url(object.image.url(:original)) : nil
|
||||||
|
@ -15,4 +15,8 @@ class REST::PreviewCardSerializer < ActiveModel::Serializer
|
||||||
def html
|
def html
|
||||||
Sanitize.fragment(object.html, Sanitize::Config::MASTODON_OEMBED)
|
Sanitize.fragment(object.html, Sanitize::Config::MASTODON_OEMBED)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def published_at
|
||||||
|
object.created_at
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -19,4 +19,3 @@
|
||||||
= render partial: 'announcement', collection: @announcements
|
= render partial: 'announcement', collection: @announcements
|
||||||
|
|
||||||
= paginate @announcements
|
= paginate @announcements
|
||||||
|
|
||||||
|
|
|
@ -91,4 +91,3 @@
|
||||||
= render partial: 'custom_emoji', collection: @custom_emojis, locals: { f: f }
|
= render partial: 'custom_emoji', collection: @custom_emojis, locals: { f: f }
|
||||||
|
|
||||||
= paginate @custom_emojis
|
= paginate @custom_emojis
|
||||||
|
|
||||||
|
|
|
@ -25,4 +25,3 @@
|
||||||
= render partial: 'ip_block', collection: @ip_blocks, locals: { f: f }
|
= render partial: 'ip_block', collection: @ip_blocks, locals: { f: f }
|
||||||
|
|
||||||
= paginate @ip_blocks
|
= paginate @ip_blocks
|
||||||
|
|
||||||
|
|
|
@ -17,4 +17,3 @@
|
||||||
%th
|
%th
|
||||||
%tbody
|
%tbody
|
||||||
= render @relays
|
= render @relays
|
||||||
|
|
||||||
|
|
|
@ -5,4 +5,3 @@
|
||||||
= link_to t('admin.roles.delete'), admin_role_path(@role), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button button--destructive' if can?(:destroy, @role)
|
= link_to t('admin.roles.delete'), admin_role_path(@role), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button button--destructive' if can?(:destroy, @role)
|
||||||
|
|
||||||
= render partial: 'form'
|
= render partial: 'form'
|
||||||
|
|
||||||
|
|
|
@ -28,4 +28,3 @@
|
||||||
|
|
||||||
.actions
|
.actions
|
||||||
= f.button :button, t('generic.save_changes'), type: :submit
|
= f.button :button, t('generic.save_changes'), type: :submit
|
||||||
|
|
||||||
|
|
|
@ -37,11 +37,6 @@
|
||||||
= ff.input :'web.disable_swiping', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_disable_swiping')
|
= ff.input :'web.disable_swiping', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_disable_swiping')
|
||||||
= ff.input :'web.use_system_font', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_system_font_ui')
|
= ff.input :'web.use_system_font', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_system_font_ui')
|
||||||
|
|
||||||
%h4= t 'appearance.toot_layout'
|
|
||||||
|
|
||||||
.fields-group
|
|
||||||
= ff.input :'web.crop_images', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_crop_images')
|
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= ff.input :'web.hide_recent_emojis', wrapper: :with_label, kmyblue: true, label: I18n.t('simple_form.labels.defaults.setting_hide_recent_emojis'), hint: false
|
= ff.input :'web.hide_recent_emojis', wrapper: :with_label, kmyblue: true, label: I18n.t('simple_form.labels.defaults.setting_hide_recent_emojis'), hint: false
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ class Scheduler::VacuumScheduler
|
||||||
preview_cards_vacuum,
|
preview_cards_vacuum,
|
||||||
backups_vacuum,
|
backups_vacuum,
|
||||||
access_tokens_vacuum,
|
access_tokens_vacuum,
|
||||||
|
applications_vacuum,
|
||||||
feeds_vacuum,
|
feeds_vacuum,
|
||||||
imports_vacuum,
|
imports_vacuum,
|
||||||
]
|
]
|
||||||
|
@ -55,6 +56,10 @@ class Scheduler::VacuumScheduler
|
||||||
Vacuum::ImportsVacuum.new
|
Vacuum::ImportsVacuum.new
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def applications_vacuum
|
||||||
|
Vacuum::ApplicationsVacuum.new
|
||||||
|
end
|
||||||
|
|
||||||
def content_retention_policy
|
def content_retention_policy
|
||||||
ContentRetentionPolicy.current
|
ContentRetentionPolicy.current
|
||||||
end
|
end
|
||||||
|
|
|
@ -192,7 +192,9 @@ module Mastodon
|
||||||
# config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
|
# config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
|
||||||
|
|
||||||
config.active_job.queue_adapter = :sidekiq
|
config.active_job.queue_adapter = :sidekiq
|
||||||
|
|
||||||
config.action_mailer.deliver_later_queue_name = 'mailers'
|
config.action_mailer.deliver_later_queue_name = 'mailers'
|
||||||
|
config.action_mailer.preview_path = Rails.root.join('spec', 'mailers', 'previews')
|
||||||
|
|
||||||
# We use our own middleware for this
|
# We use our own middleware for this
|
||||||
config.public_file_server.enabled = false
|
config.public_file_server.enabled = false
|
||||||
|
|
|
@ -65,8 +65,8 @@ ignore_unused:
|
||||||
- 'move_handler.carry_{mutes,blocks}_over_text'
|
- 'move_handler.carry_{mutes,blocks}_over_text'
|
||||||
- 'admin_mailer.*.subject'
|
- 'admin_mailer.*.subject'
|
||||||
- 'notification_mailer.*'
|
- 'notification_mailer.*'
|
||||||
- 'imports.overwrite_preambles.{following,blocking,muting,domain_blocking,bookmarks}_html'
|
- 'imports.overwrite_preambles.{following,blocking,muting,domain_blocking,bookmarks,lists}_html'
|
||||||
- 'imports.preambles.{following,blocking,muting,domain_blocking,bookmarks}_html'
|
- 'imports.preambles.{following,blocking,muting,domain_blocking,bookmarks,lists}_html'
|
||||||
- 'mail_subscriptions.unsubscribe.emails.*'
|
- 'mail_subscriptions.unsubscribe.emails.*'
|
||||||
- 'preferences.other' # some locales are missing other keys, therefore leading i18n-tasks to detect `preferences` as plural and not finding use
|
- 'preferences.other' # some locales are missing other keys, therefore leading i18n-tasks to detect `preferences` as plural and not finding use
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
|
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
|
||||||
|
|
||||||
def host_to_url(str)
|
def host_to_url(str)
|
||||||
"http#{Rails.configuration.x.use_https ? 's' : ''}://#{str}".split('/').first if str.present?
|
"http#{Rails.configuration.x.use_https ? 's' : ''}://#{str.split('/').first}" if str.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
base_host = Rails.configuration.x.web_domain
|
base_host = Rails.configuration.x.web_domain
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# TODO: Remove after 4.2.0
|
||||||
|
Rails.application.configure do
|
||||||
|
config.active_support.key_generator_hash_digest_class = OpenSSL::Digest::SHA1
|
||||||
|
end
|
||||||
|
|
||||||
Rails.application.config.after_initialize do
|
Rails.application.config.after_initialize do
|
||||||
Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies|
|
Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies|
|
||||||
authenticated_encrypted_cookie_salt = Rails.application.config.action_dispatch.authenticated_encrypted_cookie_salt
|
authenticated_encrypted_cookie_salt = Rails.application.config.action_dispatch.authenticated_encrypted_cookie_salt
|
||||||
|
@ -7,8 +12,9 @@ Rails.application.config.after_initialize do
|
||||||
|
|
||||||
secret_key_base = Rails.application.secret_key_base
|
secret_key_base = Rails.application.secret_key_base
|
||||||
|
|
||||||
|
# TODO: Switch to SHA1 after 4.2.0
|
||||||
key_generator = ActiveSupport::KeyGenerator.new(
|
key_generator = ActiveSupport::KeyGenerator.new(
|
||||||
secret_key_base, iterations: 1000, hash_digest_class: OpenSSL::Digest::SHA1
|
secret_key_base, iterations: 1000, hash_digest_class: OpenSSL::Digest::SHA256
|
||||||
)
|
)
|
||||||
key_len = ActiveSupport::MessageEncryptor.key_len
|
key_len = ActiveSupport::MessageEncryptor.key_len
|
||||||
|
|
||||||
|
|
|
@ -922,7 +922,6 @@ an:
|
||||||
guide_link: https://crowdin.com/project/mastodon
|
guide_link: https://crowdin.com/project/mastodon
|
||||||
guide_link_text: Totz pueden contribuyir.
|
guide_link_text: Totz pueden contribuyir.
|
||||||
sensitive_content: Conteniu sensible
|
sensitive_content: Conteniu sensible
|
||||||
toot_layout: Disenyo d'as publicacions
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: Cambiar preferencias de correu electronico
|
notification_preferences: Cambiar preferencias de correu electronico
|
||||||
salutation: "%{name}:"
|
salutation: "%{name}:"
|
||||||
|
|
|
@ -981,7 +981,6 @@ ar:
|
||||||
guide_link: https://crowdin.com/project/mastodon
|
guide_link: https://crowdin.com/project/mastodon
|
||||||
guide_link_text: يمكن للجميع المساهمة.
|
guide_link_text: يمكن للجميع المساهمة.
|
||||||
sensitive_content: المحتوى الحساس
|
sensitive_content: المحتوى الحساس
|
||||||
toot_layout: شكل المنشور
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: تعديل تفضيلات البريد الإلكتروني
|
notification_preferences: تعديل تفضيلات البريد الإلكتروني
|
||||||
salutation: "%{name}،"
|
salutation: "%{name}،"
|
||||||
|
|
|
@ -438,7 +438,6 @@ ast:
|
||||||
guide_link: https://crowdin.com/project/mastodon
|
guide_link: https://crowdin.com/project/mastodon
|
||||||
guide_link_text: tol mundu pue collaborar.
|
guide_link_text: tol mundu pue collaborar.
|
||||||
sensitive_content: Conteníu sensible
|
sensitive_content: Conteníu sensible
|
||||||
toot_layout: Distribución de los artículos
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: Camudar les preferencies de los mensaxes de corréu electrónicu
|
notification_preferences: Camudar les preferencies de los mensaxes de corréu electrónicu
|
||||||
applications:
|
applications:
|
||||||
|
|
|
@ -1008,7 +1008,6 @@ be:
|
||||||
guide_link: https://be.crowdin.com/project/mastodon/be
|
guide_link: https://be.crowdin.com/project/mastodon/be
|
||||||
guide_link_text: Кожны можа зрабіць унёсак.
|
guide_link_text: Кожны можа зрабіць унёсак.
|
||||||
sensitive_content: Далікатны змест
|
sensitive_content: Далікатны змест
|
||||||
toot_layout: Макет допісу
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: Змяніць налады эл. пошты
|
notification_preferences: Змяніць налады эл. пошты
|
||||||
salutation: "%{name},"
|
salutation: "%{name},"
|
||||||
|
|
|
@ -972,7 +972,6 @@ bg:
|
||||||
guide_link: https://ru.crowdin.com/project/mastodon
|
guide_link: https://ru.crowdin.com/project/mastodon
|
||||||
guide_link_text: Всеки може да участва.
|
guide_link_text: Всеки може да участва.
|
||||||
sensitive_content: Деликатно съдържание
|
sensitive_content: Деликатно съдържание
|
||||||
toot_layout: Оформление на публикацията
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: Промяна на предпочитанията за имейл
|
notification_preferences: Промяна на предпочитанията за имейл
|
||||||
salutation: "%{name},"
|
salutation: "%{name},"
|
||||||
|
|
|
@ -972,7 +972,6 @@ ca:
|
||||||
guide_link: https://crowdin.com/project/mastodon
|
guide_link: https://crowdin.com/project/mastodon
|
||||||
guide_link_text: Tothom hi pot contribuir.
|
guide_link_text: Tothom hi pot contribuir.
|
||||||
sensitive_content: Contingut sensible
|
sensitive_content: Contingut sensible
|
||||||
toot_layout: Disseny dels tuts
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: Canvia les preferències de correu
|
notification_preferences: Canvia les preferències de correu
|
||||||
salutation: "%{name},"
|
salutation: "%{name},"
|
||||||
|
|
|
@ -570,7 +570,6 @@ ckb:
|
||||||
body: ماستۆدۆن لەلایەن خۆبەخشەوە وەردەگێڕێت.
|
body: ماستۆدۆن لەلایەن خۆبەخشەوە وەردەگێڕێت.
|
||||||
guide_link_text: هەموو کەسێک دەتوانێت بەشداری بکات.
|
guide_link_text: هەموو کەسێک دەتوانێت بەشداری بکات.
|
||||||
sensitive_content: ناوەڕۆکی هەستیار
|
sensitive_content: ناوەڕۆکی هەستیار
|
||||||
toot_layout: لۆی توت
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: گۆڕینی پەسەندکراوەکانی ئیمەیڵ
|
notification_preferences: گۆڕینی پەسەندکراوەکانی ئیمەیڵ
|
||||||
salutation: "%{name},"
|
salutation: "%{name},"
|
||||||
|
|
|
@ -536,7 +536,6 @@ co:
|
||||||
guide_link: https://fr.crowdin.com/project/mastodon
|
guide_link: https://fr.crowdin.com/project/mastodon
|
||||||
guide_link_text: Tuttu u mondu pò participà.
|
guide_link_text: Tuttu u mondu pò participà.
|
||||||
sensitive_content: Cuntinutu sensibile
|
sensitive_content: Cuntinutu sensibile
|
||||||
toot_layout: Urganizazione
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: Cambià e priferenze e-mail
|
notification_preferences: Cambià e priferenze e-mail
|
||||||
salutation: "%{name},"
|
salutation: "%{name},"
|
||||||
|
|
|
@ -996,7 +996,6 @@ cs:
|
||||||
guide_link: https://cs.crowdin.com/project/mastodon
|
guide_link: https://cs.crowdin.com/project/mastodon
|
||||||
guide_link_text: Zapojit se může každý.
|
guide_link_text: Zapojit se může každý.
|
||||||
sensitive_content: Citlivý obsah
|
sensitive_content: Citlivý obsah
|
||||||
toot_layout: Rozložení příspěvků
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: Změnit předvolby e-mailů
|
notification_preferences: Změnit předvolby e-mailů
|
||||||
salutation: "%{name},"
|
salutation: "%{name},"
|
||||||
|
|
|
@ -1046,7 +1046,6 @@ cy:
|
||||||
guide_link: https://crowdin.com/project/mastodon
|
guide_link: https://crowdin.com/project/mastodon
|
||||||
guide_link_text: Gall pawb gyfrannu.
|
guide_link_text: Gall pawb gyfrannu.
|
||||||
sensitive_content: Cynnwys sensitif
|
sensitive_content: Cynnwys sensitif
|
||||||
toot_layout: Cynllun postiad
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: Newid gosodiadau e-bost
|
notification_preferences: Newid gosodiadau e-bost
|
||||||
salutation: "%{name},"
|
salutation: "%{name},"
|
||||||
|
|
|
@ -972,7 +972,6 @@ da:
|
||||||
guide_link: https://da.crowdin.com/project/mastodon
|
guide_link: https://da.crowdin.com/project/mastodon
|
||||||
guide_link_text: Alle kan bidrage.
|
guide_link_text: Alle kan bidrage.
|
||||||
sensitive_content: Sensitivt indhold
|
sensitive_content: Sensitivt indhold
|
||||||
toot_layout: Indlægslayout
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: Skift e-mailpræferencer
|
notification_preferences: Skift e-mailpræferencer
|
||||||
salutation: "%{name}"
|
salutation: "%{name}"
|
||||||
|
|
|
@ -972,7 +972,6 @@ de:
|
||||||
guide_link: https://de.crowdin.com/project/mastodon/de
|
guide_link: https://de.crowdin.com/project/mastodon/de
|
||||||
guide_link_text: Alle können mitmachen und etwas dazu beitragen.
|
guide_link_text: Alle können mitmachen und etwas dazu beitragen.
|
||||||
sensitive_content: Inhaltswarnung
|
sensitive_content: Inhaltswarnung
|
||||||
toot_layout: Timeline-Layout
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: E-Mail-Einstellungen ändern
|
notification_preferences: E-Mail-Einstellungen ändern
|
||||||
salutation: "%{name},"
|
salutation: "%{name},"
|
||||||
|
|
|
@ -127,7 +127,7 @@ en:
|
||||||
bookmarks: Bookmarks
|
bookmarks: Bookmarks
|
||||||
conversations: Conversations
|
conversations: Conversations
|
||||||
crypto: End-to-end encryption
|
crypto: End-to-end encryption
|
||||||
favourites: Favourites
|
favourites: Favorites
|
||||||
filters: Filters
|
filters: Filters
|
||||||
follow: Follows, Mutes and Blocks
|
follow: Follows, Mutes and Blocks
|
||||||
follows: Follows
|
follows: Follows
|
||||||
|
@ -170,7 +170,7 @@ en:
|
||||||
read:accounts: see accounts information
|
read:accounts: see accounts information
|
||||||
read:blocks: see your blocks
|
read:blocks: see your blocks
|
||||||
read:bookmarks: see your bookmarks
|
read:bookmarks: see your bookmarks
|
||||||
read:favourites: see your favourites
|
read:favourites: see your favorites
|
||||||
read:filters: see your filters
|
read:filters: see your filters
|
||||||
read:follows: see your follows
|
read:follows: see your follows
|
||||||
read:lists: see your lists
|
read:lists: see your lists
|
||||||
|
@ -184,7 +184,7 @@ en:
|
||||||
write:blocks: block accounts and domains
|
write:blocks: block accounts and domains
|
||||||
write:bookmarks: bookmark posts
|
write:bookmarks: bookmark posts
|
||||||
write:conversations: mute and delete conversations
|
write:conversations: mute and delete conversations
|
||||||
write:favourites: favourite posts
|
write:favourites: favorite posts
|
||||||
write:filters: create filters
|
write:filters: create filters
|
||||||
write:follows: follow people
|
write:follows: follow people
|
||||||
write:lists: create lists
|
write:lists: create lists
|
||||||
|
|
|
@ -960,7 +960,6 @@ el:
|
||||||
guide_link: https://crowdin.com/project/mastodon
|
guide_link: https://crowdin.com/project/mastodon
|
||||||
guide_link_text: Μπορεί να συνεισφέρει ο οποιοσδήποτε.
|
guide_link_text: Μπορεί να συνεισφέρει ο οποιοσδήποτε.
|
||||||
sensitive_content: Ευαίσθητο περιεχόμενο
|
sensitive_content: Ευαίσθητο περιεχόμενο
|
||||||
toot_layout: Διαρρύθμιση αναρτήσεων
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: Αλλαγή προτιμήσεων email
|
notification_preferences: Αλλαγή προτιμήσεων email
|
||||||
salutation: "%{name},"
|
salutation: "%{name},"
|
||||||
|
|
|
@ -972,7 +972,6 @@ en-GB:
|
||||||
guide_link: https://crowdin.com/project/mastodon
|
guide_link: https://crowdin.com/project/mastodon
|
||||||
guide_link_text: Everyone can contribute.
|
guide_link_text: Everyone can contribute.
|
||||||
sensitive_content: Sensitive content
|
sensitive_content: Sensitive content
|
||||||
toot_layout: Post layout
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: Change e-mail preferences
|
notification_preferences: Change e-mail preferences
|
||||||
salutation: "%{name},"
|
salutation: "%{name},"
|
||||||
|
|
|
@ -1073,7 +1073,6 @@ en:
|
||||||
guide_link: https://crowdin.com/project/mastodon
|
guide_link: https://crowdin.com/project/mastodon
|
||||||
guide_link_text: Everyone can contribute.
|
guide_link_text: Everyone can contribute.
|
||||||
sensitive_content: Sensitive content
|
sensitive_content: Sensitive content
|
||||||
toot_layout: Post layout
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: Change e-mail preferences
|
notification_preferences: Change e-mail preferences
|
||||||
salutation: "%{name},"
|
salutation: "%{name},"
|
||||||
|
@ -1379,12 +1378,14 @@ en:
|
||||||
bookmarks_html: You are about to <strong>replace your bookmarks</strong> with up to <strong>%{total_items} posts</strong> from <strong>%{filename}</strong>.
|
bookmarks_html: You are about to <strong>replace your bookmarks</strong> with up to <strong>%{total_items} posts</strong> from <strong>%{filename}</strong>.
|
||||||
domain_blocking_html: You are about to <strong>replace your domain block list</strong> with up to <strong>%{total_items} domains</strong> from <strong>%{filename}</strong>.
|
domain_blocking_html: You are about to <strong>replace your domain block list</strong> with up to <strong>%{total_items} domains</strong> from <strong>%{filename}</strong>.
|
||||||
following_html: You are about to <strong>follow</strong> up to <strong>%{total_items} accounts</strong> from <strong>%{filename}</strong> and <strong>stop following anyone else</strong>.
|
following_html: You are about to <strong>follow</strong> up to <strong>%{total_items} accounts</strong> from <strong>%{filename}</strong> and <strong>stop following anyone else</strong>.
|
||||||
|
lists_html: You are about to <strong>replace your lists</strong> with contents of <strong>%{filename}</strong>. Up to <strong>%{total_items} accounts</strong> will be added to new lists.
|
||||||
muting_html: You are about to <strong>replace your list of muted accounts</strong> with up to <strong>%{total_items} accounts</strong> from <strong>%{filename}</strong>.
|
muting_html: You are about to <strong>replace your list of muted accounts</strong> with up to <strong>%{total_items} accounts</strong> from <strong>%{filename}</strong>.
|
||||||
preambles:
|
preambles:
|
||||||
blocking_html: You are about to <strong>block</strong> up to <strong>%{total_items} accounts</strong> from <strong>%{filename}</strong>.
|
blocking_html: You are about to <strong>block</strong> up to <strong>%{total_items} accounts</strong> from <strong>%{filename}</strong>.
|
||||||
bookmarks_html: You are about to add up to <strong>%{total_items} posts</strong> from <strong>%{filename}</strong> to your <strong>bookmarks</strong>.
|
bookmarks_html: You are about to add up to <strong>%{total_items} posts</strong> from <strong>%{filename}</strong> to your <strong>bookmarks</strong>.
|
||||||
domain_blocking_html: You are about to <strong>block</strong> up to <strong>%{total_items} domains</strong> from <strong>%{filename}</strong>.
|
domain_blocking_html: You are about to <strong>block</strong> up to <strong>%{total_items} domains</strong> from <strong>%{filename}</strong>.
|
||||||
following_html: You are about to <strong>follow</strong> up to <strong>%{total_items} accounts</strong> from <strong>%{filename}</strong>.
|
following_html: You are about to <strong>follow</strong> up to <strong>%{total_items} accounts</strong> from <strong>%{filename}</strong>.
|
||||||
|
lists_html: You are about to add up to <strong>%{total_items} accounts</strong> from <strong>%{filename}</strong> to your <strong>lists</strong>. New lists will be created if there is no list to add to.
|
||||||
muting_html: You are about to <strong>mute</strong> up to <strong>%{total_items} accounts</strong> from <strong>%{filename}</strong>.
|
muting_html: You are about to <strong>mute</strong> up to <strong>%{total_items} accounts</strong> from <strong>%{filename}</strong>.
|
||||||
preface: You can import data that you have exported from another server, such as a list of the people you are following or blocking.
|
preface: You can import data that you have exported from another server, such as a list of the people you are following or blocking.
|
||||||
recent_imports: Recent imports
|
recent_imports: Recent imports
|
||||||
|
@ -1401,6 +1402,7 @@ en:
|
||||||
bookmarks: Importing bookmarks
|
bookmarks: Importing bookmarks
|
||||||
domain_blocking: Importing blocked domains
|
domain_blocking: Importing blocked domains
|
||||||
following: Importing followed accounts
|
following: Importing followed accounts
|
||||||
|
lists: Importing lists
|
||||||
muting: Importing muted accounts
|
muting: Importing muted accounts
|
||||||
type: Import type
|
type: Import type
|
||||||
type_groups:
|
type_groups:
|
||||||
|
@ -1411,6 +1413,7 @@ en:
|
||||||
bookmarks: Bookmarks
|
bookmarks: Bookmarks
|
||||||
domain_blocking: Domain blocking list
|
domain_blocking: Domain blocking list
|
||||||
following: Following list
|
following: Following list
|
||||||
|
lists: Lists
|
||||||
muting: Muting list
|
muting: Muting list
|
||||||
upload: Upload
|
upload: Upload
|
||||||
invites:
|
invites:
|
||||||
|
|
|
@ -972,7 +972,6 @@ eo:
|
||||||
guide_link: https://crowdin.com/project/mastodon
|
guide_link: https://crowdin.com/project/mastodon
|
||||||
guide_link_text: Ĉiu povas kontribui.
|
guide_link_text: Ĉiu povas kontribui.
|
||||||
sensitive_content: Tikla enhavo
|
sensitive_content: Tikla enhavo
|
||||||
toot_layout: Mesaĝo aranĝo
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: Ŝanĝi retmesaĝajn preferojn
|
notification_preferences: Ŝanĝi retmesaĝajn preferojn
|
||||||
salutation: "%{name},"
|
salutation: "%{name},"
|
||||||
|
|
|
@ -972,7 +972,6 @@ es-AR:
|
||||||
guide_link: https://es.crowdin.com/project/mastodon
|
guide_link: https://es.crowdin.com/project/mastodon
|
||||||
guide_link_text: Todos pueden contribuir.
|
guide_link_text: Todos pueden contribuir.
|
||||||
sensitive_content: Contenido sensible
|
sensitive_content: Contenido sensible
|
||||||
toot_layout: Diseño del mensaje
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: Cambiar configuración de correo electrónico
|
notification_preferences: Cambiar configuración de correo electrónico
|
||||||
salutation: "%{name}:"
|
salutation: "%{name}:"
|
||||||
|
|
|
@ -972,7 +972,6 @@ es-MX:
|
||||||
guide_link: https://es.crowdin.com/project/mastodon
|
guide_link: https://es.crowdin.com/project/mastodon
|
||||||
guide_link_text: Todos pueden contribuir.
|
guide_link_text: Todos pueden contribuir.
|
||||||
sensitive_content: Contenido sensible
|
sensitive_content: Contenido sensible
|
||||||
toot_layout: Diseño de los toots
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: Cambiar preferencias de correo electrónico
|
notification_preferences: Cambiar preferencias de correo electrónico
|
||||||
salutation: "%{name}:"
|
salutation: "%{name}:"
|
||||||
|
|
|
@ -972,7 +972,6 @@ es:
|
||||||
guide_link: https://es.crowdin.com/project/mastodon
|
guide_link: https://es.crowdin.com/project/mastodon
|
||||||
guide_link_text: Todos pueden contribuir.
|
guide_link_text: Todos pueden contribuir.
|
||||||
sensitive_content: Contenido sensible
|
sensitive_content: Contenido sensible
|
||||||
toot_layout: Diseño de las publicaciones
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: Cambiar preferencias de correo electrónico
|
notification_preferences: Cambiar preferencias de correo electrónico
|
||||||
salutation: "%{name}:"
|
salutation: "%{name}:"
|
||||||
|
|
|
@ -972,7 +972,6 @@ et:
|
||||||
guide_link: https://crowdin.com/project/mastodon/et
|
guide_link: https://crowdin.com/project/mastodon/et
|
||||||
guide_link_text: Panustada võib igaüks!
|
guide_link_text: Panustada võib igaüks!
|
||||||
sensitive_content: Tundlik sisu
|
sensitive_content: Tundlik sisu
|
||||||
toot_layout: Postituse väljanägemine
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: Muuda e-kirjade eelistusi
|
notification_preferences: Muuda e-kirjade eelistusi
|
||||||
salutation: "%{name}!"
|
salutation: "%{name}!"
|
||||||
|
|
|
@ -969,7 +969,6 @@ eu:
|
||||||
guide_link: https://crowdin.com/project/mastodon
|
guide_link: https://crowdin.com/project/mastodon
|
||||||
guide_link_text: Edonork lagundu dezake.
|
guide_link_text: Edonork lagundu dezake.
|
||||||
sensitive_content: Eduki hunkigarria
|
sensitive_content: Eduki hunkigarria
|
||||||
toot_layout: Bidalketen diseinua
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: Aldatu e-mail hobespenak
|
notification_preferences: Aldatu e-mail hobespenak
|
||||||
salutation: "%{name},"
|
salutation: "%{name},"
|
||||||
|
|
|
@ -820,7 +820,6 @@ fa:
|
||||||
guide_link: https://crowdin.com/project/mastodon
|
guide_link: https://crowdin.com/project/mastodon
|
||||||
guide_link_text: همه میتوانند کمک کنند.
|
guide_link_text: همه میتوانند کمک کنند.
|
||||||
sensitive_content: محتوای حساس
|
sensitive_content: محتوای حساس
|
||||||
toot_layout: آرایش فرسته
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: تغییر ترجیحات ایمیل
|
notification_preferences: تغییر ترجیحات ایمیل
|
||||||
salutation: "%{name}،"
|
salutation: "%{name}،"
|
||||||
|
|
|
@ -972,7 +972,6 @@ fi:
|
||||||
guide_link: https://crowdin.com/project/mastodon
|
guide_link: https://crowdin.com/project/mastodon
|
||||||
guide_link_text: Kaikki voivat osallistua.
|
guide_link_text: Kaikki voivat osallistua.
|
||||||
sensitive_content: Arkaluonteinen sisältö
|
sensitive_content: Arkaluonteinen sisältö
|
||||||
toot_layout: Viestin asettelu
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: Muuta sähköpostiasetuksia
|
notification_preferences: Muuta sähköpostiasetuksia
|
||||||
salutation: "%{name},"
|
salutation: "%{name},"
|
||||||
|
|
|
@ -972,7 +972,6 @@ fo:
|
||||||
guide_link: https://crowdin.com/project/mastodon
|
guide_link: https://crowdin.com/project/mastodon
|
||||||
guide_link_text: Øll kunnu geva íkast.
|
guide_link_text: Øll kunnu geva íkast.
|
||||||
sensitive_content: Viðkvæmt innihald
|
sensitive_content: Viðkvæmt innihald
|
||||||
toot_layout: Uppseting av postum
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: Broyt teldupostastillingar
|
notification_preferences: Broyt teldupostastillingar
|
||||||
salutation: "%{name}"
|
salutation: "%{name}"
|
||||||
|
|
|
@ -972,7 +972,6 @@ fr-QC:
|
||||||
guide_link: https://fr.crowdin.com/project/mastodon
|
guide_link: https://fr.crowdin.com/project/mastodon
|
||||||
guide_link_text: Tout le monde peut y contribuer.
|
guide_link_text: Tout le monde peut y contribuer.
|
||||||
sensitive_content: Contenu sensible
|
sensitive_content: Contenu sensible
|
||||||
toot_layout: Agencement des messages
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: Modifier les préférences de courriel
|
notification_preferences: Modifier les préférences de courriel
|
||||||
salutation: "%{name},"
|
salutation: "%{name},"
|
||||||
|
|
|
@ -972,7 +972,6 @@ fr:
|
||||||
guide_link: https://fr.crowdin.com/project/mastodon
|
guide_link: https://fr.crowdin.com/project/mastodon
|
||||||
guide_link_text: Tout le monde peut y contribuer.
|
guide_link_text: Tout le monde peut y contribuer.
|
||||||
sensitive_content: Contenu sensible
|
sensitive_content: Contenu sensible
|
||||||
toot_layout: Agencement des messages
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: Modifier les préférences de courriel
|
notification_preferences: Modifier les préférences de courriel
|
||||||
salutation: "%{name},"
|
salutation: "%{name},"
|
||||||
|
|
|
@ -972,7 +972,6 @@ fy:
|
||||||
guide_link: https://crowdin.com/project/mastodon/fy
|
guide_link: https://crowdin.com/project/mastodon/fy
|
||||||
guide_link_text: Elkenien kin bydrage.
|
guide_link_text: Elkenien kin bydrage.
|
||||||
sensitive_content: Gefoelige ynhâld
|
sensitive_content: Gefoelige ynhâld
|
||||||
toot_layout: Lay-out fan berjochten
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: E-mailynstellingen wizigje
|
notification_preferences: E-mailynstellingen wizigje
|
||||||
salutation: "%{name},"
|
salutation: "%{name},"
|
||||||
|
|
|
@ -1008,7 +1008,6 @@ gd:
|
||||||
guide_link: https://crowdin.com/project/mastodon
|
guide_link: https://crowdin.com/project/mastodon
|
||||||
guide_link_text: '’S urrainn do neach sam bith cuideachadh.'
|
guide_link_text: '’S urrainn do neach sam bith cuideachadh.'
|
||||||
sensitive_content: Susbaint fhrionasach
|
sensitive_content: Susbaint fhrionasach
|
||||||
toot_layout: Co-dhealbhachd nam postaichean
|
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: Atharraich roghainnean a’ phuist-d
|
notification_preferences: Atharraich roghainnean a’ phuist-d
|
||||||
salutation: "%{name},"
|
salutation: "%{name},"
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue