diff --git a/CODEOWNERS b/.github/CODEOWNERS
similarity index 100%
rename from CODEOWNERS
rename to .github/CODEOWNERS
diff --git a/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
similarity index 100%
rename from ISSUE_TEMPLATE.md
rename to .github/ISSUE_TEMPLATE.md
diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb
index e9a512e70c..7428c3f229 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 3fa2a0b72e..ccab03de42 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 9443934b30..359c43d0e3 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 f7c484ee3a..570505833f 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 f5d67b34e8..3ec1d77304 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 7890755f33..a876c5197e 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 (
+
+
-
-
@@ -199,11 +199,11 @@ export default class ComposeForm extends ImmutablePureComponent {
+
+
-
-
-
-
+
+
);
diff --git a/app/javascript/mastodon/features/compose/components/reply_indicator.js b/app/javascript/mastodon/features/compose/components/reply_indicator.js
index 7672440b49..d8cda96f32 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 (
@@ -55,7 +59,7 @@ export default class ReplyIndicator extends ImmutablePureComponent {
-
+
);
}
diff --git a/app/javascript/mastodon/features/compose/components/upload.js b/app/javascript/mastodon/features/compose/components/upload.js
index 6ab76492a9..3a3d177100 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 (
diff --git a/app/javascript/mastodon/features/list_timeline/index.js b/app/javascript/mastodon/features/list_timeline/index.js
index 1dcd4de144..ae136e48f9 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={}
+ emptyMessage={}
/>
);
diff --git a/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js b/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js
index f15fbb2f40..f14be2aafb 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 de4b5320a5..5805d1a105 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 93ed9e605d..c5b3c20d4f 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 ec66a0027e..d699a69dfd 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 f705937fd0..62d85a5e1e 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 0f766af6a7..3fc4a8c960 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 a7a8876d0e..01cbd26572 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 bb0b1a9fd8..6398daa110 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 40ad66a7fd..68abd906f4 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 c290ed7673..9044c2011e 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 f8b4751d6f..0d1f7c9716 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 6bac65865e..70632846c9 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 728fb3a10e..15d5deb932 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 cb5607cc5d..3d6bd5334a 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 36c68ffc5b..9a6f4f26d1 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 da789ba067..be28473e53 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 67bfa8a385..77420c84b9 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 bcc4ed500e..04ba381010 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 1898723682..dc7a03039f 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 2d1394a597..2c09ed65ca 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 7f4518ea7f..d0472a1d7f 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