Merge commit '1483a3ddfe74e4fb81d87447a1781943eab86c60' into glitch-soc/merge-upstream
Conflicts: - `config/initializers/simple_form.rb`: Upstream added a new simple_form component, where we had an extra one. Kept both components.
This commit is contained in:
@@ -42,6 +42,6 @@ class Api::V1::ListsController < Api::BaseController
|
||||
end
|
||||
|
||||
def list_params
|
||||
params.permit(:title, :replies_policy)
|
||||
params.permit(:title, :replies_policy, :exclusive)
|
||||
end
|
||||
end
|
||||
|
@@ -11,15 +11,15 @@ class BackupsController < ApplicationController
|
||||
def download
|
||||
case Paperclip::Attachment.default_options[:storage]
|
||||
when :s3
|
||||
redirect_to @backup.dump.expiring_url(10)
|
||||
redirect_to @backup.dump.expiring_url(10), allow_other_host: true
|
||||
when :fog
|
||||
if Paperclip::Attachment.default_options.dig(:fog_credentials, :openstack_temp_url_key).present?
|
||||
redirect_to @backup.dump.expiring_url(Time.now.utc + 10)
|
||||
redirect_to @backup.dump.expiring_url(Time.now.utc + 10), allow_other_host: true
|
||||
else
|
||||
redirect_to full_asset_url(@backup.dump.url)
|
||||
redirect_to full_asset_url(@backup.dump.url), allow_other_host: true
|
||||
end
|
||||
when :filesystem
|
||||
redirect_to full_asset_url(@backup.dump.url)
|
||||
redirect_to full_asset_url(@backup.dump.url), allow_other_host: true
|
||||
end
|
||||
end
|
||||
|
||||
|
@@ -138,7 +138,7 @@ export function normalizePollOptionTranslation(translation, poll) {
|
||||
|
||||
export function normalizeAnnouncement(announcement) {
|
||||
const normalAnnouncement = { ...announcement };
|
||||
const emojiMap = makeEmojiMap.emojis(normalAnnouncement);
|
||||
const emojiMap = makeEmojiMap(normalAnnouncement.emojis);
|
||||
|
||||
normalAnnouncement.contentHtml = emojify(normalAnnouncement.content, emojiMap);
|
||||
|
||||
|
@@ -151,10 +151,10 @@ export const createListFail = error => ({
|
||||
error,
|
||||
});
|
||||
|
||||
export const updateList = (id, title, shouldReset, replies_policy) => (dispatch, getState) => {
|
||||
export const updateList = (id, title, shouldReset, isExclusive, replies_policy) => (dispatch, getState) => {
|
||||
dispatch(updateListRequest(id));
|
||||
|
||||
api(getState).put(`/api/v1/lists/${id}`, { title, replies_policy }).then(({ data }) => {
|
||||
api(getState).put(`/api/v1/lists/${id}`, { title, replies_policy, exclusive: typeof isExclusive === 'undefined' ? undefined : !!isExclusive }).then(({ data }) => {
|
||||
dispatch(updateListSuccess(data));
|
||||
|
||||
if (shouldReset) {
|
||||
|
@@ -67,7 +67,7 @@ class Explore extends PureComponent {
|
||||
<Search />
|
||||
</div>
|
||||
|
||||
<div className='scrollable scrollable--flex'>
|
||||
<div className='scrollable scrollable--flex' data-nosnippet>
|
||||
{isSearching ? (
|
||||
<SearchResults />
|
||||
) : (
|
||||
|
@@ -8,6 +8,8 @@ import { Helmet } from 'react-helmet';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import Toggle from 'react-toggle';
|
||||
|
||||
import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns';
|
||||
import { fetchList, deleteList, updateList } from 'mastodon/actions/lists';
|
||||
import { openModal } from 'mastodon/actions/modal';
|
||||
@@ -145,7 +147,13 @@ class ListTimeline extends PureComponent {
|
||||
handleRepliesPolicyChange = ({ target }) => {
|
||||
const { dispatch } = this.props;
|
||||
const { id } = this.props.params;
|
||||
dispatch(updateList(id, undefined, false, target.value));
|
||||
dispatch(updateList(id, undefined, false, undefined, target.value));
|
||||
};
|
||||
|
||||
onExclusiveToggle = ({ target }) => {
|
||||
const { dispatch } = this.props;
|
||||
const { id } = this.props.params;
|
||||
dispatch(updateList(id, undefined, false, target.checked, undefined));
|
||||
};
|
||||
|
||||
render () {
|
||||
@@ -154,6 +162,7 @@ class ListTimeline extends PureComponent {
|
||||
const pinned = !!columnId;
|
||||
const title = list ? list.get('title') : id;
|
||||
const replies_policy = list ? list.get('replies_policy') : undefined;
|
||||
const isExclusive = list ? list.get('exclusive') : undefined;
|
||||
|
||||
if (typeof list === 'undefined') {
|
||||
return (
|
||||
@@ -191,6 +200,13 @@ class ListTimeline extends PureComponent {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className='setting-toggle'>
|
||||
<Toggle id={`list-${id}-exclusive`} defaultChecked={isExclusive} onChange={this.onExclusiveToggle} />
|
||||
<label htmlFor={`list-${id}-exclusive`} className='setting-toggle__label'>
|
||||
<FormattedMessage id='lists.exclusive' defaultMessage='Hide these posts from home' />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{ replies_policy !== undefined && (
|
||||
<div role='group' aria-labelledby={`list-${id}-replies-policy`}>
|
||||
<span id={`list-${id}-replies-policy`} className='column-settings__section'>
|
||||
|
@@ -121,7 +121,7 @@ class Onboarding extends ImmutablePureComponent {
|
||||
|
||||
<div className='onboarding__steps'>
|
||||
<Step onClick={this.handleProfileClick} href='/settings/profile' completed={(!account.get('avatar').endsWith('missing.png')) || (account.get('display_name').length > 0 && account.get('note').length > 0)} icon='address-book-o' label={<FormattedMessage id='onboarding.steps.setup_profile.title' defaultMessage='Customize your profile' />} description={<FormattedMessage id='onboarding.steps.setup_profile.body' defaultMessage='Others are more likely to interact with you with a filled out profile.' />} />
|
||||
<Step onClick={this.handleFollowClick} completed={(account.get('following_count') * 1) >= 7} icon='user-plus' label={<FormattedMessage id='onboarding.steps.follow_people.title' defaultMessage='Follow {count, plural, one {one person} other {# people}}' values={{ count: 7 }} />} description={<FormattedMessage id='onboarding.steps.follow_people.body' defaultMessage="You curate your own feed. Let's fill it with interesting people." />} />
|
||||
<Step onClick={this.handleFollowClick} completed={(account.get('following_count') * 1) >= 7} icon='user-plus' label={<FormattedMessage id='onboarding.steps.follow_people.title' defaultMessage='Find at least {count, plural, one {one person} other {# people}} to follow' values={{ count: 7 }} />} description={<FormattedMessage id='onboarding.steps.follow_people.body' defaultMessage="You curate your own home feed. Let's fill it with interesting people." />} />
|
||||
<Step onClick={this.handleComposeClick} completed={(account.get('statuses_count') * 1) >= 1} icon='pencil-square-o' label={<FormattedMessage id='onboarding.steps.publish_status.title' defaultMessage='Make your first post' />} description={<FormattedMessage id='onboarding.steps.publish_status.body' defaultMessage='Say hello to the world.' />} />
|
||||
<Step onClick={this.handleShareClick} completed={shareClicked} icon='copy' label={<FormattedMessage id='onboarding.steps.share_profile.title' defaultMessage='Share your profile' />} description={<FormattedMessage id='onboarding.steps.share_profile.body' defaultMessage='Let your friends know how to find you on Mastodon!' />} />
|
||||
</div>
|
||||
|
@@ -217,7 +217,7 @@ class DetailedStatus extends ImmutablePureComponent {
|
||||
} else if (this.context.router) {
|
||||
reblogLink = (
|
||||
<>
|
||||
·
|
||||
{' · '}
|
||||
<Link to={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}/reblogs`} className='detailed-status__link'>
|
||||
<Icon id={reblogIcon} />
|
||||
<span className='detailed-status__reblogs'>
|
||||
@@ -229,7 +229,7 @@ class DetailedStatus extends ImmutablePureComponent {
|
||||
} else {
|
||||
reblogLink = (
|
||||
<>
|
||||
·
|
||||
{' · '}
|
||||
<a href={`/interact/${status.get('id')}?type=reblog`} className='detailed-status__link' onClick={this.handleModalLink}>
|
||||
<Icon id={reblogIcon} />
|
||||
<span className='detailed-status__reblogs'>
|
||||
@@ -263,7 +263,7 @@ class DetailedStatus extends ImmutablePureComponent {
|
||||
if (status.get('edited_at')) {
|
||||
edited = (
|
||||
<>
|
||||
·
|
||||
{' · '}
|
||||
<EditedTimestamp statusId={status.get('id')} timestamp={status.get('edited_at')} />
|
||||
</>
|
||||
);
|
||||
|
@@ -356,6 +356,7 @@
|
||||
"lists.delete": "Delete list",
|
||||
"lists.edit": "Edit list",
|
||||
"lists.edit.submit": "Change title",
|
||||
"lists.exclusive": "Hide these posts from home",
|
||||
"lists.new.create": "Add list",
|
||||
"lists.new.title_placeholder": "New list title",
|
||||
"lists.replies_policy.followed": "Any followed user",
|
||||
@@ -460,8 +461,8 @@
|
||||
"onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:",
|
||||
"onboarding.start.skip": "Want to skip right ahead?",
|
||||
"onboarding.start.title": "You've made it!",
|
||||
"onboarding.steps.follow_people.body": "You curate your own feed. Lets fill it with interesting people.",
|
||||
"onboarding.steps.follow_people.title": "Follow {count, plural, one {one person} other {# people}}",
|
||||
"onboarding.steps.follow_people.body": "You curate your own home feed. Let's fill it with interesting people.",
|
||||
"onboarding.steps.follow_people.title": "Find at least {count, plural, one {one person} other {# people}} to follow",
|
||||
"onboarding.steps.publish_status.body": "Say hello to the world.",
|
||||
"onboarding.steps.publish_status.title": "Make your first post",
|
||||
"onboarding.steps.setup_profile.body": "Others are more likely to interact with you with a filled out profile.",
|
||||
|
@@ -48,6 +48,7 @@ export const IntlProvider: React.FC<
|
||||
locale={locale}
|
||||
messages={messages}
|
||||
onError={onProviderError}
|
||||
textComponent='span'
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
|
@@ -25,6 +25,7 @@ const initialState = ImmutableMap({
|
||||
isSubmitting: false,
|
||||
isChanged: false,
|
||||
title: '',
|
||||
isExclusive: false,
|
||||
|
||||
accounts: ImmutableMap({
|
||||
items: ImmutableList(),
|
||||
@@ -46,6 +47,7 @@ export default function listEditorReducer(state = initialState, action) {
|
||||
return state.withMutations(map => {
|
||||
map.set('listId', action.list.get('id'));
|
||||
map.set('title', action.list.get('title'));
|
||||
map.set('isExclusive', action.list.get('is_exclusive'));
|
||||
map.set('isSubmitting', false);
|
||||
});
|
||||
case LIST_EDITOR_TITLE_CHANGE:
|
||||
|
@@ -3,11 +3,8 @@
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
box-shadow: none;
|
||||
}
|
||||
overflow: hidden;
|
||||
border-radius: 4px;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
@@ -22,7 +19,6 @@
|
||||
height: 130px;
|
||||
position: relative;
|
||||
background: darken($ui-base-color, 12%);
|
||||
border-radius: 4px 4px 0 0;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
@@ -30,7 +26,6 @@
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
object-fit: cover;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
||||
@media screen and (width <= 600px) {
|
||||
@@ -45,11 +40,6 @@
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
background: lighten($ui-base-color, 4%);
|
||||
border-radius: 0 0 4px 4px;
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
flex: 0 0 auto;
|
||||
|
@@ -137,6 +137,10 @@ code {
|
||||
color: $secondary-text-color;
|
||||
margin-bottom: 30px;
|
||||
|
||||
&.invited-by {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $highlight-text-color;
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::Metrics::Dimension::InstanceAccountsDimension < Admin::Metrics::Dimension::BaseDimension
|
||||
include Admin::Metrics::Dimension::QueryHelper
|
||||
include LanguagesHelper
|
||||
|
||||
def self.with_params?
|
||||
@@ -14,19 +15,23 @@ class Admin::Metrics::Dimension::InstanceAccountsDimension < Admin::Metrics::Dim
|
||||
protected
|
||||
|
||||
def perform_query
|
||||
sql = <<-SQL.squish
|
||||
dimension_data_rows.map { |row| { key: row['username'], human_key: row['username'], value: row['value'].to_s } }
|
||||
end
|
||||
|
||||
def sql_array
|
||||
[sql_query_string, { domain: params[:domain], limit: @limit }]
|
||||
end
|
||||
|
||||
def sql_query_string
|
||||
<<~SQL.squish
|
||||
SELECT accounts.username, count(follows.*) AS value
|
||||
FROM accounts
|
||||
LEFT JOIN follows ON follows.target_account_id = accounts.id
|
||||
WHERE accounts.domain = $1
|
||||
WHERE accounts.domain = :domain
|
||||
GROUP BY accounts.id, follows.target_account_id
|
||||
ORDER BY value DESC
|
||||
LIMIT $2
|
||||
LIMIT :limit
|
||||
SQL
|
||||
|
||||
rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, params[:domain]], [nil, @limit]])
|
||||
|
||||
rows.map { |row| { key: row['username'], human_key: row['username'], value: row['value'].to_s } }
|
||||
end
|
||||
|
||||
def params
|
||||
|
@@ -1,6 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::Metrics::Dimension::InstanceLanguagesDimension < Admin::Metrics::Dimension::BaseDimension
|
||||
include Admin::Metrics::Dimension::QueryHelper
|
||||
include LanguagesHelper
|
||||
|
||||
def self.with_params?
|
||||
@@ -14,21 +15,33 @@ class Admin::Metrics::Dimension::InstanceLanguagesDimension < Admin::Metrics::Di
|
||||
protected
|
||||
|
||||
def perform_query
|
||||
sql = <<-SQL.squish
|
||||
dimension_data_rows.map { |row| { key: row['language'], human_key: standard_locale_name(row['language']), value: row['value'].to_s } }
|
||||
end
|
||||
|
||||
def sql_array
|
||||
[sql_query_string, { domain: params[:domain], earliest_status_id: earliest_status_id, latest_status_id: latest_status_id, limit: @limit }]
|
||||
end
|
||||
|
||||
def sql_query_string
|
||||
<<~SQL.squish
|
||||
SELECT COALESCE(statuses.language, 'und') AS language, count(*) AS value
|
||||
FROM statuses
|
||||
INNER JOIN accounts ON accounts.id = statuses.account_id
|
||||
WHERE accounts.domain = $1
|
||||
AND statuses.id BETWEEN $2 AND $3
|
||||
WHERE accounts.domain = :domain
|
||||
AND statuses.id BETWEEN :earliest_status_id AND :latest_status_id
|
||||
AND statuses.reblog_of_id IS NULL
|
||||
GROUP BY COALESCE(statuses.language, 'und')
|
||||
ORDER BY count(*) DESC
|
||||
LIMIT $4
|
||||
LIMIT :limit
|
||||
SQL
|
||||
end
|
||||
|
||||
rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, params[:domain]], [nil, Mastodon::Snowflake.id_at(@start_at, with_random: false)], [nil, Mastodon::Snowflake.id_at(@end_at, with_random: false)], [nil, @limit]])
|
||||
def earliest_status_id
|
||||
Mastodon::Snowflake.id_at(@start_at, with_random: false)
|
||||
end
|
||||
|
||||
rows.map { |row| { key: row['language'], human_key: standard_locale_name(row['language']), value: row['value'].to_s } }
|
||||
def latest_status_id
|
||||
Mastodon::Snowflake.id_at(@end_at, with_random: false)
|
||||
end
|
||||
|
||||
def params
|
||||
|
@@ -1,6 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::Metrics::Dimension::LanguagesDimension < Admin::Metrics::Dimension::BaseDimension
|
||||
include Admin::Metrics::Dimension::QueryHelper
|
||||
include LanguagesHelper
|
||||
|
||||
def key
|
||||
@@ -10,18 +11,22 @@ class Admin::Metrics::Dimension::LanguagesDimension < Admin::Metrics::Dimension:
|
||||
protected
|
||||
|
||||
def perform_query
|
||||
sql = <<-SQL.squish
|
||||
dimension_data_rows.map { |row| { key: row['locale'], human_key: standard_locale_name(row['locale']), value: row['value'].to_s } }
|
||||
end
|
||||
|
||||
def sql_array
|
||||
[sql_query_string, { start_at: @start_at, end_at: @end_at, limit: @limit }]
|
||||
end
|
||||
|
||||
def sql_query_string
|
||||
<<~SQL.squish
|
||||
SELECT locale, count(*) AS value
|
||||
FROM users
|
||||
WHERE current_sign_in_at BETWEEN $1 AND $2
|
||||
WHERE current_sign_in_at BETWEEN :start_at AND :end_at
|
||||
AND locale IS NOT NULL
|
||||
GROUP BY locale
|
||||
ORDER BY count(*) DESC
|
||||
LIMIT $3
|
||||
LIMIT :limit
|
||||
SQL
|
||||
|
||||
rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, @start_at], [nil, @end_at], [nil, @limit]])
|
||||
|
||||
rows.map { |row| { key: row['locale'], human_key: standard_locale_name(row['locale']), value: row['value'].to_s } }
|
||||
end
|
||||
end
|
||||
|
13
app/lib/admin/metrics/dimension/query_helper.rb
Normal file
13
app/lib/admin/metrics/dimension/query_helper.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin::Metrics::Dimension::QueryHelper
|
||||
protected
|
||||
|
||||
def dimension_data_rows
|
||||
ActiveRecord::Base.connection.select_all(sanitized_sql_string)
|
||||
end
|
||||
|
||||
def sanitized_sql_string
|
||||
ActiveRecord::Base.sanitize_sql_array(sql_array)
|
||||
end
|
||||
end
|
@@ -1,6 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::Metrics::Dimension::ServersDimension < Admin::Metrics::Dimension::BaseDimension
|
||||
include Admin::Metrics::Dimension::QueryHelper
|
||||
|
||||
def key
|
||||
'servers'
|
||||
end
|
||||
@@ -8,18 +10,30 @@ class Admin::Metrics::Dimension::ServersDimension < Admin::Metrics::Dimension::B
|
||||
protected
|
||||
|
||||
def perform_query
|
||||
sql = <<-SQL.squish
|
||||
dimension_data_rows.map { |row| { key: row['domain'] || Rails.configuration.x.local_domain, human_key: row['domain'] || Rails.configuration.x.local_domain, value: row['value'].to_s } }
|
||||
end
|
||||
|
||||
def sql_array
|
||||
[sql_query_string, { earliest_status_id: earliest_status_id, latest_status_id: latest_status_id, limit: @limit }]
|
||||
end
|
||||
|
||||
def sql_query_string
|
||||
<<~SQL.squish
|
||||
SELECT accounts.domain, count(*) AS value
|
||||
FROM statuses
|
||||
INNER JOIN accounts ON accounts.id = statuses.account_id
|
||||
WHERE statuses.id BETWEEN $1 AND $2
|
||||
WHERE statuses.id BETWEEN :earliest_status_id AND :latest_status_id
|
||||
GROUP BY accounts.domain
|
||||
ORDER BY count(*) DESC
|
||||
LIMIT $3
|
||||
LIMIT :limit
|
||||
SQL
|
||||
end
|
||||
|
||||
rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, Mastodon::Snowflake.id_at(@start_at)], [nil, Mastodon::Snowflake.id_at(@end_at)], [nil, @limit]])
|
||||
def earliest_status_id
|
||||
Mastodon::Snowflake.id_at(@start_at)
|
||||
end
|
||||
|
||||
rows.map { |row| { key: row['domain'] || Rails.configuration.x.local_domain, human_key: row['domain'] || Rails.configuration.x.local_domain, value: row['value'].to_s } }
|
||||
def latest_status_id
|
||||
Mastodon::Snowflake.id_at(@end_at)
|
||||
end
|
||||
end
|
||||
|
@@ -1,6 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::Metrics::Dimension::SourcesDimension < Admin::Metrics::Dimension::BaseDimension
|
||||
include Admin::Metrics::Dimension::QueryHelper
|
||||
|
||||
def key
|
||||
'sources'
|
||||
end
|
||||
@@ -8,18 +10,22 @@ class Admin::Metrics::Dimension::SourcesDimension < Admin::Metrics::Dimension::B
|
||||
protected
|
||||
|
||||
def perform_query
|
||||
sql = <<-SQL.squish
|
||||
dimension_data_rows.map { |row| { key: row['name'] || 'web', human_key: row['name'] || I18n.t('admin.dashboard.website'), value: row['value'].to_s } }
|
||||
end
|
||||
|
||||
def sql_array
|
||||
[sql_query_string, { start_at: @start_at, end_at: @end_at, limit: @limit }]
|
||||
end
|
||||
|
||||
def sql_query_string
|
||||
<<~SQL.squish
|
||||
SELECT oauth_applications.name, count(*) AS value
|
||||
FROM users
|
||||
LEFT JOIN oauth_applications ON oauth_applications.id = users.created_by_application_id
|
||||
WHERE users.created_at BETWEEN $1 AND $2
|
||||
WHERE users.created_at BETWEEN :start_at AND :end_at
|
||||
GROUP BY oauth_applications.name
|
||||
ORDER BY count(*) DESC
|
||||
LIMIT $3
|
||||
LIMIT :limit
|
||||
SQL
|
||||
|
||||
rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, @start_at], [nil, @end_at], [nil, @limit]])
|
||||
|
||||
rows.map { |row| { key: row['name'] || 'web', human_key: row['name'] || I18n.t('admin.dashboard.website'), value: row['value'].to_s } }
|
||||
end
|
||||
end
|
||||
|
@@ -1,6 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::Metrics::Dimension::TagLanguagesDimension < Admin::Metrics::Dimension::BaseDimension
|
||||
include Admin::Metrics::Dimension::QueryHelper
|
||||
include LanguagesHelper
|
||||
|
||||
def self.with_params?
|
||||
@@ -14,20 +15,36 @@ class Admin::Metrics::Dimension::TagLanguagesDimension < Admin::Metrics::Dimensi
|
||||
protected
|
||||
|
||||
def perform_query
|
||||
sql = <<-SQL.squish
|
||||
dimension_data_rows.map { |row| { key: row['language'], human_key: standard_locale_name(row['language']), value: row['value'].to_s } }
|
||||
end
|
||||
|
||||
def sql_array
|
||||
[sql_query_string, { tag_id: tag_id, earliest_status_id: earliest_status_id, latest_status_id: latest_status_id, limit: @limit }]
|
||||
end
|
||||
|
||||
def sql_query_string
|
||||
<<~SQL.squish
|
||||
SELECT COALESCE(statuses.language, 'und') AS language, count(*) AS value
|
||||
FROM statuses
|
||||
INNER JOIN statuses_tags ON statuses_tags.status_id = statuses.id
|
||||
WHERE statuses_tags.tag_id = $1
|
||||
AND statuses.id BETWEEN $2 AND $3
|
||||
WHERE statuses_tags.tag_id = :tag_id
|
||||
AND statuses.id BETWEEN :earliest_status_id AND :latest_status_id
|
||||
GROUP BY COALESCE(statuses.language, 'und')
|
||||
ORDER BY count(*) DESC
|
||||
LIMIT $4
|
||||
LIMIT :limit
|
||||
SQL
|
||||
end
|
||||
|
||||
rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, params[:id]], [nil, Mastodon::Snowflake.id_at(@start_at, with_random: false)], [nil, Mastodon::Snowflake.id_at(@end_at, with_random: false)], [nil, @limit]])
|
||||
def tag_id
|
||||
params[:id]
|
||||
end
|
||||
|
||||
rows.map { |row| { key: row['language'], human_key: standard_locale_name(row['language']), value: row['value'].to_s } }
|
||||
def earliest_status_id
|
||||
Mastodon::Snowflake.id_at(@start_at, with_random: false)
|
||||
end
|
||||
|
||||
def latest_status_id
|
||||
Mastodon::Snowflake.id_at(@end_at, with_random: false)
|
||||
end
|
||||
|
||||
def params
|
||||
|
@@ -1,6 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::Metrics::Dimension::TagServersDimension < Admin::Metrics::Dimension::BaseDimension
|
||||
include Admin::Metrics::Dimension::QueryHelper
|
||||
|
||||
def self.with_params?
|
||||
true
|
||||
end
|
||||
@@ -12,21 +14,37 @@ class Admin::Metrics::Dimension::TagServersDimension < Admin::Metrics::Dimension
|
||||
protected
|
||||
|
||||
def perform_query
|
||||
sql = <<-SQL.squish
|
||||
dimension_data_rows.map { |row| { key: row['domain'] || Rails.configuration.x.local_domain, human_key: row['domain'] || Rails.configuration.x.local_domain, value: row['value'].to_s } }
|
||||
end
|
||||
|
||||
def sql_array
|
||||
[sql_query_string, { tag_id: tag_id, earliest_status_id: earliest_status_id, latest_status_id: latest_status_id, limit: @limit }]
|
||||
end
|
||||
|
||||
def sql_query_string
|
||||
<<-SQL.squish
|
||||
SELECT accounts.domain, count(*) AS value
|
||||
FROM statuses
|
||||
INNER JOIN accounts ON accounts.id = statuses.account_id
|
||||
INNER JOIN statuses_tags ON statuses_tags.status_id = statuses.id
|
||||
WHERE statuses_tags.tag_id = $1
|
||||
AND statuses.id BETWEEN $2 AND $3
|
||||
WHERE statuses_tags.tag_id = :tag_id
|
||||
AND statuses.id BETWEEN :earliest_status_id AND :latest_status_id
|
||||
GROUP BY accounts.domain
|
||||
ORDER BY count(*) DESC
|
||||
LIMIT $4
|
||||
LIMIT :limit
|
||||
SQL
|
||||
end
|
||||
|
||||
rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, params[:id]], [nil, Mastodon::Snowflake.id_at(@start_at, with_random: false)], [nil, Mastodon::Snowflake.id_at(@end_at, with_random: false)], [nil, @limit]])
|
||||
def tag_id
|
||||
params[:id]
|
||||
end
|
||||
|
||||
rows.map { |row| { key: row['domain'] || Rails.configuration.x.local_domain, human_key: row['domain'] || Rails.configuration.x.local_domain, value: row['value'].to_s } }
|
||||
def earliest_status_id
|
||||
Mastodon::Snowflake.id_at(@start_at, with_random: false)
|
||||
end
|
||||
|
||||
def latest_status_id
|
||||
Mastodon::Snowflake.id_at(@end_at, with_random: false)
|
||||
end
|
||||
|
||||
def params
|
||||
|
@@ -40,9 +40,9 @@ class FeedManager
|
||||
def filter?(timeline_type, status, receiver)
|
||||
case timeline_type
|
||||
when :home
|
||||
filter_from_home?(status, receiver.id, build_crutches(receiver.id, [status]))
|
||||
filter_from_home?(status, receiver.id, build_crutches(receiver.id, [status]), :home)
|
||||
when :list
|
||||
filter_from_list?(status, receiver) || filter_from_home?(status, receiver.account_id, build_crutches(receiver.account_id, [status]))
|
||||
filter_from_list?(status, receiver) || filter_from_home?(status, receiver.account_id, build_crutches(receiver.account_id, [status]), :list)
|
||||
when :mentions
|
||||
filter_from_mentions?(status, receiver.id)
|
||||
when :direct
|
||||
@@ -401,10 +401,11 @@ class FeedManager
|
||||
# @param [Integer] receiver_id
|
||||
# @param [Hash] crutches
|
||||
# @return [Boolean]
|
||||
def filter_from_home?(status, receiver_id, crutches)
|
||||
def filter_from_home?(status, receiver_id, crutches, timeline_type = :home)
|
||||
return false if receiver_id == status.account_id
|
||||
return true if status.reply? && (status.in_reply_to_id.nil? || status.in_reply_to_account_id.nil?)
|
||||
return true if crutches[:languages][status.account_id].present? && status.language.present? && !crutches[:languages][status.account_id].include?(status.language)
|
||||
return true if timeline_type != :list && crutches[:exclusive_list_users][status.account_id].present?
|
||||
return true if crutches[:languages][status.account_id].present? && status.language.present? && !crutches[:languages][status.account_id].include?(status.language)
|
||||
|
||||
check_for_blocks = crutches[:active_mentions][status.id] || []
|
||||
check_for_blocks.push(status.account_id)
|
||||
@@ -603,13 +604,16 @@ class FeedManager
|
||||
arr
|
||||
end
|
||||
|
||||
crutches[:following] = Follow.where(account_id: receiver_id, target_account_id: statuses.filter_map(&:in_reply_to_account_id)).pluck(:target_account_id).index_with(true)
|
||||
crutches[:languages] = Follow.where(account_id: receiver_id, target_account_id: statuses.map(&:account_id)).pluck(:target_account_id, :languages).to_h
|
||||
crutches[:hiding_reblogs] = Follow.where(account_id: receiver_id, target_account_id: statuses.filter_map { |s| s.account_id if s.reblog? }, show_reblogs: false).pluck(:target_account_id).index_with(true)
|
||||
crutches[:blocking] = Block.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).index_with(true)
|
||||
crutches[:muting] = Mute.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).index_with(true)
|
||||
crutches[:domain_blocking] = AccountDomainBlock.where(account_id: receiver_id, domain: statuses.flat_map { |s| [s.account.domain, s.reblog&.account&.domain] }.compact).pluck(:domain).index_with(true)
|
||||
crutches[:blocked_by] = Block.where(target_account_id: receiver_id, account_id: statuses.map { |s| [s.account_id, s.reblog&.account_id] }.flatten.compact).pluck(:account_id).index_with(true)
|
||||
lists = List.where(account_id: receiver_id, exclusive: true)
|
||||
|
||||
crutches[:following] = Follow.where(account_id: receiver_id, target_account_id: statuses.filter_map(&:in_reply_to_account_id)).pluck(:target_account_id).index_with(true)
|
||||
crutches[:languages] = Follow.where(account_id: receiver_id, target_account_id: statuses.map(&:account_id)).pluck(:target_account_id, :languages).to_h
|
||||
crutches[:hiding_reblogs] = Follow.where(account_id: receiver_id, target_account_id: statuses.filter_map { |s| s.account_id if s.reblog? }, show_reblogs: false).pluck(:target_account_id).index_with(true)
|
||||
crutches[:blocking] = Block.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).index_with(true)
|
||||
crutches[:muting] = Mute.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).index_with(true)
|
||||
crutches[:domain_blocking] = AccountDomainBlock.where(account_id: receiver_id, domain: statuses.flat_map { |s| [s.account.domain, s.reblog&.account&.domain] }.compact).pluck(:domain).index_with(true)
|
||||
crutches[:blocked_by] = Block.where(target_account_id: receiver_id, account_id: statuses.map { |s| [s.account_id, s.reblog&.account_id] }.flatten.compact).pluck(:account_id).index_with(true)
|
||||
crutches[:exclusive_list_users] = ListAccount.where(list: lists, account_id: statuses.map(&:account_id)).pluck(:account_id).index_with(true)
|
||||
|
||||
crutches
|
||||
end
|
||||
|
@@ -10,6 +10,7 @@
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# replies_policy :integer default("list"), not null
|
||||
# exclusive :boolean default(FALSE)
|
||||
#
|
||||
|
||||
class List < ApplicationRecord
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class REST::ListSerializer < ActiveModel::Serializer
|
||||
attributes :id, :title, :replies_policy
|
||||
attributes :id, :title, :replies_policy, :exclusive
|
||||
|
||||
def id
|
||||
object.id.to_s
|
||||
|
@@ -9,6 +9,6 @@
|
||||
|
||||
- if email_domain_block.parent.present?
|
||||
= t('admin.email_domain_blocks.resolved_through_html', domain: content_tag(:samp, email_domain_block.parent.domain))
|
||||
•
|
||||
·
|
||||
|
||||
= t('admin.email_domain_blocks.attempts_over_week', count: email_domain_block.history.reduce(0) { |sum, day| sum + day.accounts })
|
||||
|
@@ -17,11 +17,11 @@
|
||||
|
||||
%br/
|
||||
|
||||
= f.object.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' • ')
|
||||
= f.object.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' · ')
|
||||
- if f.object.public_comment.present?
|
||||
•
|
||||
·
|
||||
= f.object.public_comment
|
||||
- if existing_relationships
|
||||
•
|
||||
·
|
||||
= fa_icon 'warning fw'
|
||||
= t('admin.export_domain_blocks.import.existing_relationships_warning')
|
||||
|
@@ -6,7 +6,7 @@
|
||||
|
||||
%small
|
||||
- if instance.domain_block
|
||||
= instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' • ')
|
||||
= instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' · ')
|
||||
- elsif instance.domain_allow
|
||||
= t('admin.accounts.whitelisted')
|
||||
- else
|
||||
|
@@ -55,7 +55,7 @@
|
||||
%td= @instance.domain_block.public_comment
|
||||
%tr
|
||||
%th= t('admin.instances.content_policies.policy')
|
||||
%td= @instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' • ')
|
||||
%td= @instance.domain_block.policies.map { |policy| t(policy, scope: 'admin.instances.content_policies.policies') }.join(' · ')
|
||||
|
||||
= link_to t('admin.domain_blocks.edit'), edit_admin_domain_block_path(@instance.domain_block), class: 'button'
|
||||
= link_to t('admin.domain_blocks.undo'), admin_domain_block_path(@instance.domain_block), class: 'button', data: { confirm: t('admin.accounts.are_you_sure'), method: :delete }
|
||||
|
@@ -5,7 +5,7 @@
|
||||
.pending-account__header
|
||||
%samp= link_to "#{ip_block.ip}/#{ip_block.ip.prefix}", admin_accounts_path(ip: "#{ip_block.ip}/#{ip_block.ip.prefix}")
|
||||
- if ip_block.comment.present?
|
||||
•
|
||||
·
|
||||
= ip_block.comment
|
||||
%br/
|
||||
= t("simple_form.labels.ip_block.severities.#{ip_block.severity}")
|
||||
|
@@ -24,7 +24,7 @@
|
||||
= t('admin.roles.everyone_full_description_html')
|
||||
- else
|
||||
= link_to t('admin.roles.assigned_users', count: role.users.count), admin_accounts_path(role_ids: role.id)
|
||||
•
|
||||
·
|
||||
%abbr{ title: role.permissions_as_keys.map { |privilege| I18n.t("admin.roles.privileges.#{privilege}") }.join(', ') }= t('admin.roles.permissions_count', count: role.permissions_as_keys.size)
|
||||
%div
|
||||
= table_link_to 'pencil', t('admin.accounts.edit'), edit_admin_role_path(role) if can?(:update, role)
|
||||
|
@@ -12,7 +12,7 @@
|
||||
|
||||
.fields-group
|
||||
= f.input :media_cache_retention_period, wrapper: :with_block_label, input_html: { pattern: '[0-9]+' }
|
||||
= f.input :content_cache_retention_period, wrapper: :with_block_label, input_html: { pattern: '[0-9]+' }
|
||||
= f.input :content_cache_retention_period, wrapper: :with_block_label, input_html: { pattern: '[0-9]+' }, hint: false, warning_hint: t('simple_form.hints.form_admin_settings.content_cache_retention_period')
|
||||
= f.input :backups_retention_period, wrapper: :with_block_label, input_html: { pattern: '[0-9]+' }
|
||||
|
||||
.actions
|
||||
|
@@ -10,21 +10,21 @@
|
||||
|
||||
- if preview_card.provider_name.present?
|
||||
= preview_card.provider_name
|
||||
•
|
||||
·
|
||||
|
||||
- if preview_card.language.present?
|
||||
= standard_locale_name(preview_card.language)
|
||||
•
|
||||
·
|
||||
|
||||
= t('admin.trends.links.shared_by_over_week', count: preview_card.history.reduce(0) { |sum, day| sum + day.accounts })
|
||||
|
||||
- if preview_card.trend.allowed?
|
||||
•
|
||||
·
|
||||
%abbr{ title: t('admin.trends.tags.current_score', score: preview_card.trend.score) }= t('admin.trends.tags.trending_rank', rank: preview_card.trend.rank)
|
||||
|
||||
- if preview_card.decaying?
|
||||
•
|
||||
·
|
||||
= t('admin.trends.tags.peaked_on_and_decaying', date: l(preview_card.max_score_at.to_date, format: :short))
|
||||
- elsif preview_card.requires_review?
|
||||
•
|
||||
·
|
||||
= t('admin.trends.pending_review')
|
||||
|
@@ -17,17 +17,17 @@
|
||||
= t('admin.trends.statuses.shared_by', count: status.reblogs_count + status.favourites_count, friendly_count: friendly_number_to_human(status.reblogs_count + status.favourites_count))
|
||||
|
||||
- if status.account.domain.present?
|
||||
•
|
||||
·
|
||||
= status.account.domain
|
||||
- if status.language.present?
|
||||
•
|
||||
·
|
||||
= standard_locale_name(status.language)
|
||||
- if status.trendable? && !status.account.discoverable?
|
||||
•
|
||||
·
|
||||
= t('admin.trends.statuses.not_discoverable')
|
||||
- if status.trend.allowed?
|
||||
•
|
||||
·
|
||||
%abbr{ title: t('admin.trends.tags.current_score', score: status.trend.score) }= t('admin.trends.tags.trending_rank', rank: status.trend.rank)
|
||||
- elsif status.requires_review?
|
||||
•
|
||||
·
|
||||
= t('admin.trends.pending_review')
|
||||
|
@@ -13,12 +13,12 @@
|
||||
= t('admin.trends.tags.used_by_over_week', count: tag.history.reduce(0) { |sum, day| sum + day.accounts })
|
||||
|
||||
- if tag.trendable? && (rank = Trends.tags.rank(tag.id))
|
||||
•
|
||||
·
|
||||
%abbr{ title: t('admin.trends.tags.current_score', score: Trends.tags.score(tag.id)) }= t('admin.trends.tags.trending_rank', rank: rank + 1)
|
||||
|
||||
- if tag.decaying?
|
||||
•
|
||||
·
|
||||
= t('admin.trends.tags.peaked_on_and_decaying', date: l(tag.max_score_at.to_date, format: :short))
|
||||
- elsif tag.requires_review?
|
||||
•
|
||||
·
|
||||
= t('admin.trends.pending_review')
|
||||
|
@@ -10,7 +10,7 @@
|
||||
- else
|
||||
%span.negative-hint= t('admin.webhooks.disabled')
|
||||
|
||||
•
|
||||
·
|
||||
|
||||
%abbr{ title: webhook.events.join(', ') }= t('admin.webhooks.enabled_events', count: webhook.events.size)
|
||||
|
||||
|
@@ -1,8 +1,8 @@
|
||||
<%= raw t('admin_mailer.new_trends.new_trending_links.title') %>
|
||||
|
||||
<% @links.each do |link| %>
|
||||
- <%= link.title %> • <%= link.url %>
|
||||
<%= standard_locale_name(link.language) %> • <%= raw t('admin.trends.links.usage_comparison', today: link.history.get(Time.now.utc).accounts, yesterday: link.history.get(Time.now.utc - 1.day).accounts) %> • <%= t('admin.trends.tags.current_score', score: link.trend.score.round(2)) %>
|
||||
- <%= link.title %> · <%= link.url %>
|
||||
<%= standard_locale_name(link.language) %> · <%= raw t('admin.trends.links.usage_comparison', today: link.history.get(Time.now.utc).accounts, yesterday: link.history.get(Time.now.utc - 1.day).accounts) %> · <%= t('admin.trends.tags.current_score', score: link.trend.score.round(2)) %>
|
||||
<% end %>
|
||||
|
||||
<%= raw t('application_mailer.view')%> <%= admin_trends_links_url %>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
<% @statuses.each do |status| %>
|
||||
- <%= ActivityPub::TagManager.instance.url_for(status) %>
|
||||
<%= standard_locale_name(status.language) %> • <%= raw t('admin.trends.tags.current_score', score: status.trend.score.round(2)) %>
|
||||
<%= standard_locale_name(status.language) %> · <%= raw t('admin.trends.tags.current_score', score: status.trend.score.round(2)) %>
|
||||
<% end %>
|
||||
|
||||
<%= raw t('application_mailer.view')%> <%= admin_trends_statuses_url %>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
<% @tags.each do |tag| %>
|
||||
- #<%= tag.display_name %>
|
||||
<%= raw t('admin.trends.tags.usage_comparison', today: tag.history.get(Time.now.utc).accounts, yesterday: tag.history.get(Time.now.utc - 1.day).accounts) %> • <%= t('admin.trends.tags.current_score', score: Trends.tags.score(tag.id).round(2)) %>
|
||||
<%= raw t('admin.trends.tags.usage_comparison', today: tag.history.get(Time.now.utc).accounts, yesterday: tag.history.get(Time.now.utc - 1.day).accounts) %> · <%= t('admin.trends.tags.current_score', score: Trends.tags.score(tag.id).round(2)) %>
|
||||
<% end %>
|
||||
|
||||
<% if @lowest_trending_tag %>
|
||||
|
@@ -1,9 +1,11 @@
|
||||
- account_url = local_assigns[:admin] ? admin_account_path(account.id) : ActivityPub::TagManager.instance.url_for(account)
|
||||
- compact ||= false
|
||||
|
||||
.card.h-card
|
||||
= link_to account_url, target: '_blank', rel: 'noopener noreferrer' do
|
||||
.card__img
|
||||
= image_tag account.header.url, alt: ''
|
||||
- unless compact
|
||||
.card__img
|
||||
= image_tag account.header.url, alt: ''
|
||||
.card__bar
|
||||
.avatar
|
||||
= image_tag account.avatar.url, alt: '', width: 48, height: 48, class: 'u-photo'
|
||||
|
@@ -7,8 +7,14 @@
|
||||
.simple_form
|
||||
= render 'auth/shared/progress', stage: 'rules'
|
||||
|
||||
%h1.title= t('auth.rules.title')
|
||||
%p.lead= t('auth.rules.preamble', domain: site_hostname)
|
||||
- if @invite.present? && @invite.autofollow?
|
||||
%h1.title= t('auth.rules.title_invited')
|
||||
%p.lead.invited-by= t('auth.rules.invited_by', domain: site_hostname)
|
||||
= render 'application/card', account: @invite.user.account, compact: true
|
||||
%p.lead= t('auth.rules.preamble_invited', domain: site_hostname)
|
||||
- else
|
||||
%h1.title= t('auth.rules.title')
|
||||
%p.lead= t('auth.rules.preamble', domain: site_hostname)
|
||||
|
||||
%ol.rules-list
|
||||
- @rules.each do |rule|
|
||||
|
@@ -23,7 +23,7 @@
|
||||
- else
|
||||
= t('doorkeeper.authorized_applications.index.never_used')
|
||||
|
||||
•
|
||||
·
|
||||
|
||||
= t('doorkeeper.authorized_applications.index.authorized_at', date: l(application.created_at.to_date))
|
||||
|
||||
|
Reference in New Issue
Block a user