Merge branch 'kb_migration' into kb_migration_development
This commit is contained in:
commit
45f192732f
12 changed files with 169 additions and 35 deletions
|
@ -31,6 +31,7 @@ class Api::V1::ListsController < Api::BaseController
|
|||
end
|
||||
|
||||
def destroy
|
||||
raise Mastodon::ValidationError, I18n.t('antennas.errors.remove_list_with_antenna') if Antenna.where(list_id: @list.id).any?
|
||||
@list.destroy!
|
||||
render_empty
|
||||
end
|
||||
|
|
|
@ -46,6 +46,16 @@ class Follows extends React.PureComponent {
|
|||
render () {
|
||||
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 (
|
||||
<Column>
|
||||
<ColumnBackButton onClick={onBack} />
|
||||
|
@ -59,12 +69,10 @@ class Follows extends React.PureComponent {
|
|||
<ProgressIndicator steps={7} completed={account.get('following_count') * 1} />
|
||||
|
||||
<div className='follow-recommendations'>
|
||||
{isLoading ? (new Array(8)).fill().map((_, i) => <EmptyAccount key={i} />) : suggestions.map(suggestion => (
|
||||
<Account id={suggestion.get('account')} key={suggestion.get('account')} />
|
||||
))}
|
||||
{loadedContent}
|
||||
</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'>
|
||||
<button className='link-button' onClick={onBack}><FormattedMessage id='onboarding.actions.back' defaultMessage='Take me back' /></button>
|
||||
|
|
|
@ -5,14 +5,15 @@ import PropTypes from 'prop-types';
|
|||
import { me, domain } from 'mastodon/initial_state';
|
||||
import { connect } from 'react-redux';
|
||||
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 Icon from 'mastodon/components/icon';
|
||||
import ArrowSmallRight from './components/arrow_small_right';
|
||||
import { Link } from 'react-router-dom';
|
||||
import SwipeableViews from 'react-swipeable-views';
|
||||
|
||||
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 => ({
|
||||
|
@ -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 {
|
||||
|
||||
static propTypes = {
|
||||
|
@ -105,6 +160,12 @@ class Share extends React.PureComponent {
|
|||
|
||||
<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>
|
||||
|
||||
<div className='onboarding__links'>
|
||||
|
|
|
@ -3186,6 +3186,10 @@
|
|||
},
|
||||
{
|
||||
"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",
|
||||
"id": "onboarding.follows.title"
|
||||
|
@ -3265,7 +3269,7 @@
|
|||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
|
@ -3284,6 +3288,18 @@
|
|||
"defaultMessage": "Let people know how they can find you on Mastodon!",
|
||||
"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:",
|
||||
"id": "onboarding.share.next_steps"
|
||||
|
|
|
@ -447,10 +447,11 @@
|
|||
"onboarding.actions.close": "Don't show this screen again",
|
||||
"onboarding.actions.go_to_explore": "See what's trending",
|
||||
"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.title": "Popular 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.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:",
|
||||
|
@ -464,7 +465,10 @@
|
|||
"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.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.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.mismatching": "Password confirmation does not match",
|
||||
"picture_in_picture.restore": "Put it back",
|
||||
|
|
|
@ -3080,6 +3080,29 @@ $ui-header-height: 55px;
|
|||
.account:last-child {
|
||||
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 {
|
||||
|
@ -5617,6 +5640,11 @@ a.status-card.compact:hover {
|
|||
&.active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: 0;
|
||||
background-color: $highlight-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.media-modal__close {
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
# updated_at :datetime not null
|
||||
# expires_at :datetime
|
||||
# with_media_only :boolean default(FALSE), not null
|
||||
# exclude_domains :jsonb
|
||||
# exclude_accounts :jsonb
|
||||
# exclude_tags :jsonb
|
||||
#
|
||||
class Antenna < ApplicationRecord
|
||||
include Expireable
|
||||
|
@ -86,7 +89,7 @@ class Antenna < ApplicationRecord
|
|||
def keywords_raw=(raw)
|
||||
keywords = raw.split(/\R/).filter { |r| r.present? && r.length >= 2 }.uniq
|
||||
self[:keywords] = keywords
|
||||
self[:any_keywords] = !keywords.any? && !exclude_keywords&.any?
|
||||
self[:any_keywords] = !keywords.any?
|
||||
end
|
||||
|
||||
def exclude_keywords_raw
|
||||
|
@ -98,7 +101,6 @@ class Antenna < ApplicationRecord
|
|||
def exclude_keywords_raw=(raw)
|
||||
exclude_keywords = raw.split(/\R/).filter { |r| r.present? }.uniq
|
||||
self[:exclude_keywords] = exclude_keywords
|
||||
self[:any_keywords] = !keywords&.any? && !exclude_keywords.any?
|
||||
end
|
||||
|
||||
def tags_raw
|
||||
|
@ -118,18 +120,19 @@ class Antenna < ApplicationRecord
|
|||
end
|
||||
|
||||
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
|
||||
|
||||
def 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
|
||||
|
||||
antenna_tags.where(exclude: true).destroy_all
|
||||
Tag.find_or_create_by_names(tag_names).each do |tag|
|
||||
antenna_tags.create!(tag: tag, exclude: true)
|
||||
tags << tag.id
|
||||
end
|
||||
self[:exclude_tags] = tags
|
||||
end
|
||||
|
||||
def domains_raw
|
||||
|
@ -149,18 +152,15 @@ class Antenna < ApplicationRecord
|
|||
end
|
||||
|
||||
def exclude_domains_raw
|
||||
antenna_domains.where(exclude: true).map(&:name).join("\n")
|
||||
return '' if !exclude_domains.present?
|
||||
exclude_domains.join("\n")
|
||||
end
|
||||
|
||||
def exclude_domains_raw=(raw)
|
||||
return if exclude_domains_raw == raw
|
||||
|
||||
domain_names = raw.split(/\R/).filter { |r| r.present? }.uniq
|
||||
|
||||
antenna_domains.where(exclude: true).destroy_all
|
||||
domain_names.each do |domain|
|
||||
antenna_domains.create!(name: domain, exclude: true)
|
||||
end
|
||||
self[:exclude_domains] = domain_names
|
||||
end
|
||||
|
||||
def accounts_raw
|
||||
|
@ -186,7 +186,8 @@ class Antenna < ApplicationRecord
|
|||
end
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
hit = false
|
||||
antenna_accounts.where(exclude: true).destroy_all
|
||||
accounts = []
|
||||
account_names.each do |name|
|
||||
username, domain = name.split('@')
|
||||
account = Account.find_by(username: username, domain: domain)
|
||||
if account.present?
|
||||
antenna_accounts.create!(account: account, exclude: true)
|
||||
hit = true
|
||||
accounts << account.id
|
||||
end
|
||||
end
|
||||
self[:exclude_accounts] = accounts
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -118,30 +118,34 @@ class FanOutOnWriteService < BaseService
|
|||
|
||||
def deliver_to_antennas!
|
||||
lists = []
|
||||
tag_ids = @status.tags.pluck(:id)
|
||||
domain = @account.domain || Rails.configuration.x.local_domain
|
||||
|
||||
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_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.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.not(account: @status.account.blocking)
|
||||
antennas = antennas.includes(:antenna_accounts).includes(:antenna_domains).includes(:antenna_tags)
|
||||
antennas.in_batches do |ans|
|
||||
ans.each do |antenna|
|
||||
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? && !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.antenna_accounts.where(exclude: true, account: @status.account).any?
|
||||
next if antenna.antenna_domains.where(exclude: true, name: @status.account.domain).any?
|
||||
next if antenna.antenna_tags.where(exclude: true, tag: @status.tags).any?
|
||||
lists << antenna.list
|
||||
next if antenna.exclude_keywords&.any? { |keyword| @status.text.include?(keyword) }
|
||||
next if antenna.exclude_accounts&.include?(@status.account_id)
|
||||
next if antenna.exclude_domains&.include?(domain)
|
||||
next if antenna.exclude_tags&.any? { |tag_id| tag_ids.include?(tag_id) }
|
||||
lists << antenna.list_id
|
||||
end
|
||||
end
|
||||
lists = lists.uniq
|
||||
|
||||
if lists.any?
|
||||
FeedInsertWorker.push_bulk(lists) do |list|
|
||||
[@status.id, list.id, 'list', { 'update' => update? }]
|
||||
[@status.id, list, 'list', { 'update' => update? }]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -994,6 +994,7 @@ en:
|
|||
empty_contexts: No contexts! You must set any context filters
|
||||
invalid_context: None or invalid context supplied
|
||||
invalid_list_owner: This list is not yours
|
||||
remove_list_with_antenna: Cannot remove list because this list is related to antenna.
|
||||
index:
|
||||
contexts: Antennas in %{contexts}
|
||||
delete: Delete
|
||||
|
|
|
@ -945,7 +945,7 @@ ja:
|
|||
hint_html: 他のアカウントからこのアカウントにフォロワーを引き継いで引っ越したい場合、ここでエイリアスを作成しておく必要があります。エイリアス自体は<strong>無害で、取り消す</strong>ことができます。<strong>引っ越しは以前のアカウント側から開始する必要があります</strong>。
|
||||
remove: エイリアスを削除
|
||||
antennas:
|
||||
beta: アンテナ機能はベータ版です。今後、予告なく全データリセット・機能削除を行う場合があります。この機能の存在は外部に積極的に宣伝しないよう、ご協力をお願いします。
|
||||
beta: アンテナ機能はベータ版です。今後、予告なく全データリセット・機能削除を行う場合があります。
|
||||
contexts:
|
||||
account: アカウント
|
||||
domain: ドメイン
|
||||
|
@ -954,12 +954,13 @@ ja:
|
|||
errors:
|
||||
empty_contexts: 絞り込み条件が1つも指定されていないため無効です(除外条件はカウントされません)
|
||||
invalid_list_owner: これはあなたのリストではありません
|
||||
remove_list_with_antenna: アンテナが関連付けられているリストは削除できません
|
||||
edit:
|
||||
accounts_hint: ローカルアカウントの場合は「@info」、リモートアカウントの場合は「@info@example.com」の形式で指定します。サーバーが認識していないアカウントは保存時に自動的に削除されます。
|
||||
accounts_raw: 絞り込むアカウント
|
||||
available: 有効
|
||||
description: アンテナは、サーバーが認識した全ての公開・ローカル公開投稿のうち、検索許可が「公開」または明示的に設定されていないもの(検索許可システムに対応していないサーバーからの投稿)、かつ購読を拒否していないすべてのアカウントからの投稿が対象です。検出された投稿は、指定したリストに追加されます。
|
||||
domains_hint: ドメインとは、アカウントIDやサイトのURLのうち「kmy.blue」「example.com」に該当する部分です。自身のサーバーを指定することはできません
|
||||
domains_hint: ドメインとは、アカウントIDやサイトのURLのうち「kmy.blue」「example.com」に該当する部分です
|
||||
domains_raw: 絞り込むドメイン
|
||||
exclude_accounts_raw: 除外するアカウント
|
||||
exclude_domains_raw: 除外するドメイン
|
||||
|
|
7
db/migrate/20230426013738_add_excludes_to_antennas.rb
Normal file
7
db/migrate/20230426013738_add_excludes_to_antennas.rb
Normal 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
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# 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
|
||||
enable_extension "plpgsql"
|
||||
|
@ -302,6 +302,9 @@ ActiveRecord::Schema.define(version: 2023_04_23_233429) do
|
|||
t.datetime "updated_at", null: false
|
||||
t.datetime "expires_at"
|
||||
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 ["any_accounts"], name: "index_antennas_on_any_accounts"
|
||||
t.index ["any_domains"], name: "index_antennas_on_any_domains"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue