Merge branch 'kb_migration' into kb_migration_development

This commit is contained in:
KMY 2023-04-26 11:38:42 +09:00
commit 45f192732f
12 changed files with 169 additions and 35 deletions

View file

@ -31,6 +31,7 @@ class Api::V1::ListsController < Api::BaseController
end end
def destroy def destroy
raise Mastodon::ValidationError, I18n.t('antennas.errors.remove_list_with_antenna') if Antenna.where(list_id: @list.id).any?
@list.destroy! @list.destroy!
render_empty render_empty
end end

View file

@ -46,6 +46,16 @@ class Follows extends React.PureComponent {
render () { render () {
const { onBack, isLoading, suggestions, account } = this.props; const { onBack, isLoading, suggestions, account } = this.props;
let loadedContent;
if (isLoading) {
loadedContent = (new Array(8)).fill().map((_, i) => <EmptyAccount key={i} />);
} else if (suggestions.isEmpty()) {
loadedContent = <div className='follow-recommendations__empty'><FormattedMessage id='onboarding.follows.empty' defaultMessage='Unfortunately, no results can be shown right now. You can try using search or browsing the explore page to find people to follow, or try again later.' /></div>;
} else {
loadedContent = suggestions.map(suggestion => <Account id={suggestion.get('account')} key={suggestion.get('account')} />);
}
return ( return (
<Column> <Column>
<ColumnBackButton onClick={onBack} /> <ColumnBackButton onClick={onBack} />
@ -59,12 +69,10 @@ class Follows extends React.PureComponent {
<ProgressIndicator steps={7} completed={account.get('following_count') * 1} /> <ProgressIndicator steps={7} completed={account.get('following_count') * 1} />
<div className='follow-recommendations'> <div className='follow-recommendations'>
{isLoading ? (new Array(8)).fill().map((_, i) => <EmptyAccount key={i} />) : suggestions.map(suggestion => ( {loadedContent}
<Account id={suggestion.get('account')} key={suggestion.get('account')} />
))}
</div> </div>
<p className='onboarding__lead'><FormattedHTMLMessage id='onboarding.tips.accounts_from_other_servers' defaultMessage='<strong>Did you know?</strong> Since Mastodon is decentralized, some profiles you come across will be hosted on servers other than yours. And yet you can interact with them seamlessly! Their server is in the second half of their username!' values={{ strong: text => <strong>{text}</strong> }} /></p> <p className='onboarding__lead'><FormattedHTMLMessage id='onboarding.tips.accounts_from_other_servers' defaultMessage='<strong>Did you know?</strong> Since Mastodon is decentralized, some profiles you come across will be hosted on servers other than yours. And yet you can interact with them seamlessly! Their server is in the second half of their username!' /></p>
<div className='onboarding__footer'> <div className='onboarding__footer'>
<button className='link-button' onClick={onBack}><FormattedMessage id='onboarding.actions.back' defaultMessage='Take me back' /></button> <button className='link-button' onClick={onBack}><FormattedMessage id='onboarding.actions.back' defaultMessage='Take me back' /></button>

View file

@ -5,14 +5,15 @@ import PropTypes from 'prop-types';
import { me, domain } from 'mastodon/initial_state'; import { me, domain } from 'mastodon/initial_state';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { defineMessages, injectIntl, FormattedMessage, FormattedHTMLMessage } from 'react-intl';
import classNames from 'classnames'; import classNames from 'classnames';
import Icon from 'mastodon/components/icon'; import Icon from 'mastodon/components/icon';
import ArrowSmallRight from './components/arrow_small_right'; import ArrowSmallRight from './components/arrow_small_right';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import SwipeableViews from 'react-swipeable-views';
const messages = defineMessages({ const messages = defineMessages({
shareableMessage: { id: 'onboarding.share.message', defaultMessage: 'I\'m {username} on Mastodon! Come follow me at {url}' }, shareableMessage: { id: 'onboarding.share.message', defaultMessage: 'I\'m {username} on #Mastodon! Come follow me at {url}' },
}); });
const mapStateToProps = state => ({ const mapStateToProps = state => ({
@ -80,6 +81,60 @@ class CopyPasteText extends React.PureComponent {
} }
class TipCarousel extends React.PureComponent {
static propTypes = {
children: PropTypes.node,
};
state = {
index: 0,
};
handleSwipe = index => {
this.setState({ index });
};
handleChangeIndex = e => {
this.setState({ index: Number(e.currentTarget.getAttribute('data-index')) });
};
handleKeyDown = e => {
switch(e.key) {
case 'ArrowLeft':
e.preventDefault();
this.setState(({ index }, { children }) => ({ index: Math.abs(index - 1) % children.length }));
break;
case 'ArrowRight':
e.preventDefault();
this.setState(({ index }, { children }) => ({ index: (index + 1) % children.length }));
break;
}
};
render () {
const { children } = this.props;
const { index } = this.state;
return (
<div className='tip-carousel' tabIndex='0' onKeyDown={this.handleKeyDown}>
<SwipeableViews onChangeIndex={this.handleSwipe} index={index} enableMouseEvents tabIndex='-1'>
{children}
</SwipeableViews>
<div className='media-modal__pagination'>
{children.map((_, i) => (
<button key={i} className={classNames('media-modal__page-dot', { active: i === index })} data-index={i} onClick={this.handleChangeIndex}>
{i + 1}
</button>
))}
</div>
</div>
);
}
}
class Share extends React.PureComponent { class Share extends React.PureComponent {
static propTypes = { static propTypes = {
@ -105,6 +160,12 @@ class Share extends React.PureComponent {
<CopyPasteText value={intl.formatMessage(messages.shareableMessage, { username: `@${account.get('username')}@${domain}`, url })} /> <CopyPasteText value={intl.formatMessage(messages.shareableMessage, { username: `@${account.get('username')}@${domain}`, url })} />
<TipCarousel>
<div><p className='onboarding__lead'><FormattedHTMLMessage id='onboarding.tips.verification' defaultMessage='<strong>Did you know?</strong> You can verify your account by putting a link to your Mastodon profile on your own website and adding the website to your profile. No fees or documents necessary!' /></p></div>
<div><p className='onboarding__lead'><FormattedHTMLMessage id='onboarding.tips.migration' defaultMessage='<strong>Did you know?</strong> If you feel like {domain} is not a great server choice for you in the future, you can move to another Mastodon server without losing your followers. You can even host your own server!' values={{ domain }} /></p></div>
<div><p className='onboarding__lead'><FormattedHTMLMessage id='onboarding.tips.2fa' defaultMessage='<strong>Did you know?</strong> You can secure your account by setting up two-factor authentication in your account settings. It works with any TOTP app of your choice, no phone number necessary!' /></p></div>
</TipCarousel>
<p className='onboarding__lead'><FormattedMessage id='onboarding.share.next_steps' defaultMessage='Possible next steps:' /></p> <p className='onboarding__lead'><FormattedMessage id='onboarding.share.next_steps' defaultMessage='Possible next steps:' /></p>
<div className='onboarding__links'> <div className='onboarding__links'>

View file

@ -3186,6 +3186,10 @@
}, },
{ {
"descriptors": [ "descriptors": [
{
"defaultMessage": "Unfortunately, no results can be shown right now. You can try using search or browsing the explore page to find people to follow, or try again later.",
"id": "onboarding.follows.empty"
},
{ {
"defaultMessage": "Popular on Mastodon", "defaultMessage": "Popular on Mastodon",
"id": "onboarding.follows.title" "id": "onboarding.follows.title"
@ -3265,7 +3269,7 @@
{ {
"descriptors": [ "descriptors": [
{ {
"defaultMessage": "I'm {username} on Mastodon! Come follow me at {url}", "defaultMessage": "I'm {username} on #Mastodon! Come follow me at {url}",
"id": "onboarding.share.message" "id": "onboarding.share.message"
}, },
{ {
@ -3284,6 +3288,18 @@
"defaultMessage": "Let people know how they can find you on Mastodon!", "defaultMessage": "Let people know how they can find you on Mastodon!",
"id": "onboarding.share.lead" "id": "onboarding.share.lead"
}, },
{
"defaultMessage": "<strong>Did you know?</strong> You can verify your account by putting a link to your Mastodon profile on your own website and adding the website to your profile. No fees or documents necessary!",
"id": "onboarding.tips.verification"
},
{
"defaultMessage": "<strong>Did you know?</strong> If you feel like {domain} is not a great server choice for you in the future, you can move to another Mastodon server without losing your followers. You can even host your own server!",
"id": "onboarding.tips.migration"
},
{
"defaultMessage": "<strong>Did you know?</strong> You can secure your account by setting up two-factor authentication in your account settings. It works with any TOTP app of your choice, no phone number necessary!",
"id": "onboarding.tips.2fa"
},
{ {
"defaultMessage": "Possible next steps:", "defaultMessage": "Possible next steps:",
"id": "onboarding.share.next_steps" "id": "onboarding.share.next_steps"

View file

@ -447,10 +447,11 @@
"onboarding.actions.close": "Don't show this screen again", "onboarding.actions.close": "Don't show this screen again",
"onboarding.actions.go_to_explore": "See what's trending", "onboarding.actions.go_to_explore": "See what's trending",
"onboarding.actions.go_to_home": "Go to your home feed", "onboarding.actions.go_to_home": "Go to your home feed",
"onboarding.follows.empty": "Unfortunately, no results can be shown right now. You can try using search or browsing the explore page to find people to follow, or try again later.",
"onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!", "onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!",
"onboarding.follows.title": "Popular on Mastodon", "onboarding.follows.title": "Popular on Mastodon",
"onboarding.share.lead": "Let people know how they can find you on Mastodon!", "onboarding.share.lead": "Let people know how they can find you on Mastodon!",
"onboarding.share.message": "I'm {username} on Mastodon! Come follow me at {url}", "onboarding.share.message": "I'm {username} on #Mastodon! Come follow me at {url}",
"onboarding.share.next_steps": "Possible next steps:", "onboarding.share.next_steps": "Possible next steps:",
"onboarding.share.title": "Share your profile", "onboarding.share.title": "Share your profile",
"onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:", "onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:",
@ -464,7 +465,10 @@
"onboarding.steps.setup_profile.title": "Customize your profile", "onboarding.steps.setup_profile.title": "Customize your profile",
"onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!", "onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!",
"onboarding.steps.share_profile.title": "Share your profile", "onboarding.steps.share_profile.title": "Share your profile",
"onboarding.tips.2fa": "<strong>Did you know?</strong> You can secure your account by setting up two-factor authentication in your account settings. It works with any TOTP app of your choice, no phone number necessary!",
"onboarding.tips.accounts_from_other_servers": "<strong>Did you know?</strong> Since Mastodon is decentralized, some profiles you come across will be hosted on servers other than yours. And yet you can interact with them seamlessly! Their server is in the second half of their username!", "onboarding.tips.accounts_from_other_servers": "<strong>Did you know?</strong> Since Mastodon is decentralized, some profiles you come across will be hosted on servers other than yours. And yet you can interact with them seamlessly! Their server is in the second half of their username!",
"onboarding.tips.migration": "<strong>Did you know?</strong> If you feel like {domain} is not a great server choice for you in the future, you can move to another Mastodon server without losing your followers. You can even host your own server!",
"onboarding.tips.verification": "<strong>Did you know?</strong> You can verify your account by putting a link to your Mastodon profile on your own website and adding the website to your profile. No fees or documents necessary!",
"password_confirmation.exceeds_maxlength": "Password confirmation exceeds the maximum password length", "password_confirmation.exceeds_maxlength": "Password confirmation exceeds the maximum password length",
"password_confirmation.mismatching": "Password confirmation does not match", "password_confirmation.mismatching": "Password confirmation does not match",
"picture_in_picture.restore": "Put it back", "picture_in_picture.restore": "Put it back",

View file

@ -3080,6 +3080,29 @@ $ui-header-height: 55px;
.account:last-child { .account:last-child {
border-bottom: 0; border-bottom: 0;
} }
&__empty {
text-align: center;
color: $darker-text-color;
font-weight: 500;
padding: 40px;
}
}
.tip-carousel {
border: 1px solid transparent;
border-radius: 8px;
padding: 16px;
margin-bottom: 30px;
&:focus {
outline: 0;
border-color: $highlight-text-color;
}
.media-modal__pagination {
margin-bottom: 0;
}
} }
.copy-paste-text { .copy-paste-text {
@ -5617,6 +5640,11 @@ a.status-card.compact:hover {
&.active { &.active {
opacity: 1; opacity: 1;
} }
&:focus {
outline: 0;
background-color: $highlight-text-color;
}
} }
.media-modal__close { .media-modal__close {

View file

@ -17,6 +17,9 @@
# updated_at :datetime not null # updated_at :datetime not null
# expires_at :datetime # expires_at :datetime
# with_media_only :boolean default(FALSE), not null # with_media_only :boolean default(FALSE), not null
# exclude_domains :jsonb
# exclude_accounts :jsonb
# exclude_tags :jsonb
# #
class Antenna < ApplicationRecord class Antenna < ApplicationRecord
include Expireable include Expireable
@ -86,7 +89,7 @@ class Antenna < ApplicationRecord
def keywords_raw=(raw) def keywords_raw=(raw)
keywords = raw.split(/\R/).filter { |r| r.present? && r.length >= 2 }.uniq keywords = raw.split(/\R/).filter { |r| r.present? && r.length >= 2 }.uniq
self[:keywords] = keywords self[:keywords] = keywords
self[:any_keywords] = !keywords.any? && !exclude_keywords&.any? self[:any_keywords] = !keywords.any?
end end
def exclude_keywords_raw def exclude_keywords_raw
@ -98,7 +101,6 @@ class Antenna < ApplicationRecord
def exclude_keywords_raw=(raw) def exclude_keywords_raw=(raw)
exclude_keywords = raw.split(/\R/).filter { |r| r.present? }.uniq exclude_keywords = raw.split(/\R/).filter { |r| r.present? }.uniq
self[:exclude_keywords] = exclude_keywords self[:exclude_keywords] = exclude_keywords
self[:any_keywords] = !keywords&.any? && !exclude_keywords.any?
end end
def tags_raw def tags_raw
@ -118,18 +120,19 @@ class Antenna < ApplicationRecord
end end
def exclude_tags_raw def exclude_tags_raw
antenna_tags.where(exclude: true).map(&:tag).map(&:name).join("\n") return '' if !exclude_tags.present?
Tag.where(id: exclude_tags).map(&:name).join("\n")
end end
def exclude_tags_raw=(raw) def exclude_tags_raw=(raw)
return if exclude_tags_raw == raw return if exclude_tags_raw == raw
tags = []
tag_names = raw.split(/\R/).filter { |r| r.present? }.map { |r| r.start_with?('#') ? r[1..-1] : r }.uniq tag_names = raw.split(/\R/).filter { |r| r.present? }.map { |r| r.start_with?('#') ? r[1..-1] : r }.uniq
antenna_tags.where(exclude: true).destroy_all
Tag.find_or_create_by_names(tag_names).each do |tag| Tag.find_or_create_by_names(tag_names).each do |tag|
antenna_tags.create!(tag: tag, exclude: true) tags << tag.id
end end
self[:exclude_tags] = tags
end end
def domains_raw def domains_raw
@ -149,18 +152,15 @@ class Antenna < ApplicationRecord
end end
def exclude_domains_raw def exclude_domains_raw
antenna_domains.where(exclude: true).map(&:name).join("\n") return '' if !exclude_domains.present?
exclude_domains.join("\n")
end end
def exclude_domains_raw=(raw) def exclude_domains_raw=(raw)
return if exclude_domains_raw == raw return if exclude_domains_raw == raw
domain_names = raw.split(/\R/).filter { |r| r.present? }.uniq domain_names = raw.split(/\R/).filter { |r| r.present? }.uniq
self[:exclude_domains] = domain_names
antenna_domains.where(exclude: true).destroy_all
domain_names.each do |domain|
antenna_domains.create!(name: domain, exclude: true)
end
end end
def accounts_raw def accounts_raw
@ -186,7 +186,8 @@ class Antenna < ApplicationRecord
end end
def exclude_accounts_raw def exclude_accounts_raw
antenna_accounts.where(exclude: true).map(&:account).map { |account| account.domain ? "@#{account.username}@#{account.domain}" : "@#{account.username}" }.join("\n") return '' if !exclude_accounts.present?
Account.where(id: exclude_accounts).map { |account| account.domain ? "@#{account.username}@#{account.domain}" : "@#{account.username}" }.join("\n")
end end
def exclude_accounts_raw=(raw) def exclude_accounts_raw=(raw)
@ -194,16 +195,15 @@ class Antenna < ApplicationRecord
account_names = raw.split(/\R/).filter { |r| r.present? }.map { |r| r.start_with?('@') ? r[1..-1] : r }.uniq account_names = raw.split(/\R/).filter { |r| r.present? }.map { |r| r.start_with?('@') ? r[1..-1] : r }.uniq
hit = false accounts = []
antenna_accounts.where(exclude: true).destroy_all
account_names.each do |name| account_names.each do |name|
username, domain = name.split('@') username, domain = name.split('@')
account = Account.find_by(username: username, domain: domain) account = Account.find_by(username: username, domain: domain)
if account.present? if account.present?
antenna_accounts.create!(account: account, exclude: true) accounts << account.id
hit = true
end end
end end
self[:exclude_accounts] = accounts
end end
end end

View file

@ -118,30 +118,34 @@ class FanOutOnWriteService < BaseService
def deliver_to_antennas! def deliver_to_antennas!
lists = [] lists = []
tag_ids = @status.tags.pluck(:id)
domain = @account.domain || Rails.configuration.x.local_domain
antennas = Antenna.availables antennas = Antenna.availables
antennas = antennas.left_joins(:antenna_accounts).where(any_accounts: true).or(Antenna.availables.left_joins(:antenna_accounts) .where(antenna_accounts: { exclude: false, account: @status.account })) antennas = antennas.left_joins(:antenna_accounts).where(any_accounts: true).or(Antenna.availables.left_joins(:antenna_accounts) .where(antenna_accounts: { exclude: false, account: @status.account }))
antennas = antennas.left_joins(:antenna_domains) .where(any_domains: true) .or(Antenna.availables.left_joins(:antenna_accounts).left_joins(:antenna_domains) .where(antenna_domains: { exclude: false, name: @status.account.domain })) antennas = antennas.left_joins(:antenna_domains) .where(any_domains: true) .or(Antenna.availables.left_joins(:antenna_accounts).left_joins(:antenna_domains) .where(antenna_domains: { exclude: false, name: @status.account.domain }))
antennas = antennas.left_joins(:antenna_tags) .where(any_tags: true) .or(Antenna.availables.left_joins(:antenna_accounts).left_joins(:antenna_domains).left_joins(:antenna_tags).where(antenna_tags: { exclude: false, tag: @status.tags })) antennas = antennas.left_joins(:antenna_tags) .where(any_tags: true) .or(Antenna.availables.left_joins(:antenna_accounts).left_joins(:antenna_domains).left_joins(:antenna_tags).where(antenna_tags: { exclude: false, tag: @status.tags }))
antennas = antennas.where(account: @status.account.followers) if @status.visibility.to_sym == :unlisted antennas = antennas.where(account: @status.account.followers) if @status.visibility.to_sym == :unlisted
antennas = antennas.where(with_media_only: false) if !@status.with_media? antennas = antennas.where(with_media_only: false) if !@status.with_media?
antennas = antennas.where.not(account: @status.account.blocking)
antennas = antennas.includes(:antenna_accounts).includes(:antenna_domains).includes(:antenna_tags)
antennas.in_batches do |ans| antennas.in_batches do |ans|
ans.each do |antenna| ans.each do |antenna|
next if !antenna.enabled? next if !antenna.enabled?
next if @status.account.blocking?(antenna.account)
next if antenna.keywords.any? && !([nil, :public].include?(@status.searchability&.to_sym)) next if antenna.keywords.any? && !([nil, :public].include?(@status.searchability&.to_sym))
next if antenna.keywords.any? && !antenna.keywords.any? { |keyword| @status.text.include?(keyword) } next if antenna.keywords.any? && !antenna.keywords.any? { |keyword| @status.text.include?(keyword) }
next if antenna.exclude_keywords.any? && antenna.exclude_keywords.any? { |keyword| @status.text.include?(keyword) } next if antenna.exclude_keywords&.any? { |keyword| @status.text.include?(keyword) }
next if antenna.antenna_accounts.where(exclude: true, account: @status.account).any? next if antenna.exclude_accounts&.include?(@status.account_id)
next if antenna.antenna_domains.where(exclude: true, name: @status.account.domain).any? next if antenna.exclude_domains&.include?(domain)
next if antenna.antenna_tags.where(exclude: true, tag: @status.tags).any? next if antenna.exclude_tags&.any? { |tag_id| tag_ids.include?(tag_id) }
lists << antenna.list lists << antenna.list_id
end end
end end
lists = lists.uniq lists = lists.uniq
if lists.any? if lists.any?
FeedInsertWorker.push_bulk(lists) do |list| FeedInsertWorker.push_bulk(lists) do |list|
[@status.id, list.id, 'list', { 'update' => update? }] [@status.id, list, 'list', { 'update' => update? }]
end end
end end
end end

View file

@ -994,6 +994,7 @@ en:
empty_contexts: No contexts! You must set any context filters empty_contexts: No contexts! You must set any context filters
invalid_context: None or invalid context supplied invalid_context: None or invalid context supplied
invalid_list_owner: This list is not yours invalid_list_owner: This list is not yours
remove_list_with_antenna: Cannot remove list because this list is related to antenna.
index: index:
contexts: Antennas in %{contexts} contexts: Antennas in %{contexts}
delete: Delete delete: Delete

View file

@ -945,7 +945,7 @@ ja:
hint_html: 他のアカウントからこのアカウントにフォロワーを引き継いで引っ越したい場合、ここでエイリアスを作成しておく必要があります。エイリアス自体は<strong>無害で、取り消す</strong>ことができます。<strong>引っ越しは以前のアカウント側から開始する必要があります</strong>。 hint_html: 他のアカウントからこのアカウントにフォロワーを引き継いで引っ越したい場合、ここでエイリアスを作成しておく必要があります。エイリアス自体は<strong>無害で、取り消す</strong>ことができます。<strong>引っ越しは以前のアカウント側から開始する必要があります</strong>。
remove: エイリアスを削除 remove: エイリアスを削除
antennas: antennas:
beta: アンテナ機能はベータ版です。今後、予告なく全データリセット・機能削除を行う場合があります。この機能の存在は外部に積極的に宣伝しないよう、ご協力をお願いします。 beta: アンテナ機能はベータ版です。今後、予告なく全データリセット・機能削除を行う場合があります。
contexts: contexts:
account: アカウント account: アカウント
domain: ドメイン domain: ドメイン
@ -954,12 +954,13 @@ ja:
errors: errors:
empty_contexts: 絞り込み条件が1つも指定されていないため無効です除外条件はカウントされません empty_contexts: 絞り込み条件が1つも指定されていないため無効です除外条件はカウントされません
invalid_list_owner: これはあなたのリストではありません invalid_list_owner: これはあなたのリストではありません
remove_list_with_antenna: アンテナが関連付けられているリストは削除できません
edit: edit:
accounts_hint: ローカルアカウントの場合は「@info」、リモートアカウントの場合は「@info@example.com」の形式で指定します。サーバーが認識していないアカウントは保存時に自動的に削除されます。 accounts_hint: ローカルアカウントの場合は「@info」、リモートアカウントの場合は「@info@example.com」の形式で指定します。サーバーが認識していないアカウントは保存時に自動的に削除されます。
accounts_raw: 絞り込むアカウント accounts_raw: 絞り込むアカウント
available: 有効 available: 有効
description: アンテナは、サーバーが認識した全ての公開・ローカル公開投稿のうち、検索許可が「公開」または明示的に設定されていないもの(検索許可システムに対応していないサーバーからの投稿)、かつ購読を拒否していないすべてのアカウントからの投稿が対象です。検出された投稿は、指定したリストに追加されます。 description: アンテナは、サーバーが認識した全ての公開・ローカル公開投稿のうち、検索許可が「公開」または明示的に設定されていないもの(検索許可システムに対応していないサーバーからの投稿)、かつ購読を拒否していないすべてのアカウントからの投稿が対象です。検出された投稿は、指定したリストに追加されます。
domains_hint: ドメインとは、アカウントIDやサイトのURLのうち「kmy.blue」「example.com」に該当する部分です。自身のサーバーを指定することはできません domains_hint: ドメインとは、アカウントIDやサイトのURLのうち「kmy.blue」「example.com」に該当する部分です
domains_raw: 絞り込むドメイン domains_raw: 絞り込むドメイン
exclude_accounts_raw: 除外するアカウント exclude_accounts_raw: 除外するアカウント
exclude_domains_raw: 除外するドメイン exclude_domains_raw: 除外するドメイン

View file

@ -0,0 +1,7 @@
class AddExcludesToAntennas < ActiveRecord::Migration[6.1]
def change
add_column :antennas, :exclude_domains, :jsonb
add_column :antennas, :exclude_accounts, :jsonb
add_column :antennas, :exclude_tags, :jsonb
end
end

View file

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2023_04_23_233429) do ActiveRecord::Schema.define(version: 2023_04_26_013738) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -302,6 +302,9 @@ ActiveRecord::Schema.define(version: 2023_04_23_233429) do
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.datetime "expires_at" t.datetime "expires_at"
t.boolean "with_media_only", default: false, null: false t.boolean "with_media_only", default: false, null: false
t.jsonb "exclude_domains"
t.jsonb "exclude_accounts"
t.jsonb "exclude_tags"
t.index ["account_id"], name: "index_antennas_on_account_id" t.index ["account_id"], name: "index_antennas_on_account_id"
t.index ["any_accounts"], name: "index_antennas_on_any_accounts" t.index ["any_accounts"], name: "index_antennas_on_any_accounts"
t.index ["any_domains"], name: "index_antennas_on_any_domains" t.index ["any_domains"], name: "index_antennas_on_any_domains"