diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/controllers/api/v1/tags_controller.rb | 1 | ||||
-rw-r--r-- | app/javascript/flavours/glitch/components/hashtag.js | 18 | ||||
-rw-r--r-- | app/javascript/flavours/glitch/features/ui/components/image_loader.js | 26 | ||||
-rw-r--r-- | app/javascript/flavours/glitch/styles/components/index.scss | 6 | ||||
-rw-r--r-- | app/javascript/flavours/glitch/styles/components/search.scss | 7 | ||||
-rw-r--r-- | app/javascript/mastodon/components/hashtag.js | 18 | ||||
-rw-r--r-- | app/javascript/mastodon/features/ui/components/image_loader.js | 26 | ||||
-rw-r--r-- | app/javascript/mastodon/locales/defaultMessages.json | 8 | ||||
-rw-r--r-- | app/javascript/mastodon/locales/en.json | 3 | ||||
-rw-r--r-- | app/javascript/mastodon/locales/th.json | 6 | ||||
-rw-r--r-- | app/javascript/mastodon/locales/zh-CN.json | 10 | ||||
-rw-r--r-- | app/javascript/styles/mastodon/components.scss | 13 | ||||
-rw-r--r-- | app/models/account.rb | 2 | ||||
-rw-r--r-- | app/models/tag.rb | 2 |
14 files changed, 97 insertions, 49 deletions
diff --git a/app/controllers/api/v1/tags_controller.rb b/app/controllers/api/v1/tags_controller.rb index d45015ff5..9e5c53330 100644 --- a/app/controllers/api/v1/tags_controller.rb +++ b/app/controllers/api/v1/tags_controller.rb @@ -24,6 +24,7 @@ class Api::V1::TagsController < Api::BaseController private def set_or_create_tag + return not_found unless /\A(#{Tag::HASHTAG_NAME_RE})\z/.match?(params[:id]) @tag = Tag.find_normalized(params[:id]) || Tag.new(name: Tag.normalize(params[:id]), display_name: params[:id]) end end diff --git a/app/javascript/flavours/glitch/components/hashtag.js b/app/javascript/flavours/glitch/components/hashtag.js index 769185a2b..5bbf32c87 100644 --- a/app/javascript/flavours/glitch/components/hashtag.js +++ b/app/javascript/flavours/glitch/components/hashtag.js @@ -1,7 +1,7 @@ // @ts-check import React from 'react'; import { Sparklines, SparklinesCurve } from 'react-sparklines'; -import { FormattedMessage } from 'react-intl'; +import { FormattedMessage, injectIntl, defineMessages } from 'react-intl'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import Permalink from './permalink'; @@ -9,6 +9,10 @@ import ShortNumber from 'flavours/glitch/components/short_number'; import Skeleton from 'flavours/glitch/components/skeleton'; import classNames from 'classnames'; +const messages = defineMessages({ + totalVolume: { id: 'hashtag.total_volume', defaultMessage: 'Total volume in the last {days, plural, one {day} other {{days} days}}' }, +}); + class SilentErrorBoundary extends React.Component { static propTypes = { @@ -41,10 +45,11 @@ class SilentErrorBoundary extends React.Component { const accountsCountRenderer = (displayNumber, pluralReady) => ( <FormattedMessage id='trends.counter_by_accounts' - defaultMessage='{count, plural, one {{counter} person} other {{counter} people}} talking' + defaultMessage='{count, plural, one {{counter} person} other {{counter} people}} in the past {days, plural, one {day} other {{days} days}}' values={{ count: pluralReady, counter: <strong>{displayNumber}</strong>, + days: 2, }} /> ); @@ -64,7 +69,7 @@ ImmutableHashtag.propTypes = { hashtag: ImmutablePropTypes.map.isRequired, }; -const Hashtag = ({ name, href, to, people, uses, history, className }) => ( +const Hashtag = injectIntl(({ name, href, to, people, uses, history, className, intl }) => ( <div className={classNames('trends__item', className)}> <div className='trends__item__name'> <Permalink href={href} to={to}> @@ -74,9 +79,10 @@ const Hashtag = ({ name, href, to, people, uses, history, className }) => ( {typeof people !== 'undefined' ? <ShortNumber value={people} renderer={accountsCountRenderer} /> : <Skeleton width={100} />} </div> - <div className='trends__item__current'> + <abbr className='trends__item__current' title={intl.formatMessage(messages.totalVolume, { days: 2 })}> {typeof uses !== 'undefined' ? <ShortNumber value={uses} /> : <Skeleton width={42} height={36} />} - </div> + <span className='trends__item__current__asterisk'>*</span> + </abbr> <div className='trends__item__sparkline'> <SilentErrorBoundary> @@ -86,7 +92,7 @@ const Hashtag = ({ name, href, to, people, uses, history, className }) => ( </SilentErrorBoundary> </div> </div> -); +)); Hashtag.propTypes = { name: PropTypes.string, diff --git a/app/javascript/flavours/glitch/features/ui/components/image_loader.js b/app/javascript/flavours/glitch/features/ui/components/image_loader.js index c6f16a792..dfa0efe49 100644 --- a/app/javascript/flavours/glitch/features/ui/components/image_loader.js +++ b/app/javascript/flavours/glitch/features/ui/components/image_loader.js @@ -1,10 +1,10 @@ -import React from 'react'; -import PropTypes from 'prop-types'; import classNames from 'classnames'; +import PropTypes from 'prop-types'; +import React, { PureComponent } from 'react'; import { LoadingBar } from 'react-redux-loading-bar'; import ZoomableImage from './zoomable_image'; -export default class ImageLoader extends React.PureComponent { +export default class ImageLoader extends PureComponent { static propTypes = { alt: PropTypes.string, @@ -43,7 +43,7 @@ export default class ImageLoader extends React.PureComponent { this.loadImage(this.props); } - componentWillReceiveProps (nextProps) { + UNSAFE_componentWillReceiveProps (nextProps) { if (this.props.src !== nextProps.src) { this.loadImage(nextProps); } @@ -139,14 +139,18 @@ export default class ImageLoader extends React.PureComponent { return ( <div className={className}> - <LoadingBar loading={loading ? 1 : 0} className='loading-bar' style={{ width: this.state.width || width }} /> {loading ? ( - <canvas - className='image-loader__preview-canvas' - ref={this.setCanvasRef} - width={width} - height={height} - /> + <> + <div className='loading-bar__container' style={{ width: this.state.width || width }}> + <LoadingBar className='loading-bar' loading={1} /> + </div> + <canvas + className='image-loader__preview-canvas' + ref={this.setCanvasRef} + width={width} + height={height} + /> + </> ) : ( <ZoomableImage alt={alt} diff --git a/app/javascript/flavours/glitch/styles/components/index.scss b/app/javascript/flavours/glitch/styles/components/index.scss index 7f9ed2186..b54c3f696 100644 --- a/app/javascript/flavours/glitch/styles/components/index.scss +++ b/app/javascript/flavours/glitch/styles/components/index.scss @@ -442,10 +442,14 @@ object-fit: contain; } - .loading-bar { + .loading-bar__container { position: relative; } + .loading-bar { + position: absolute; + } + &.image-loader--amorphous .image-loader__preview-canvas { display: none; } diff --git a/app/javascript/flavours/glitch/styles/components/search.scss b/app/javascript/flavours/glitch/styles/components/search.scss index f7415368b..17a34db62 100644 --- a/app/javascript/flavours/glitch/styles/components/search.scss +++ b/app/javascript/flavours/glitch/styles/components/search.scss @@ -176,6 +176,13 @@ padding-right: 15px; margin-left: 5px; color: $secondary-text-color; + text-decoration: none; + + &__asterisk { + color: $darker-text-color; + font-size: 18px; + vertical-align: super; + } } &__sparkline { diff --git a/app/javascript/mastodon/components/hashtag.js b/app/javascript/mastodon/components/hashtag.js index 7f442d189..4e9cd3569 100644 --- a/app/javascript/mastodon/components/hashtag.js +++ b/app/javascript/mastodon/components/hashtag.js @@ -1,7 +1,7 @@ // @ts-check import React from 'react'; import { Sparklines, SparklinesCurve } from 'react-sparklines'; -import { FormattedMessage } from 'react-intl'; +import { FormattedMessage, injectIntl, defineMessages } from 'react-intl'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import Permalink from './permalink'; @@ -9,6 +9,10 @@ import ShortNumber from 'mastodon/components/short_number'; import Skeleton from 'mastodon/components/skeleton'; import classNames from 'classnames'; +const messages = defineMessages({ + totalVolume: { id: 'hashtag.total_volume', defaultMessage: 'Total volume in the last {days, plural, one {day} other {{days} days}}' }, +}); + class SilentErrorBoundary extends React.Component { static propTypes = { @@ -41,10 +45,11 @@ class SilentErrorBoundary extends React.Component { export const accountsCountRenderer = (displayNumber, pluralReady) => ( <FormattedMessage id='trends.counter_by_accounts' - defaultMessage='{count, plural, one {{counter} person} other {{counter} people}} talking' + defaultMessage='{count, plural, one {{counter} person} other {{counter} people}} in the past {days, plural, one {day} other {{days} days}}' values={{ count: pluralReady, counter: <strong>{displayNumber}</strong>, + days: 2, }} /> ); @@ -64,7 +69,7 @@ ImmutableHashtag.propTypes = { hashtag: ImmutablePropTypes.map.isRequired, }; -const Hashtag = ({ name, href, to, people, uses, history, className }) => ( +const Hashtag = injectIntl(({ name, href, to, people, uses, history, className, intl }) => ( <div className={classNames('trends__item', className)}> <div className='trends__item__name'> <Permalink href={href} to={to}> @@ -74,9 +79,10 @@ const Hashtag = ({ name, href, to, people, uses, history, className }) => ( {typeof people !== 'undefined' ? <ShortNumber value={people} renderer={accountsCountRenderer} /> : <Skeleton width={100} />} </div> - <div className='trends__item__current'> + <abbr className='trends__item__current' title={intl.formatMessage(messages.totalVolume, { days: 2 })}> {typeof uses !== 'undefined' ? <ShortNumber value={uses} /> : <Skeleton width={42} height={36} />} - </div> + <span className='trends__item__current__asterisk'>*</span> + </abbr> <div className='trends__item__sparkline'> <SilentErrorBoundary> @@ -86,7 +92,7 @@ const Hashtag = ({ name, href, to, people, uses, history, className }) => ( </SilentErrorBoundary> </div> </div> -); +)); Hashtag.propTypes = { name: PropTypes.string, diff --git a/app/javascript/mastodon/features/ui/components/image_loader.js b/app/javascript/mastodon/features/ui/components/image_loader.js index c6f16a792..dfa0efe49 100644 --- a/app/javascript/mastodon/features/ui/components/image_loader.js +++ b/app/javascript/mastodon/features/ui/components/image_loader.js @@ -1,10 +1,10 @@ -import React from 'react'; -import PropTypes from 'prop-types'; import classNames from 'classnames'; +import PropTypes from 'prop-types'; +import React, { PureComponent } from 'react'; import { LoadingBar } from 'react-redux-loading-bar'; import ZoomableImage from './zoomable_image'; -export default class ImageLoader extends React.PureComponent { +export default class ImageLoader extends PureComponent { static propTypes = { alt: PropTypes.string, @@ -43,7 +43,7 @@ export default class ImageLoader extends React.PureComponent { this.loadImage(this.props); } - componentWillReceiveProps (nextProps) { + UNSAFE_componentWillReceiveProps (nextProps) { if (this.props.src !== nextProps.src) { this.loadImage(nextProps); } @@ -139,14 +139,18 @@ export default class ImageLoader extends React.PureComponent { return ( <div className={className}> - <LoadingBar loading={loading ? 1 : 0} className='loading-bar' style={{ width: this.state.width || width }} /> {loading ? ( - <canvas - className='image-loader__preview-canvas' - ref={this.setCanvasRef} - width={width} - height={height} - /> + <> + <div className='loading-bar__container' style={{ width: this.state.width || width }}> + <LoadingBar className='loading-bar' loading={1} /> + </div> + <canvas + className='image-loader__preview-canvas' + ref={this.setCanvasRef} + width={width} + height={height} + /> + </> ) : ( <ZoomableImage alt={alt} diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json index 9027c98bb..8c287f892 100644 --- a/app/javascript/mastodon/locales/defaultMessages.json +++ b/app/javascript/mastodon/locales/defaultMessages.json @@ -290,7 +290,11 @@ { "descriptors": [ { - "defaultMessage": "{count, plural, one {{counter} person} other {{counter} people}} talking", + "defaultMessage": "Total volume in the last {days, plural, one {day} other {{days} days}}", + "id": "hashtag.total_volume" + }, + { + "defaultMessage": "{count, plural, one {{counter} person} other {{counter} people}} in the past {days, plural, one {day} other {{days} days}}", "id": "trends.counter_by_accounts" } ], @@ -3765,4 +3769,4 @@ ], "path": "app/javascript/mastodon/features/video/index.json" } -] \ No newline at end of file +] diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index e7cbc7e01..0dfb50adc 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -224,6 +224,7 @@ "hashtag.column_settings.tag_mode.any": "Any of these", "hashtag.column_settings.tag_mode.none": "None of these", "hashtag.column_settings.tag_toggle": "Include additional tags for this column", + "hashtag.total_volume": "Total volume in the last {days, plural, one {day} other {{days} days}}", "home.column_settings.basic": "Basic", "home.column_settings.show_reblogs": "Show boosts", "home.column_settings.show_replies": "Show replies", @@ -522,7 +523,7 @@ "timeline_hint.resources.followers": "Followers", "timeline_hint.resources.follows": "Follows", "timeline_hint.resources.statuses": "Older posts", - "trends.counter_by_accounts": "{count, plural, one {{counter} person} other {{counter} people}} talking", + "trends.counter_by_accounts": "{count, plural, one {{counter} person} other {{counter} people}} in the past {days, plural, one {day} other {{days} days}}", "trends.trending_now": "Trending now", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "units.short.billion": "{count}B", diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json index 74bbf0446..3bcae61b8 100644 --- a/app/javascript/mastodon/locales/th.json +++ b/app/javascript/mastodon/locales/th.json @@ -198,7 +198,7 @@ "explore.trending_tags": "แฮชแท็ก", "follow_recommendations.done": "เสร็จสิ้น", "follow_recommendations.heading": "ติดตามผู้คนที่คุณต้องการเห็นโพสต์! นี่คือข้อเสนอแนะบางส่วน", - "follow_recommendations.lead": "Posts from people you follow will show up in chronological order on your home feed. Don't be afraid to make mistakes, you can unfollow people just as easily any time!", + "follow_recommendations.lead": "โพสต์จากคนที่คุณติดตามจะแสดงตามลำดับเวลาบนฟีดหลักของคุณ อย่ากลัวที่จะทำผิดพลาด คุณสามารถเลิกติดตามผู้คนได้ง่ายๆ ทุกเมื่อ!", "follow_request.authorize": "อนุญาต", "follow_request.reject": "ปฏิเสธ", "follow_requests.unlocked_explanation": "แม้ว่าไม่มีการล็อคบัญชีของคุณ พนักงานของ {domain} คิดว่าคุณอาจต้องการตรวจทานคำขอติดตามจากบัญชีเหล่านี้ด้วยตนเอง", @@ -268,7 +268,7 @@ "lightbox.next": "ถัดไป", "lightbox.previous": "ก่อนหน้า", "limited_account_hint.action": "แสดงโปรไฟล์ต่อไป", - "limited_account_hint.title": "This profile has been hidden by the moderators of your server.", + "limited_account_hint.title": "โปรไฟล์นี้ถูกซ่อนไว้โดยโมเดอเรเตอร์ของเซิร์ฟเวอร์ของคุณ", "lists.account.add": "เพิ่มไปยังรายการ", "lists.account.remove": "เอาออกจากรายการ", "lists.delete": "ลบรายการ", @@ -360,7 +360,7 @@ "notifications.permission_denied_alert": "ไม่สามารถเปิดใช้งานการแจ้งเตือนบนเดสก์ท็อป เนื่องจากมีการปฏิเสธสิทธิอนุญาตเบราว์เซอร์ก่อนหน้านี้", "notifications.permission_required": "การแจ้งเตือนบนเดสก์ท็อปไม่พร้อมใช้งานเนื่องจากไม่ได้ให้สิทธิอนุญาตที่จำเป็น", "notifications_permission_banner.enable": "เปิดใช้งานการแจ้งเตือนบนเดสก์ท็อป", - "notifications_permission_banner.how_to_control": "To receive notifications when Mastodon isn't open, enable desktop notifications. You can control precisely which types of interactions generate desktop notifications through the {icon} button above once they're enabled.", + "notifications_permission_banner.how_to_control": "หากต้องการรับการแจ้งเตือนเมื่อไม่ได้เปิด Mastodon ให้เปิดใช้การแจ้งเตือนบนเดสก์ท็อป คุณสามารถควบคุมได้ตามความต้องการด้วยการโต้ตอบประเภทที่สร้างการแจ้งเตือนบนเดสก์ท็อปผ่านปุ่ม {icon} ด้านบนเมื่อเปิดใช้งาน", "notifications_permission_banner.title": "ไม่พลาดสิ่งใด", "picture_in_picture.restore": "นำกลับมา", "poll.closed": "ปิดแล้ว", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index e92227f72..dcb54735f 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -314,7 +314,7 @@ "navigation_bar.preferences": "首选项", "navigation_bar.public_timeline": "跨站公共时间轴", "navigation_bar.security": "安全", - "notification.admin.report": "{name} reported {target}", + "notification.admin.report": "{name} 已报告 {target}", "notification.admin.sign_up": "{name} 注册了", "notification.favourite": "{name} 喜欢了你的嘟文", "notification.follow": "{name} 开始关注你", @@ -327,7 +327,7 @@ "notification.update": "{name} 编辑了嘟文", "notifications.clear": "清空通知列表", "notifications.clear_confirmation": "你确定要永久清空通知列表吗?", - "notifications.column_settings.admin.report": "New reports:", + "notifications.column_settings.admin.report": "新报告", "notifications.column_settings.admin.sign_up": "新注册:", "notifications.column_settings.alert": "桌面通知", "notifications.column_settings.favourite": "喜欢:", @@ -433,7 +433,7 @@ "report.thanks.title_actionable": "感谢提交举报,我们将会进行处理。", "report.unfollow": "取消关注 @{name}", "report.unfollow_explanation": "你正在关注此账户。如果要想在你的主页上不再看到他们的帖子,取消对他们的关注即可。", - "report_notification.attached_statuses": "{count, plural, one {{count} post} other {{count} posts}} attached", + "report_notification.attached_statuses": "{count, plural, one {{count} 嘟文} other {{count} 嘟文}} 附件", "report_notification.categories.other": "其他", "report_notification.categories.spam": "骚扰", "report_notification.categories.violation": "违反规则", @@ -468,7 +468,7 @@ "status.embed": "嵌入", "status.favourite": "喜欢", "status.filtered": "已过滤", - "status.hide": "Hide toot", + "status.hide": "屏蔽嘟文", "status.history.created": "{name} 创建于 {date}", "status.history.edited": "{name} 编辑于 {date}", "status.load_more": "加载更多", @@ -492,7 +492,7 @@ "status.report": "举报 @{name}", "status.sensitive_warning": "敏感内容", "status.share": "分享", - "status.show_filter_reason": "Show anyway", + "status.show_filter_reason": "继续显示", "status.show_less": "隐藏内容", "status.show_less_all": "隐藏全部内容", "status.show_more": "显示更多", diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 26f4c54a3..e9e9a2faa 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1783,10 +1783,14 @@ a.account__display-name { object-fit: contain; } - .loading-bar { + .loading-bar__container { position: relative; } + .loading-bar { + position: absolute; + } + &.image-loader--amorphous .image-loader__preview-canvas { display: none; } @@ -7239,6 +7243,13 @@ noscript { padding-right: 15px; margin-left: 5px; color: $secondary-text-color; + text-decoration: none; + + &__asterisk { + color: $darker-text-color; + font-size: 18px; + vertical-align: super; + } } &__sparkline { diff --git a/app/models/account.rb b/app/models/account.rb index ee8caebcc..9627cc608 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -62,7 +62,7 @@ class Account < ApplicationRecord ) USERNAME_RE = /[a-z0-9_]+([a-z0-9_\.-]+[a-z0-9_]+)?/i - MENTION_RE = /(?<=^|[^\/[:word:]])@((#{USERNAME_RE})(?:@[[:alnum:]\.\-]+[[:alnum:]]+)?)/i + MENTION_RE = /(?<=^|[^\/[:word:]])@((#{USERNAME_RE})(?:@[[:word:]\.\-]+[[:word:]]+)?)/i URL_PREFIX_RE = /\Ahttp(s?):\/\/[^\/]+/ include Attachmentable diff --git a/app/models/tag.rb b/app/models/tag.rb index eebf3b47d..8929baf66 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -27,7 +27,7 @@ class Tag < ApplicationRecord has_many :followers, through: :passive_relationships, source: :account HASHTAG_SEPARATORS = "_\u00B7\u200c" - HASHTAG_NAME_RE = "([[:alnum:]_][[:alnum:]#{HASHTAG_SEPARATORS}]*[[:alpha:]#{HASHTAG_SEPARATORS}][[:alnum:]#{HASHTAG_SEPARATORS}]*[[:alnum:]_])|([[:alnum:]_]*[[:alpha:]][[:alnum:]_]*)" + HASHTAG_NAME_RE = "([[:word:]_][[:word:]#{HASHTAG_SEPARATORS}]*[[:alpha:]#{HASHTAG_SEPARATORS}][[:word:]#{HASHTAG_SEPARATORS}]*[[:word:]_])|([[:word:]_]*[[:alpha:]][[:word:]_]*)" HASHTAG_RE = /(?:^|[^\/\)\w])#(#{HASHTAG_NAME_RE})/i validates :name, presence: true, format: { with: /\A(#{HASHTAG_NAME_RE})\z/i } |