diff options
Diffstat (limited to 'app')
37 files changed, 475 insertions, 443 deletions
diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb index e9a512e70..7428c3f22 100644 --- a/app/controllers/admin/accounts_controller.rb +++ b/app/controllers/admin/accounts_controller.rb @@ -89,7 +89,8 @@ module Admin :username, :display_name, :email, - :ip + :ip, + :staff ) end end diff --git a/app/controllers/admin/custom_emojis_controller.rb b/app/controllers/admin/custom_emojis_controller.rb index 3fa2a0b72..ccab03de4 100644 --- a/app/controllers/admin/custom_emojis_controller.rb +++ b/app/controllers/admin/custom_emojis_controller.rb @@ -92,7 +92,9 @@ module Admin def filter_params params.permit( :local, - :remote + :remote, + :by_domain, + :shortcode ) end end diff --git a/app/helpers/admin/filter_helper.rb b/app/helpers/admin/filter_helper.rb index 9443934b3..359c43d0e 100644 --- a/app/helpers/admin/filter_helper.rb +++ b/app/helpers/admin/filter_helper.rb @@ -1,11 +1,12 @@ # frozen_string_literal: true module Admin::FilterHelper - ACCOUNT_FILTERS = %i(local remote by_domain silenced suspended recent username display_name email ip).freeze - REPORT_FILTERS = %i(resolved account_id target_account_id).freeze - INVITE_FILTER = %i(available expired).freeze + ACCOUNT_FILTERS = %i(local remote by_domain silenced suspended recent username display_name email ip staff).freeze + REPORT_FILTERS = %i(resolved account_id target_account_id).freeze + INVITE_FILTER = %i(available expired).freeze + CUSTOM_EMOJI_FILTERS = %i(local remote by_domain shortcode).freeze - FILTERS = ACCOUNT_FILTERS + REPORT_FILTERS + INVITE_FILTER + FILTERS = ACCOUNT_FILTERS + REPORT_FILTERS + INVITE_FILTER + CUSTOM_EMOJI_FILTERS def filter_link_to(text, link_to_params, link_class_params = link_to_params) new_url = filtered_url_for(link_to_params) diff --git a/app/javascript/mastodon/components/avatar.js b/app/javascript/mastodon/components/avatar.js index f7c484ee3..570505833 100644 --- a/app/javascript/mastodon/components/avatar.js +++ b/app/javascript/mastodon/components/avatar.js @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; +import { autoPlayGif } from '../initial_state'; export default class Avatar extends React.PureComponent { @@ -8,12 +9,12 @@ export default class Avatar extends React.PureComponent { account: ImmutablePropTypes.map.isRequired, size: PropTypes.number.isRequired, style: PropTypes.object, - animate: PropTypes.bool, inline: PropTypes.bool, + animate: PropTypes.bool, }; static defaultProps = { - animate: false, + animate: autoPlayGif, size: 20, inline: false, }; diff --git a/app/javascript/mastodon/components/avatar_overlay.js b/app/javascript/mastodon/components/avatar_overlay.js index f5d67b34e..3ec1d7730 100644 --- a/app/javascript/mastodon/components/avatar_overlay.js +++ b/app/javascript/mastodon/components/avatar_overlay.js @@ -1,22 +1,29 @@ import React from 'react'; +import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; +import { autoPlayGif } from '../initial_state'; export default class AvatarOverlay extends React.PureComponent { static propTypes = { account: ImmutablePropTypes.map.isRequired, friend: ImmutablePropTypes.map.isRequired, + animate: PropTypes.bool, + }; + + static defaultProps = { + animate: autoPlayGif, }; render() { - const { account, friend } = this.props; + const { account, friend, animate } = this.props; const baseStyle = { - backgroundImage: `url(${account.get('avatar_static')})`, + backgroundImage: `url(${account.get(animate ? 'avatar' : 'avatar_static')})`, }; const overlayStyle = { - backgroundImage: `url(${friend.get('avatar_static')})`, + backgroundImage: `url(${friend.get(animate ? 'avatar' : 'avatar_static')})`, }; return ( diff --git a/app/javascript/mastodon/features/compose/components/compose_form.js b/app/javascript/mastodon/features/compose/components/compose_form.js index 7890755f3..a876c5197 100644 --- a/app/javascript/mastodon/features/compose/components/compose_form.js +++ b/app/javascript/mastodon/features/compose/components/compose_form.js @@ -156,6 +156,8 @@ export default class ComposeForm extends ImmutablePureComponent { return ( <div className='compose-form'> + <WarningContainer /> + <Collapsable isVisible={this.props.spoiler} fullHeight={50}> <div className='spoiler-input'> <label> @@ -165,8 +167,6 @@ export default class ComposeForm extends ImmutablePureComponent { </div> </Collapsable> - <WarningContainer /> - <ReplyIndicatorContainer /> <div className='compose-form__autosuggest-wrapper'> @@ -199,11 +199,11 @@ export default class ComposeForm extends ImmutablePureComponent { <SensitiveButtonContainer /> <SpoilerButtonContainer /> </div> + <div className='character-counter__wrapper'><CharacterCounter max={500} text={text} /></div> + </div> - <div className='compose-form__publish'> - <div className='character-counter__wrapper'><CharacterCounter max={500} text={text} /></div> - <div className='compose-form__publish-button-wrapper'><Button text={publishText} onClick={this.handleSubmit} disabled={disabled || this.props.is_uploading || length(text) > 500 || (text.length !== 0 && text.trim().length === 0)} block /></div> - </div> + <div className='compose-form__publish'> + <div className='compose-form__publish-button-wrapper'><Button text={publishText} onClick={this.handleSubmit} disabled={disabled || this.props.is_uploading || length(text) > 500 || (text.length !== 0 && text.trim().length === 0)} block /></div> </div> </div> ); diff --git a/app/javascript/mastodon/features/compose/components/reply_indicator.js b/app/javascript/mastodon/features/compose/components/reply_indicator.js index 7672440b4..d8cda96f3 100644 --- a/app/javascript/mastodon/features/compose/components/reply_indicator.js +++ b/app/javascript/mastodon/features/compose/components/reply_indicator.js @@ -6,6 +6,7 @@ import IconButton from '../../../components/icon_button'; import DisplayName from '../../../components/display_name'; import { defineMessages, injectIntl } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; +import { isRtl } from '../../../rtl'; const messages = defineMessages({ cancel: { id: 'reply_indicator.cancel', defaultMessage: 'Cancel' }, @@ -42,7 +43,10 @@ export default class ReplyIndicator extends ImmutablePureComponent { return null; } - const content = { __html: status.get('contentHtml') }; + const content = { __html: status.get('contentHtml') }; + const style = { + direction: isRtl(status.get('search_index')) ? 'rtl' : 'ltr', + }; return ( <div className='reply-indicator'> @@ -55,7 +59,7 @@ export default class ReplyIndicator extends ImmutablePureComponent { </a> </div> - <div className='reply-indicator__content' dangerouslySetInnerHTML={content} /> + <div className='reply-indicator__content' style={style} dangerouslySetInnerHTML={content} /> </div> ); } diff --git a/app/javascript/mastodon/features/compose/components/upload.js b/app/javascript/mastodon/features/compose/components/upload.js index 6ab76492a..3a3d17710 100644 --- a/app/javascript/mastodon/features/compose/components/upload.js +++ b/app/javascript/mastodon/features/compose/components/upload.js @@ -62,7 +62,7 @@ export default class Upload extends ImmutablePureComponent { render () { const { intl, media } = this.props; const active = this.state.hovered || this.state.focused; - const description = this.state.dirtyDescription || media.get('description') || ''; + const description = this.state.dirtyDescription || (this.state.dirtyDescription !== '' && media.get('description')) || ''; return ( <div className='compose-form__upload' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}> diff --git a/app/javascript/mastodon/features/list_timeline/index.js b/app/javascript/mastodon/features/list_timeline/index.js index 1dcd4de14..ae136e48f 100644 --- a/app/javascript/mastodon/features/list_timeline/index.js +++ b/app/javascript/mastodon/features/list_timeline/index.js @@ -161,7 +161,7 @@ export default class ListTimeline extends React.PureComponent { scrollKey={`list_timeline-${columnId}`} timelineId={`list:${id}`} loadMore={this.handleLoadMore} - emptyMessage={<FormattedMessage id='empty_column.list' defaultMessage='There is nothing in this list yet.' />} + emptyMessage={<FormattedMessage id='empty_column.list' defaultMessage='There is nothing in this list yet. When members of this list post new statuses, they will appear here.' />} /> </Column> ); diff --git a/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js b/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js index f15fbb2f4..f14be2aaf 100644 --- a/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js +++ b/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js @@ -8,6 +8,7 @@ import { } from '../../../actions/timelines'; import Column from '../../../components/column'; import ColumnHeader from '../../../components/column_header'; +import { connectHashtagStream } from '../../../actions/streaming'; @connect() export default class HashtagTimeline extends React.PureComponent { @@ -29,16 +30,13 @@ export default class HashtagTimeline extends React.PureComponent { const { dispatch, hashtag } = this.props; dispatch(refreshHashtagTimeline(hashtag)); - - this.polling = setInterval(() => { - dispatch(refreshHashtagTimeline(hashtag)); - }, 10000); + this.disconnect = dispatch(connectHashtagStream(hashtag)); } componentWillUnmount () { - if (typeof this.polling !== 'undefined') { - clearInterval(this.polling); - this.polling = null; + if (this.disconnect) { + this.disconnect(); + this.disconnect = null; } } diff --git a/app/javascript/mastodon/features/standalone/public_timeline/index.js b/app/javascript/mastodon/features/standalone/public_timeline/index.js index de4b5320a..5805d1a10 100644 --- a/app/javascript/mastodon/features/standalone/public_timeline/index.js +++ b/app/javascript/mastodon/features/standalone/public_timeline/index.js @@ -9,6 +9,7 @@ import { import Column from '../../../components/column'; import ColumnHeader from '../../../components/column_header'; import { defineMessages, injectIntl } from 'react-intl'; +import { connectPublicStream } from '../../../actions/streaming'; const messages = defineMessages({ title: { id: 'standalone.public_title', defaultMessage: 'A look inside...' }, @@ -35,16 +36,13 @@ export default class PublicTimeline extends React.PureComponent { const { dispatch } = this.props; dispatch(refreshPublicTimeline()); - - this.polling = setInterval(() => { - dispatch(refreshPublicTimeline()); - }, 3000); + this.disconnect = dispatch(connectPublicStream()); } componentWillUnmount () { - if (typeof this.polling !== 'undefined') { - clearInterval(this.polling); - this.polling = null; + if (this.disconnect) { + this.disconnect(); + this.disconnect = null; } } diff --git a/app/javascript/mastodon/features/ui/components/columns_area.js b/app/javascript/mastodon/features/ui/components/columns_area.js index 93ed9e605..c5b3c20d4 100644 --- a/app/javascript/mastodon/features/ui/components/columns_area.js +++ b/app/javascript/mastodon/features/ui/components/columns_area.js @@ -27,6 +27,8 @@ const componentMap = { 'LIST': ListTimeline, }; +const isRtlLayout = document.getElementsByTagName('body')[0].classList.contains('rtl'); + @component => injectIntl(component, { withRef: true }) export default class ColumnsArea extends ImmutablePureComponent { @@ -79,7 +81,8 @@ export default class ColumnsArea extends ImmutablePureComponent { handleChildrenContentChange() { if (!this.props.singleColumn) { - this._interruptScrollAnimation = scrollRight(this.node, this.node.scrollWidth - window.innerWidth); + const modifier = isRtlLayout ? -1 : 1; + this._interruptScrollAnimation = scrollRight(this.node, (this.node.scrollWidth - window.innerWidth) * modifier); } } diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json index ec66a0027..d699a69df 100644 --- a/app/javascript/mastodon/locales/ar.json +++ b/app/javascript/mastodon/locales/ar.json @@ -36,9 +36,9 @@ "column.favourites": "المفضلة", "column.follow_requests": "طلبات المتابعة", "column.home": "الرئيسية", - "column.lists": "Lists", + "column.lists": "القوائم", "column.mutes": "الحسابات المكتومة", - "column.notifications": "الإشعارات", + "column.notifications": "الإخطارات", "column.pins": "التبويقات المثبتة", "column.public": "الخيط العام الموحد", "column_back_button.label": "العودة", @@ -64,7 +64,7 @@ "confirmations.delete.confirm": "حذف", "confirmations.delete.message": "هل أنت متأكد أنك تريد حذف هذا المنشور ؟", "confirmations.delete_list.confirm": "Delete", - "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", + "confirmations.delete_list.message": "هل تود حقا حذف هذه القائمة ؟", "confirmations.domain_block.confirm": "إخفاء إسم النطاق كاملا", "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", "confirmations.mute.confirm": "أكتم", @@ -109,32 +109,32 @@ "home.settings": "إعدادات العمود", "keyboard_shortcuts.back": "للعودة", "keyboard_shortcuts.boost": "للترقية", - "keyboard_shortcuts.column": "to focus a status in one of the columns", - "keyboard_shortcuts.compose": "to focus the compose textarea", + "keyboard_shortcuts.column": "للتركيز على منشور على أحد الأعمدة", + "keyboard_shortcuts.compose": "للتركيز على نافذة تحرير النصوص", "keyboard_shortcuts.description": "Description", "keyboard_shortcuts.down": "للإنتقال إلى أسفل القائمة", "keyboard_shortcuts.enter": "to open status", - "keyboard_shortcuts.favourite": "to favourite", + "keyboard_shortcuts.favourite": "للإضافة إلى المفضلة", "keyboard_shortcuts.heading": "Keyboard Shortcuts", - "keyboard_shortcuts.hotkey": "Hotkey", - "keyboard_shortcuts.legend": "to display this legend", + "keyboard_shortcuts.hotkey": "مفتاح الإختصار", + "keyboard_shortcuts.legend": "لعرض هذا المفتاح", "keyboard_shortcuts.mention": "لذِكر الناشر", "keyboard_shortcuts.reply": "للردّ", - "keyboard_shortcuts.search": "to focus search", + "keyboard_shortcuts.search": "للتركيز على البحث", "keyboard_shortcuts.toot": "لتحرير تبويق جديد", "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "للإنتقال إلى أعلى القائمة", "lightbox.close": "إغلاق", "lightbox.next": "التالي", "lightbox.previous": "العودة", - "lists.account.add": "Add to list", - "lists.account.remove": "Remove from list", + "lists.account.add": "أضف إلى القائمة", + "lists.account.remove": "إحذف من القائمة", "lists.delete": "Delete list", - "lists.edit": "Edit list", - "lists.new.create": "Add list", - "lists.new.title_placeholder": "New list title", - "lists.search": "Search among people you follow", - "lists.subheading": "Your lists", + "lists.edit": "تعديل القائمة", + "lists.new.create": "إنشاء قائمة", + "lists.new.title_placeholder": "عنوان القائمة الجديدة", + "lists.search": "إبحث في قائمة الحسابات التي تُتابِعها", + "lists.subheading": "قوائمك", "loading_indicator.label": "تحميل ...", "media_gallery.toggle_visible": "عرض / إخفاء", "missing_indicator.label": "تعذر العثور عليه", @@ -146,7 +146,7 @@ "navigation_bar.follow_requests": "طلبات المتابعة", "navigation_bar.info": "معلومات إضافية", "navigation_bar.keyboard_shortcuts": "إختصارات لوحة المفاتيح", - "navigation_bar.lists": "Lists", + "navigation_bar.lists": "القوائم", "navigation_bar.logout": "خروج", "navigation_bar.mutes": "الحسابات المكتومة", "navigation_bar.pins": "التبويقات المثبتة", @@ -209,7 +209,7 @@ "search_popout.search_format": "نمط البحث المتقدم", "search_popout.tips.hashtag": "وسم", "search_popout.tips.status": "حالة", - "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags", + "search_popout.tips.text": "جملة قصيرة تُمكّنُك من عرض أسماء و حسابات و كلمات رمزية", "search_popout.tips.user": "مستخدِم", "search_results.total": "{count, number} {count, plural, one {result} و {results}}", "standalone.public_title": "نظرة على ...", diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index f705937fd..62d85a5e1 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -36,7 +36,7 @@ "column.favourites": "Favorits", "column.follow_requests": "Peticions per seguir-te", "column.home": "Inici", - "column.lists": "Lists", + "column.lists": "Llistes", "column.mutes": "Usuaris silenciats", "column.notifications": "Notificacions", "column.pins": "Toot fixat", @@ -64,7 +64,7 @@ "confirmations.delete.confirm": "Esborrar", "confirmations.delete.message": "Estàs segur que vols esborrar aquest estat?", "confirmations.delete_list.confirm": "Delete", - "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", + "confirmations.delete_list.message": "Estàs segur que vols esborrar permanenment aquesta llista?", "confirmations.domain_block.confirm": "Amagar tot el domini", "confirmations.domain_block.message": "Estàs realment, realment segur que vols bloquejar totalment {domain}? En la majoria dels casos bloquejar o silenciar és suficient i preferible.", "confirmations.mute.confirm": "Silenciar", @@ -127,14 +127,14 @@ "lightbox.close": "Tancar", "lightbox.next": "Següent", "lightbox.previous": "Anterior", - "lists.account.add": "Add to list", - "lists.account.remove": "Remove from list", + "lists.account.add": "Afegir a la llista", + "lists.account.remove": "Treure de la llista", "lists.delete": "Delete list", - "lists.edit": "Edit list", - "lists.new.create": "Add list", - "lists.new.title_placeholder": "New list title", - "lists.search": "Search among people you follow", - "lists.subheading": "Your lists", + "lists.edit": "Editar llista", + "lists.new.create": "Afegir llista", + "lists.new.title_placeholder": "Nou títol de llista", + "lists.search": "Cercar entre les persones que segueixes", + "lists.subheading": "Les teves llistes", "loading_indicator.label": "Carregant...", "media_gallery.toggle_visible": "Alternar visibilitat", "missing_indicator.label": "No trobat", @@ -146,7 +146,7 @@ "navigation_bar.follow_requests": "Sol·licituds de seguiment", "navigation_bar.info": "Informació addicional", "navigation_bar.keyboard_shortcuts": "Dreceres de teclat", - "navigation_bar.lists": "Lists", + "navigation_bar.lists": "Llistes", "navigation_bar.logout": "Tancar sessió", "navigation_bar.mutes": "Usuaris silenciats", "navigation_bar.pins": "Toots fixats", diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 0f766af6a..3fc4a8c96 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -93,7 +93,7 @@ "empty_column.hashtag": "There is nothing in this hashtag yet.", "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.", "empty_column.home.public_timeline": "the public timeline", - "empty_column.list": "There is nothing in this list yet.", + "empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.", "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.", "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up", "follow_request.authorize": "Authorize", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index a7a8876d0..01cbd2657 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -91,7 +91,7 @@ "empty_column.hashtag": "Il n’y a encore aucun contenu associé à ce hashtag", "empty_column.home": "Vous ne suivez encore personne. Visitez {public} ou bien utilisez la recherche pour vous connecter à d’autres utilisateur⋅ice⋅s.", "empty_column.home.public_timeline": "le fil public", - "empty_column.list": "Il n'y a rien dans cette liste pour l'instant.", + "empty_column.list": "Il n'y a rien dans cette liste pour l'instant. Dès que des personnes de cette listes publierons de nouveaux statuts ils apparaîtront ici.", "empty_column.notifications": "Vous n’avez pas encore de notification. Interagissez avec d’autres utilisateur⋅ice⋅s pour débuter la conversation.", "empty_column.public": "Il n’y a rien ici ! Écrivez quelque chose publiquement, ou bien suivez manuellement des utilisateur⋅ice⋅s d’autres instances pour remplir le fil public.", "follow_request.authorize": "Accepter", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index bb0b1a9fd..6398daa11 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -36,7 +36,7 @@ "column.favourites": "Favoritas", "column.follow_requests": "Peticións de seguimento", "column.home": "Inicio", - "column.lists": "Lists", + "column.lists": "Listas", "column.mutes": "Usuarias acaladas", "column.notifications": "Notificacións", "column.pins": "Mensaxes fixadas", @@ -64,7 +64,7 @@ "confirmations.delete.confirm": "Borrar", "confirmations.delete.message": "Está segura de que quere eliminar este estado?", "confirmations.delete_list.confirm": "Delete", - "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", + "confirmations.delete_list.message": "Estás seguro de que queres eliminar permanentemente esta lista?", "confirmations.domain_block.confirm": "Agochar un dominio completo", "confirmations.domain_block.message": "Realmente está segura de que quere bloquear por completo o dominio {domain}? Normalmente é suficiente, e preferible, bloquear de xeito selectivo varios elementos.", "confirmations.mute.confirm": "Acalar", @@ -127,14 +127,14 @@ "lightbox.close": "Fechar", "lightbox.next": "Seguinte", "lightbox.previous": "Anterior", - "lists.account.add": "Add to list", - "lists.account.remove": "Remove from list", + "lists.account.add": "Engadir á lista", + "lists.account.remove": "Eliminar da lista", "lists.delete": "Delete list", - "lists.edit": "Edit list", - "lists.new.create": "Add list", - "lists.new.title_placeholder": "New list title", - "lists.search": "Search among people you follow", - "lists.subheading": "Your lists", + "lists.edit": "Editar lista", + "lists.new.create": "Engadir lista", + "lists.new.title_placeholder": "Novo título da lista", + "lists.search": "Procurar entre a xente que segues", + "lists.subheading": "As túas listas", "loading_indicator.label": "Cargando...", "media_gallery.toggle_visible": "Dar visibilidade", "missing_indicator.label": "Non atopado", @@ -146,7 +146,7 @@ "navigation_bar.follow_requests": "Peticións de seguimento", "navigation_bar.info": "Sobre esta instancia", "navigation_bar.keyboard_shortcuts": "Atallos do teclado", - "navigation_bar.lists": "Lists", + "navigation_bar.lists": "Listas", "navigation_bar.logout": "Sair", "navigation_bar.mutes": "Usuarias acaladas", "navigation_bar.pins": "Mensaxes fixadas", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 40ad66a7f..68abd906f 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -95,7 +95,7 @@ "empty_column.home.public_timeline": "連合タイムライン", "empty_column.list": "このリストにはまだなにもありません。", "empty_column.notifications": "まだ通知がありません。他の人とふれ合って会話を始めましょう。", - "empty_column.public": "ここにはまだ何もありません!公開で何かを投稿したり、他のインスタンスのユーザーをフォローしたりしていっぱいにしましょう!", + "empty_column.public": "ここにはまだ何もありません! 公開で何かを投稿したり、他のインスタンスのユーザーをフォローしたりしていっぱいにしましょう", "follow_request.authorize": "許可", "follow_request.reject": "拒否", "getting_started.appsshort": "アプリ", @@ -162,12 +162,12 @@ "notifications.clear": "通知を消去", "notifications.clear_confirmation": "本当に通知を消去しますか?", "notifications.column_settings.alert": "デスクトップ通知", - "notifications.column_settings.favourite": "お気に入り", - "notifications.column_settings.follow": "新しいフォロワー", - "notifications.column_settings.mention": "返信", + "notifications.column_settings.favourite": "お気に入り:", + "notifications.column_settings.follow": "新しいフォロワー:", + "notifications.column_settings.mention": "返信:", "notifications.column_settings.push": "プッシュ通知", "notifications.column_settings.push_meta": "このデバイス", - "notifications.column_settings.reblog": "ブースト", + "notifications.column_settings.reblog": "ブースト:", "notifications.column_settings.show": "カラムに表示", "notifications.column_settings.sound": "通知音を再生", "onboarding.done": "完了", @@ -176,7 +176,7 @@ "onboarding.page_four.home": "「ホーム」タイムラインではあなたがフォローしている人の投稿を表示します。", "onboarding.page_four.notifications": "「通知」ではあなたへの他の人からの関わりを表示します。", "onboarding.page_one.federation": "Mastodonは誰でも参加できるSNSです。", - "onboarding.page_one.handle": "あなたは今数あるMastodonインスタンスの1つである{domain}にいます。あなたのフルハンドルは{handle}です。", + "onboarding.page_one.handle": "今あなたは数あるMastodonインスタンスの1つである{domain}にいます。あなたのフルハンドルは{handle}です", "onboarding.page_one.welcome": "Mastodonへようこそ!", "onboarding.page_six.admin": "あなたのインスタンスの管理者は{admin}です。", "onboarding.page_six.almost_done": "以上です。", @@ -184,7 +184,7 @@ "onboarding.page_six.apps_available": "iOS、Androidあるいは他のプラットフォームで使える{apps}があります。", "onboarding.page_six.github": "MastodonはOSSです。バグ報告や機能要望あるいは貢献を{github}から行なえます。", "onboarding.page_six.guidelines": "コミュニティガイドライン", - "onboarding.page_six.read_guidelines": "{guidelines}を読むことを忘れないようにしてください。", + "onboarding.page_six.read_guidelines": "{guidelines}を読むことを忘れないようにしてください!", "onboarding.page_six.various_app": "様々なモバイルアプリ", "onboarding.page_three.profile": "「プロフィールを編集」から、あなたの自己紹介や表示名を変更できます。またそこでは他の設定ができます。", "onboarding.page_three.search": "検索バーで、{illustration}や{introductions}のように特定のハッシュタグの投稿を見たり、ユーザーを探したりできます。", @@ -215,7 +215,7 @@ "search_popout.tips.text": "表示名やユーザー名、ハッシュタグに一致する単純なテキスト", "search_popout.tips.user": "ユーザー", "search_results.total": "{count, number}件の結果", - "standalone.public_title": "今こんな話をしています", + "standalone.public_title": "今こんな話をしています...", "status.cannot_reblog": "この投稿はブーストできません", "status.delete": "削除", "status.embed": "埋め込み", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index c290ed767..9044c2011 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -36,7 +36,7 @@ "column.favourites": "Favorieten", "column.follow_requests": "Volgverzoeken", "column.home": "Start", - "column.lists": "Lists", + "column.lists": "Lijsten", "column.mutes": "Genegeerde gebruikers", "column.notifications": "Meldingen", "column.pins": "Vastgezette toots", @@ -64,7 +64,7 @@ "confirmations.delete.confirm": "Verwijderen", "confirmations.delete.message": "Weet je het zeker dat je deze toot wilt verwijderen?", "confirmations.delete_list.confirm": "Delete", - "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", + "confirmations.delete_list.message": "Weet je zeker dat je deze lijst permanent wilt verwijderen?", "confirmations.domain_block.confirm": "Negeer alles van deze server", "confirmations.domain_block.message": "Weet je het echt, echt zeker dat je alles van {domain} wil negeren? In de meeste gevallen is het blokkeren of negeren van een paar specifieke personen voldoende en gewenst.", "confirmations.mute.confirm": "Negeren", @@ -127,14 +127,14 @@ "lightbox.close": "Sluiten", "lightbox.next": "Volgende", "lightbox.previous": "Vorige", - "lists.account.add": "Add to list", - "lists.account.remove": "Remove from list", + "lists.account.add": "Aan lijst toevoegen", + "lists.account.remove": "Uit lijst verwijderen", "lists.delete": "Delete list", - "lists.edit": "Edit list", - "lists.new.create": "Add list", - "lists.new.title_placeholder": "New list title", - "lists.search": "Search among people you follow", - "lists.subheading": "Your lists", + "lists.edit": "Lijst bewerken", + "lists.new.create": "Lijst toevoegen", + "lists.new.title_placeholder": "Naam nieuwe lijst", + "lists.search": "Zoek naar mensen die je volgt", + "lists.subheading": "Jouw lijsten", "loading_indicator.label": "Laden…", "media_gallery.toggle_visible": "Media wel/niet tonen", "missing_indicator.label": "Niet gevonden", @@ -146,7 +146,7 @@ "navigation_bar.follow_requests": "Volgverzoeken", "navigation_bar.info": "Uitgebreide informatie", "navigation_bar.keyboard_shortcuts": "Toetsenbord sneltoetsen", - "navigation_bar.lists": "Lists", + "navigation_bar.lists": "Lijsten", "navigation_bar.logout": "Afmelden", "navigation_bar.mutes": "Genegeerde gebruikers", "navigation_bar.pins": "Vastgezette toots", diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json index f8b4751d6..0d1f7c971 100644 --- a/app/javascript/mastodon/locales/oc.json +++ b/app/javascript/mastodon/locales/oc.json @@ -91,7 +91,7 @@ "empty_column.hashtag": "I a pas encara de contengut ligat a aquesta etiqueta.", "empty_column.home": "Vòstre flux d’acuèlh es void. Visitatz {public} o utilizatz la recèrca per vos connectar a d’autras personas.", "empty_column.home.public_timeline": "lo flux public", - "empty_column.list": "I a pas res dins la lista pel moment.", + "empty_column.list": "I a pas res dins la lista pel moment. Quand de membres d’aquesta lista publiquen de novèls estatuts los veiretz aquí.", "empty_column.notifications": "Avètz pas encara de notificacions. Respondètz a qualqu’un per començar una conversacion.", "empty_column.public": "I a pas res aquí ! Escrivètz quicòm de public, o seguètz de personas d’autras instàncias per garnir lo flux public", "follow_request.authorize": "Autorizar", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index 6bac65865..70632846c 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -36,7 +36,7 @@ "column.favourites": "Favoritos", "column.follow_requests": "Seguidores pendentes", "column.home": "Página inicial", - "column.lists": "Lists", + "column.lists": "Listas", "column.mutes": "Usuários silenciados", "column.notifications": "Notificações", "column.pins": "Postagens fixadas", @@ -64,7 +64,7 @@ "confirmations.delete.confirm": "Excluir", "confirmations.delete.message": "Você tem certeza de que quer excluir esta postagem?", "confirmations.delete_list.confirm": "Delete", - "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", + "confirmations.delete_list.message": "Você tem certeza que quer deletar permanentemente a lista?", "confirmations.domain_block.confirm": "Esconder o domínio inteiro", "confirmations.domain_block.message": "Você quer mesmo bloquear {domain} inteiro? Na maioria dos casos, silenciar ou bloquear alguns usuários é o suficiente e o recomendado.", "confirmations.mute.confirm": "Silenciar", @@ -110,7 +110,7 @@ "keyboard_shortcuts.back": "para navegar de volta", "keyboard_shortcuts.boost": "para compartilhar", "keyboard_shortcuts.column": "Focar um status em uma das colunas", - "keyboard_shortcuts.compose": "to focus the compose textarea", + "keyboard_shortcuts.compose": "para focar a área de redação", "keyboard_shortcuts.description": "Description", "keyboard_shortcuts.down": "para mover para baixo na lista", "keyboard_shortcuts.enter": "to open status", @@ -127,14 +127,14 @@ "lightbox.close": "Fechar", "lightbox.next": "Próximo", "lightbox.previous": "Anterior", - "lists.account.add": "Add to list", - "lists.account.remove": "Remove from list", + "lists.account.add": "Adicionar a listas", + "lists.account.remove": "Remover da lista", "lists.delete": "Delete list", - "lists.edit": "Edit list", - "lists.new.create": "Add list", - "lists.new.title_placeholder": "New list title", - "lists.search": "Search among people you follow", - "lists.subheading": "Your lists", + "lists.edit": "Editar lista", + "lists.new.create": "Adicionar lista", + "lists.new.title_placeholder": "Novo título da lista", + "lists.search": "Procurar entre as pessoas que você segue", + "lists.subheading": "Suas listas", "loading_indicator.label": "Carregando...", "media_gallery.toggle_visible": "Esconder/Mostrar", "missing_indicator.label": "Não encontrado", @@ -146,7 +146,7 @@ "navigation_bar.follow_requests": "Seguidores pendentes", "navigation_bar.info": "Mais informações", "navigation_bar.keyboard_shortcuts": "Atalhos de teclado", - "navigation_bar.lists": "Lists", + "navigation_bar.lists": "Listas", "navigation_bar.logout": "Sair", "navigation_bar.mutes": "Usuários silenciados", "navigation_bar.pins": "Postagens fixadas", @@ -177,7 +177,7 @@ "onboarding.page_one.welcome": "Seja bem-vindo(a) ao Mastodon!", "onboarding.page_six.admin": "O administrador de sua instância é {admin}.", "onboarding.page_six.almost_done": "Quase acabando...", - "onboarding.page_six.appetoot": "Bon Appetoot!", + "onboarding.page_six.appetoot": "Bom Apetoot!", "onboarding.page_six.apps_available": "Há {apps} disponíveis para iOS, Android e outras plataformas.", "onboarding.page_six.github": "Mastodon é um software gratuito e de código aberto. Você pode reportar bugs, prequisitar novas funções ou contribuir para o código no {github}.", "onboarding.page_six.guidelines": "diretrizes da comunidade", diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json index 728fb3a10..15d5deb93 100644 --- a/app/javascript/mastodon/locales/pt.json +++ b/app/javascript/mastodon/locales/pt.json @@ -1,7 +1,7 @@ { "account.block": "Bloquear @{name}", "account.block_domain": "Esconder tudo do domínio {domain}", - "account.disclaimer_full": "Information below may reflect the user's profile incompletely.", + "account.disclaimer_full": "As informações abaixo podem refletir o perfil do usuário de forma incompleta.", "account.edit_profile": "Editar perfil", "account.follow": "Seguir", "account.followers": "Seguidores", @@ -19,7 +19,7 @@ "account.share": "Partilhar o perfil @{name}", "account.show_reblogs": "Mostrar partilhas de @{name}", "account.unblock": "Não bloquear @{name}", - "account.unblock_domain": "Unhide {domain}", + "account.unblock_domain": "Mostrar {domain}", "account.unfollow": "Deixar de seguir", "account.unmute": "Não silenciar @{name}", "account.unmute_notifications": "Deixar de silenciar @{name}", @@ -36,7 +36,7 @@ "column.favourites": "Favoritos", "column.follow_requests": "Seguidores Pendentes", "column.home": "Home", - "column.lists": "Lists", + "column.lists": "Listas", "column.mutes": "Utilizadores silenciados", "column.notifications": "Notificações", "column.pins": "Pinned toot", @@ -64,7 +64,7 @@ "confirmations.delete.confirm": "Eliminar", "confirmations.delete.message": "De certeza que queres eliminar esta publicação?", "confirmations.delete_list.confirm": "Delete", - "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", + "confirmations.delete_list.message": "Tens a certeza de que desejas apagar permanentemente esta lista?", "confirmations.domain_block.confirm": "Esconder tudo deste domínio", "confirmations.domain_block.message": "De certeza que queres bloquear por completo o domínio {domain}? Na maioria dos casos, silenciar ou bloquear alguns utilizadores é o suficiente e o recomendado.", "confirmations.mute.confirm": "Silenciar", @@ -88,12 +88,12 @@ "emoji_button.symbols": "Símbolos", "emoji_button.travel": "Viagens & Lugares", "empty_column.community": "Ainda não existe conteúdo local para mostrar!", - "empty_column.hashtag": "Não foram encontradas publicações com essa hashtag", + "empty_column.hashtag": "Não foram encontradas publicações com essa hashtag.", "empty_column.home": "Ainda não segues qualquer utilizador. Visita {public} ou utiliza a pesquisa para procurar outros utilizadores.", "empty_column.home.public_timeline": "global", "empty_column.list": "Ainda não existem publicações nesta lista.", "empty_column.notifications": "Não tens notificações. Interage com outros utilizadores para iniciar uma conversa.", - "empty_column.public": "Não há nada aqui! Escreve algo publicamente ou segue outros utilizadores para ver aqui os conteúdos públicos.", + "empty_column.public": "Não há nada aqui! Escreve algo publicamente ou segue outros utilizadores para ver aqui os conteúdos públicos", "follow_request.authorize": "Autorizar", "follow_request.reject": "Rejeitar", "getting_started.appsshort": "Aplicações", @@ -116,7 +116,7 @@ "keyboard_shortcuts.enter": "para expandir uma publicação", "keyboard_shortcuts.favourite": "para adicionar aos favoritos", "keyboard_shortcuts.heading": "Atalhos do teclado", - "keyboard_shortcuts.hotkey": "Hotkey", + "keyboard_shortcuts.hotkey": "Atalho", "keyboard_shortcuts.legend": "para mostrar esta legenda", "keyboard_shortcuts.mention": "para mencionar o autor", "keyboard_shortcuts.reply": "para responder", @@ -127,14 +127,14 @@ "lightbox.close": "Fechar", "lightbox.next": "Próximo", "lightbox.previous": "Anterior", - "lists.account.add": "Add to list", - "lists.account.remove": "Remove from list", + "lists.account.add": "Adicionar à lista", + "lists.account.remove": "Remover da lista", "lists.delete": "Delete list", - "lists.edit": "Edit list", - "lists.new.create": "Add list", - "lists.new.title_placeholder": "New list title", - "lists.search": "Search among people you follow", - "lists.subheading": "Your lists", + "lists.edit": "Editar lista", + "lists.new.create": "Adicionar lista", + "lists.new.title_placeholder": "Novo título da lista", + "lists.search": "Pesquisa entre as pessoas que segues", + "lists.subheading": "As tuas listas", "loading_indicator.label": "A carregar...", "media_gallery.toggle_visible": "Esconder/Mostrar", "missing_indicator.label": "Não encontrado", @@ -146,7 +146,7 @@ "navigation_bar.follow_requests": "Seguidores pendentes", "navigation_bar.info": "Mais informações", "navigation_bar.keyboard_shortcuts": "Atalhos de teclado", - "navigation_bar.lists": "Lists", + "navigation_bar.lists": "Listas", "navigation_bar.logout": "Sair", "navigation_bar.mutes": "Utilizadores silenciados", "navigation_bar.pins": "Posts fixos", @@ -209,13 +209,13 @@ "search_popout.search_format": "Formato avançado de pesquisa", "search_popout.tips.hashtag": "hashtag", "search_popout.tips.status": "status", - "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags", + "search_popout.tips.text": "O texto simples retorna a correspondência de nomes, utilizadores e hashtags", "search_popout.tips.user": "utilizador", "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}", "standalone.public_title": "Espreitar lá dentro...", "status.cannot_reblog": "Este post não pode ser partilhado", "status.delete": "Eliminar", - "status.embed": "Embed", + "status.embed": "Incorporar", "status.favourite": "Adicionar aos favoritos", "status.load_more": "Carregar mais", "status.media_hidden": "Media escondida", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index cb5607cc5..3d6bd5334 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -91,7 +91,7 @@ "empty_column.hashtag": "这个话题标签下暂时没有内容。", "empty_column.home": "你还没有关注任何用户。快看看{public},向其他用户搭讪吧。", "empty_column.home.public_timeline": "公共时间轴", - "empty_column.list": "这个列表中暂时没有内容。", + "empty_column.list": "这个列表中暂时没有内容。列表中用户所发送的的新嘟文将会在这里显示。", "empty_column.notifications": "你还没有收到过通知信息,快向其他用户搭讪吧。", "empty_column.public": "这里神马都没有!写一些公开的嘟文,或者关注其他实例的用户,这里就会有嘟文出现了哦!", "follow_request.authorize": "同意", diff --git a/app/javascript/mastodon/stream.js b/app/javascript/mastodon/stream.js index 36c68ffc5..9a6f4f26d 100644 --- a/app/javascript/mastodon/stream.js +++ b/app/javascript/mastodon/stream.js @@ -62,7 +62,13 @@ export function connectStream(path, pollingRefresh = null, callbacks = () => ({ export default function getStream(streamingAPIBaseURL, accessToken, stream, { connected, received, disconnected, reconnected }) { - const ws = new WebSocketClient(`${streamingAPIBaseURL}/api/v1/streaming/?access_token=${accessToken}&stream=${stream}`); + const params = [ `stream=${stream}` ]; + + if (accessToken !== null) { + params.push(`access_token=${accessToken}`); + } + + const ws = new WebSocketClient(`${streamingAPIBaseURL}/api/v1/streaming/?${params.join('&')}`); ws.onopen = connected; ws.onmessage = e => received(JSON.parse(e.data)); diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index da789ba06..be28473e5 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -265,198 +265,286 @@ .compose-form { padding: 10px; -} - -.compose-form__warning { - color: darken($ui-secondary-color, 65%); - margin-bottom: 15px; - background: $ui-primary-color; - box-shadow: 0 2px 6px rgba($base-shadow-color, 0.3); - padding: 8px 10px; - border-radius: 4px; - font-size: 13px; - font-weight: 400; - strong { + .compose-form__warning { color: darken($ui-secondary-color, 65%); - font-weight: 500; + margin-bottom: 15px; + background: $ui-primary-color; + box-shadow: 0 2px 6px rgba($base-shadow-color, 0.3); + padding: 8px 10px; + border-radius: 4px; + font-size: 13px; + font-weight: 400; - @each $lang in $cjk-langs { - &:lang(#{$lang}) { - font-weight: 700; + strong { + color: darken($ui-secondary-color, 65%); + font-weight: 500; + + @each $lang in $cjk-langs { + &:lang(#{$lang}) { + font-weight: 700; + } + } + } + + a { + color: darken($ui-primary-color, 33%); + font-weight: 500; + text-decoration: underline; + + &:hover, + &:active, + &:focus { + text-decoration: none; } } } - a { - color: darken($ui-primary-color, 33%); - font-weight: 500; - text-decoration: underline; + .compose-form__autosuggest-wrapper { + position: relative; + + .emoji-picker-dropdown { + position: absolute; + right: 5px; + top: 5px; + } + } + + .autosuggest-textarea, + .spoiler-input { + position: relative; + } + + .autosuggest-textarea__textarea, + .spoiler-input__input { + display: block; + box-sizing: border-box; + width: 100%; + margin: 0; + color: $ui-base-color; + background: $simple-background-color; + padding: 10px; + font-family: inherit; + font-size: 14px; + resize: vertical; + border: 0; + outline: 0; - &:hover, - &:active, &:focus { - text-decoration: none; + outline: 0; + } + + @media screen and (max-width: 600px) { + font-size: 16px; } } -} -.compose-form__modifiers { - color: $ui-base-color; - font-family: inherit; - font-size: 14px; - background: $simple-background-color; - border-radius: 0 0 4px; -} + .spoiler-input__input { + border-radius: 4px; + } -.compose-form__buttons-wrapper { - display: flex; - justify-content: space-between; -} + .autosuggest-textarea__textarea { + min-height: 100px; + border-radius: 4px 4px 0 0; + padding-bottom: 0; + padding-right: 10px + 22px; + resize: none; -.compose-form__buttons { - padding: 10px; - background: darken($simple-background-color, 8%); - box-shadow: inset 0 5px 5px rgba($base-shadow-color, 0.05); - border-radius: 0 0 4px 4px; - display: flex; + @media screen and (max-width: 600px) { + height: 100px !important; // prevent auto-resize textarea + resize: vertical; + } + } - .icon-button { - box-sizing: content-box; - padding: 0 3px; + .autosuggest-textarea__suggestions { + box-sizing: border-box; + display: none; + position: absolute; + top: 100%; + width: 100%; + z-index: 99; + box-shadow: 4px 4px 6px rgba($base-shadow-color, 0.4); + background: $ui-secondary-color; + border-radius: 0 0 4px 4px; + color: $ui-base-color; + font-size: 14px; + padding: 6px; + + &.autosuggest-textarea__suggestions--visible { + display: block; + } } -} -.compose-form__upload-button-icon { - line-height: 27px; -} + .autosuggest-textarea__suggestions__item { + padding: 10px; + cursor: pointer; + border-radius: 4px; -.compose-form__sensitive-button { - display: none; + &:hover, + &:focus, + &:active, + &.selected { + background: darken($ui-secondary-color, 10%); + } + } + + .autosuggest-account, + .autosuggest-emoji { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; + line-height: 18px; + font-size: 14px; + } - &.compose-form__sensitive-button--visible { + .autosuggest-account-icon, + .autosuggest-emoji img { display: block; + margin-right: 8px; + width: 16px; + height: 16px; } - .compose-form__sensitive-button__icon { - line-height: 27px; + .autosuggest-account .display-name__account { + color: lighten($ui-base-color, 36%); } -} -.compose-form__upload-wrapper { - overflow: hidden; -} + .compose-form__modifiers { + color: $ui-base-color; + font-family: inherit; + font-size: 14px; + background: $simple-background-color; -.compose-form__uploads-wrapper { - display: flex; - flex-direction: row; - padding: 5px; - flex-wrap: wrap; -} + .compose-form__upload-wrapper { + overflow: hidden; + } -.compose-form__upload { - flex: 1 1 0; - min-width: 40%; - margin: 5px; + .compose-form__uploads-wrapper { + display: flex; + flex-direction: row; + padding: 5px; + flex-wrap: wrap; + } - &-description { - position: absolute; - z-index: 2; - bottom: 0; - left: 0; - right: 0; - box-sizing: border-box; - background: linear-gradient(0deg, rgba($base-shadow-color, 0.8) 0, rgba($base-shadow-color, 0.35) 80%, transparent); - padding: 10px; - opacity: 0; - transition: opacity .1s ease; + .compose-form__upload { + flex: 1 1 0; + min-width: 40%; + margin: 5px; - input { - background: transparent; - color: $ui-secondary-color; - border: 0; - padding: 0; - margin: 0; - width: 100%; - font-family: inherit; - font-size: 14px; - font-weight: 500; + &-description { + position: absolute; + z-index: 2; + bottom: 0; + left: 0; + right: 0; + box-sizing: border-box; + background: linear-gradient(0deg, rgba($base-shadow-color, 0.8) 0, rgba($base-shadow-color, 0.35) 80%, transparent); + padding: 10px; + opacity: 0; + transition: opacity .1s ease; + + input { + background: transparent; + color: $ui-secondary-color; + border: 0; + padding: 0; + margin: 0; + width: 100%; + font-family: inherit; + font-size: 14px; + font-weight: 500; + + &:focus { + color: $white; + } - &:focus { - color: $white; + &::placeholder { + opacity: 0.54; + color: $ui-secondary-color; + } + } + + &.active { + opacity: 1; + } } - &::placeholder { - opacity: 0.54; - color: $ui-secondary-color; + .icon-button { + mix-blend-mode: difference; } } - &.active { - opacity: 1; + .compose-form__upload-thumbnail { + border-radius: 4px; + background-position: center; + background-size: cover; + background-repeat: no-repeat; + height: 100px; + width: 100%; } } - .icon-button { - mix-blend-mode: difference; - } -} + .compose-form__buttons-wrapper { + padding: 10px; + background: darken($simple-background-color, 8%); + border-radius: 0 0 4px 4px; + display: flex; + justify-content: space-between; -.compose-form__upload-thumbnail { - border-radius: 4px; - background-position: center; - background-size: cover; - background-repeat: no-repeat; - height: 100px; - width: 100%; -} + .compose-form__buttons { + display: flex; -.compose-form__label { - display: block; - line-height: 24px; - vertical-align: middle; + .compose-form__upload-button-icon { + line-height: 27px; + } - &.with-border { - border-top: 1px solid $ui-base-color; - padding-top: 10px; - } + .compose-form__sensitive-button { + display: none; - .compose-form__label__text { - display: inline-block; - vertical-align: middle; - margin-bottom: 14px; - margin-left: 8px; - color: $ui-primary-color; - } -} + &.compose-form__sensitive-button--visible { + display: block; + } -.compose-form__textarea, -.follow-form__input { - background: $simple-background-color; + .compose-form__sensitive-button__icon { + line-height: 27px; + } + } + } - &:disabled { - background: $ui-secondary-color; - } -} + .icon-button { + box-sizing: content-box; + padding: 0 3px; + } -.compose-form__autosuggest-wrapper { - position: relative; + .character-counter__wrapper { + align-self: center; + margin-right: 4px; - .emoji-picker-dropdown { - position: absolute; - right: 5px; - top: 5px; + .character-counter { + cursor: default; + font-family: 'mastodon-font-sans-serif', sans-serif; + font-size: 14px; + font-weight: 600; + color: lighten($ui-base-color, 12%); + + &.character-counter--over { + color: $warning-red; + } + } + } } -} -.compose-form__publish { - display: flex; - min-width: 0; -} + .compose-form__publish { + display: flex; + justify-content: flex-end; + min-width: 0; -.compose-form__publish-button-wrapper { - overflow: hidden; - padding-top: 10px; + .compose-form__publish-button-wrapper { + overflow: hidden; + padding-top: 10px; + } + } } .emojione { @@ -1973,121 +2061,6 @@ cursor: default; } -.autosuggest-textarea, -.spoiler-input { - position: relative; -} - -.autosuggest-textarea__textarea, -.spoiler-input__input { - display: block; - box-sizing: border-box; - width: 100%; - margin: 0; - color: $ui-base-color; - background: $simple-background-color; - padding: 10px; - font-family: inherit; - font-size: 14px; - resize: vertical; - border: 0; - outline: 0; - - &:focus { - outline: 0; - } - - @media screen and (max-width: 600px) { - font-size: 16px; - } -} - -.spoiler-input__input { - border-radius: 4px; -} - -.autosuggest-textarea__textarea { - min-height: 100px; - border-radius: 4px 4px 0 0; - padding-bottom: 0; - padding-right: 10px + 22px; - resize: none; - - @media screen and (max-width: 600px) { - height: 100px !important; // prevent auto-resize textarea - resize: vertical; - } -} - -.autosuggest-textarea__suggestions { - box-sizing: border-box; - display: none; - position: absolute; - top: 100%; - width: 100%; - z-index: 99; - box-shadow: 4px 4px 6px rgba($base-shadow-color, 0.4); - background: $ui-secondary-color; - border-radius: 0 0 4px 4px; - color: $ui-base-color; - font-size: 14px; - padding: 6px; - - &.autosuggest-textarea__suggestions--visible { - display: block; - } -} - -.autosuggest-textarea__suggestions__item { - padding: 10px; - cursor: pointer; - border-radius: 4px; - - &:hover, - &:focus, - &:active, - &.selected { - background: darken($ui-secondary-color, 10%); - } -} - -.autosuggest-account, -.autosuggest-emoji { - display: flex; - flex-direction: row; - align-items: center; - justify-content: flex-start; - line-height: 18px; - font-size: 14px; -} - -.autosuggest-account-icon, -.autosuggest-emoji img { - display: block; - margin-right: 8px; - width: 16px; - height: 16px; -} - -.autosuggest-account .display-name__account { - color: lighten($ui-base-color, 36%); -} - -.character-counter__wrapper { - line-height: 36px; - margin: 0 16px 0 8px; - padding-top: 10px; -} - -.character-counter { - cursor: default; - font-size: 16px; -} - -.character-counter--over { - color: $warning-red; -} - .getting-started__wrapper { position: relative; overflow-y: auto; diff --git a/app/javascript/styles/mastodon/rtl.scss b/app/javascript/styles/mastodon/rtl.scss index 67bfa8a38..77420c84b 100644 --- a/app/javascript/styles/mastodon/rtl.scss +++ b/app/javascript/styles/mastodon/rtl.scss @@ -7,9 +7,9 @@ body.rtl { margin-left: 5px; } - .character-counter__wrapper { - margin-right: 8px; - margin-left: 16px; + .compose-form .compose-form__buttons-wrapper .character-counter__wrapper { + margin-right: 0; + margin-left: 4px; } .navigation-bar__profile { @@ -30,6 +30,22 @@ body.rtl { .column-header__buttons { left: 0; right: auto; + margin-left: -15px; + margin-right: 0; + } + + .column-inline-form .icon-button { + margin-left: 0; + margin-right: 5px; + } + + .column-header__links .text-btn { + margin-left: 10px; + margin-right: 0; + } + + .account__avatar-wrapper { + float: right; } .column-header__back-button { @@ -41,10 +57,6 @@ body.rtl { float: left; } - .compose-form__modifiers { - border-radius: 0 0 0 4px; - } - .setting-toggle { margin-left: 0; margin-right: 8px; diff --git a/app/lib/provider_discovery.rb b/app/lib/provider_discovery.rb index bcc4ed500..04ba38101 100644 --- a/app/lib/provider_discovery.rb +++ b/app/lib/provider_discovery.rb @@ -2,13 +2,26 @@ class ProviderDiscovery < OEmbed::ProviderDiscovery class << self + def get(url, **options) + provider = discover_provider(url, options) + + options.delete(:html) + + provider.get(url, options) + end + def discover_provider(url, **options) - res = Request.new(:get, url).perform format = options[:format] - raise OEmbed::NotFound, url if res.code != 200 || res.mime_type != 'text/html' + if options[:html] + html = Nokogiri::HTML(options[:html]) + else + res = Request.new(:get, url).perform + + raise OEmbed::NotFound, url if res.code != 200 || res.mime_type != 'text/html' - html = Nokogiri::HTML(res.to_s) + html = Nokogiri::HTML(res.to_s) + end if format.nil? || format == :json provider_endpoint ||= html.at_xpath('//link[@type="application/json+oembed"]')&.attribute('href')&.value diff --git a/app/models/account_filter.rb b/app/models/account_filter.rb index 189872368..dc7a03039 100644 --- a/app/models/account_filter.rb +++ b/app/models/account_filter.rb @@ -45,6 +45,8 @@ class AccountFilter else Account.default_scoped end + when 'staff' + accounts_with_users.merge User.staff else raise "Unknown filter: #{key}" end diff --git a/app/models/custom_emoji_filter.rb b/app/models/custom_emoji_filter.rb index 2d1394a59..2c09ed65c 100644 --- a/app/models/custom_emoji_filter.rb +++ b/app/models/custom_emoji_filter.rb @@ -27,6 +27,8 @@ class CustomEmojiFilter CustomEmoji.remote when 'by_domain' CustomEmoji.where(domain: value) + when 'shortcode' + CustomEmoji.where(shortcode: value) else raise "Unknown filter: #{key}" end diff --git a/app/services/fetch_link_card_service.rb b/app/services/fetch_link_card_service.rb index 7f4518ea7..d0472a1d7 100644 --- a/app/services/fetch_link_card_service.rb +++ b/app/services/fetch_link_card_service.rb @@ -40,6 +40,12 @@ class FetchLinkCardService < BaseService return if res.code != 405 && (res.code != 200 || res.mime_type != 'text/html') + @response = Request.new(:get, @url).perform + + return if @response.code != 200 || @response.mime_type != 'text/html' + + @html = @response.to_s + attempt_oembed || attempt_opengraph end @@ -70,30 +76,32 @@ class FetchLinkCardService < BaseService end def attempt_oembed - response = OEmbed::Providers.get(@url) + embed = OEmbed::Providers.get(@url, html: @html) - return false unless response.respond_to?(:type) + return false unless embed.respond_to?(:type) - @card.type = response.type - @card.title = response.respond_to?(:title) ? response.title : '' - @card.author_name = response.respond_to?(:author_name) ? response.author_name : '' - @card.author_url = response.respond_to?(:author_url) ? response.author_url : '' - @card.provider_name = response.respond_to?(:provider_name) ? response.provider_name : '' - @card.provider_url = response.respond_to?(:provider_url) ? response.provider_url : '' + @card.type = embed.type + @card.title = embed.respond_to?(:title) ? embed.title : '' + @card.author_name = embed.respond_to?(:author_name) ? embed.author_name : '' + @card.author_url = embed.respond_to?(:author_url) ? embed.author_url : '' + @card.provider_name = embed.respond_to?(:provider_name) ? embed.provider_name : '' + @card.provider_url = embed.respond_to?(:provider_url) ? embed.provider_url : '' @card.width = 0 @card.height = 0 case @card.type when 'link' - @card.image = URI.parse(response.thumbnail_url) if response.respond_to?(:thumbnail_url) + @card.image = URI.parse(embed.thumbnail_url) if embed.respond_to?(:thumbnail_url) when 'photo' - @card.embed_url = response.url - @card.width = response.width.presence || 0 - @card.height = response.height.presence || 0 + return false unless embed.respond_to?(:url) + @card.embed_url = embed.url + @card.image = URI.parse(embed.url) + @card.width = embed.width.presence || 0 + @card.height = embed.height.presence || 0 when 'video' - @card.width = response.width.presence || 0 - @card.height = response.height.presence || 0 - @card.html = Formatter.instance.sanitize(response.html, Sanitize::Config::MASTODON_OEMBED) + @card.width = embed.width.presence || 0 + @card.height = embed.height.presence || 0 + @card.html = Formatter.instance.sanitize(embed.html, Sanitize::Config::MASTODON_OEMBED) when 'rich' # Most providers rely on <script> tags, which is a no-no return false @@ -105,17 +113,11 @@ class FetchLinkCardService < BaseService end def attempt_opengraph - response = Request.new(:get, @url).perform - - return if response.code != 200 || response.mime_type != 'text/html' - - html = response.to_s - detector = CharlockHolmes::EncodingDetector.new detector.strip_tags = true - guess = detector.detect(html, response.charset) - page = Nokogiri::HTML(html, nil, guess&.fetch(:encoding, nil)) + guess = detector.detect(@html, @response.charset) + page = Nokogiri::HTML(@html, nil, guess&.fetch(:encoding, nil)) if meta_property(page, 'twitter:player') @card.type = :video @@ -132,16 +134,16 @@ class FetchLinkCardService < BaseService @card.image_remote_url = meta_property(page, 'og:image') if meta_property(page, 'og:image') end - @card.title = meta_property(page, 'og:title').presence || page.at_xpath('//title')&.content || '' - @card.description = meta_property(page, 'og:description').presence || meta_property(page, 'description') || '' + @card.title = meta_property(page, 'og:title').presence || page.at_xpath('//title')&.content || '' + @card.description = meta_property(page, 'og:description').presence || meta_property(page, 'description') || '' return if @card.title.blank? && @card.html.blank? @card.save_with_optional_image! end - def meta_property(html, property) - html.at_xpath("//meta[@property=\"#{property}\"]")&.attribute('content')&.value || html.at_xpath("//meta[@name=\"#{property}\"]")&.attribute('content')&.value + def meta_property(page, property) + page.at_xpath("//meta[@property=\"#{property}\"]")&.attribute('content')&.value || page.at_xpath("//meta[@name=\"#{property}\"]")&.attribute('content')&.value end def lock_options diff --git a/app/services/fetch_remote_status_service.rb b/app/services/fetch_remote_status_service.rb index 9c009335b..9c3008035 100644 --- a/app/services/fetch_remote_status_service.rb +++ b/app/services/fetch_remote_status_service.rb @@ -40,6 +40,6 @@ class FetchRemoteStatusService < BaseService end def confirmed_domain?(domain, account) - account.domain.nil? || domain.casecmp(account.domain).zero? || domain.casecmp(Addressable::URI.parse(account.remote_url || account.uri).normalized_host).zero? + account.domain.nil? || domain.casecmp(account.domain).zero? || domain.casecmp(Addressable::URI.parse(account.remote_url.presence || account.uri).normalized_host).zero? end end diff --git a/app/services/follow_service.rb b/app/services/follow_service.rb index 20579ca63..ac0207a0a 100644 --- a/app/services/follow_service.rb +++ b/app/services/follow_service.rb @@ -22,7 +22,7 @@ class FollowService < BaseService elsif source_account.requested?(target_account) # This isn't managed by a method in AccountInteractions, so we modify it # ourselves if necessary. - req = follow_requests.find_by(target_account: other_account) + req = source_account.follow_requests.find_by(target_account: target_account) req.update!(show_reblogs: reblogs) return end diff --git a/app/views/admin/accounts/_account.html.haml b/app/views/admin/accounts/_account.html.haml index 5265d77f6..598f6cddd 100644 --- a/app/views/admin/accounts/_account.html.haml +++ b/app/views/admin/accounts/_account.html.haml @@ -4,22 +4,11 @@ %td.domain - unless account.local? = link_to account.domain, admin_accounts_path(by_domain: account.domain) - %td.protocol - - unless account.local? - = account.protocol.humanize - %td.confirmed - - if account.local? - - if account.user_confirmed? - %i.fa.fa-check - - else - %i.fa.fa-times - %td.subscribed + %td - if account.local? - = t('admin.accounts.location.local') - - elsif account.subscribed? - %i.fa.fa-check + = t("admin.accounts.roles.#{account.user&.role}") - else - %i.fa.fa-times + = account.protocol.humanize %td = table_link_to 'circle', t('admin.accounts.web'), web_path("accounts/#{account.id}") = table_link_to 'globe', t('admin.accounts.public'), TagManager.instance.url_for(account) diff --git a/app/views/admin/accounts/index.html.haml b/app/views/admin/accounts/index.html.haml index 27a0682d8..6aa39a80a 100644 --- a/app/views/admin/accounts/index.html.haml +++ b/app/views/admin/accounts/index.html.haml @@ -31,6 +31,11 @@ - else = filter_link_to t('admin.accounts.moderation.suspended'), suspended: '1' .filter-subset + %strong= t('admin.accounts.role') + %ul + %li= filter_link_to t('admin.accounts.moderation.all'), staff: nil + %li= filter_link_to t('admin.accounts.roles.staff'), staff: '1' + .filter-subset %strong= t('admin.accounts.order.title') %ul %li= filter_link_to t('admin.accounts.order.alphabetic'), recent: nil @@ -56,9 +61,7 @@ %tr %th= t('admin.accounts.username') %th= t('admin.accounts.domain') - %th= t('admin.accounts.protocol') - %th= t('admin.accounts.confirmed') - %th= fa_icon 'paper-plane-o' + %th %th %tbody = render @accounts diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml index ddb1cf15d..5f5d0995c 100644 --- a/app/views/admin/accounts/show.html.haml +++ b/app/views/admin/accounts/show.html.haml @@ -104,7 +104,7 @@ - else = link_to t('admin.accounts.perform_full_suspension'), admin_account_suspension_path(@account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button' if can?(:suspend, @account) -- unless @account.local? +- if !@account.local? && @account.hub_url.present? %hr %h3 OStatus @@ -132,6 +132,7 @@ - if @account.subscribed? = link_to t('admin.accounts.unsubscribe'), unsubscribe_admin_account_path(@account.id), method: :post, class: 'button negative' if can?(:unsubscribe, @account) +- if !@account.local? && @account.inbox_url.present? %hr %h3 ActivityPub diff --git a/app/views/admin/custom_emojis/_custom_emoji.html.haml b/app/views/admin/custom_emojis/_custom_emoji.html.haml index bab34bc8d..f7fd2538c 100644 --- a/app/views/admin/custom_emojis/_custom_emoji.html.haml +++ b/app/views/admin/custom_emojis/_custom_emoji.html.haml @@ -7,7 +7,7 @@ - if custom_emoji.local? = t('admin.accounts.location.local') - else - = custom_emoji.domain + = link_to custom_emoji.domain, admin_custom_emojis_path(by_domain: custom_emoji.domain) %td - if custom_emoji.local? - if custom_emoji.visible_in_picker diff --git a/app/views/admin/custom_emojis/index.html.haml b/app/views/admin/custom_emojis/index.html.haml index 20ffb8529..89ea3a6fe 100644 --- a/app/views/admin/custom_emojis/index.html.haml +++ b/app/views/admin/custom_emojis/index.html.haml @@ -17,6 +17,20 @@ - else = filter_link_to t('admin.accounts.location.remote'), remote: '1', local: nil += form_tag admin_custom_emojis_url, method: 'GET', class: 'simple_form' do + .fields-group + - Admin::FilterHelper::CUSTOM_EMOJI_FILTERS.each do |key| + - if params[key].present? + = hidden_field_tag key, params[key] + + - %i(shortcode by_domain).each do |key| + .input.string.optional + = text_field_tag key, params[key], class: 'string optional', placeholder: I18n.t("admin.custom_emojis.#{key}") + + .actions + %button= t('admin.accounts.search') + = link_to t('admin.accounts.reset'), admin_accounts_path, class: 'button negative' + .table-wrapper %table.table %thead |