Wip: antenna
This commit is contained in:
parent
07ea091320
commit
2eb5ffb9b3
19 changed files with 610 additions and 1 deletions
76
app/controllers/antennas_controller.rb
Normal file
76
app/controllers/antennas_controller.rb
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AntennasController < ApplicationController
|
||||||
|
layout 'admin'
|
||||||
|
|
||||||
|
before_action :authenticate_user!
|
||||||
|
before_action :set_antenna, only: [:edit, :update, :destroy]
|
||||||
|
before_action :set_lists, only: [:new, :edit]
|
||||||
|
before_action :set_body_classes
|
||||||
|
before_action :set_cache_headers
|
||||||
|
|
||||||
|
def index
|
||||||
|
@antennas = current_account.antennas.includes(:antenna_domains).includes(:antenna_tags).includes(:antenna_accounts)
|
||||||
|
end
|
||||||
|
|
||||||
|
def new
|
||||||
|
@antenna = current_account.antennas.build
|
||||||
|
@antenna.antenna_domains.build
|
||||||
|
@antenna.antenna_tags.build
|
||||||
|
@antenna.antenna_accounts.build
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@antenna = current_account.antennas.build(thin_resource_params)
|
||||||
|
|
||||||
|
saved = @antenna.save
|
||||||
|
saved = @antenna.update(resource_params) if saved
|
||||||
|
|
||||||
|
if saved
|
||||||
|
redirect_to antennas_path
|
||||||
|
else
|
||||||
|
render action: :new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit; end
|
||||||
|
|
||||||
|
def update
|
||||||
|
if @antenna.update(resource_params)
|
||||||
|
redirect_to antennas_path
|
||||||
|
else
|
||||||
|
render action: :edit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
@antenna.destroy
|
||||||
|
redirect_to antennas_path
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_antenna
|
||||||
|
@antenna = current_account.antennas.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_lists
|
||||||
|
@lists = current_account.owned_lists
|
||||||
|
end
|
||||||
|
|
||||||
|
def resource_params
|
||||||
|
params.require(:antenna).permit(:title, :list, :available, :expires_in, :keywords_raw, :exclude_keywords_raw, :domains_raw, :exclude_domains_raw, :accounts_raw, :exclude_accounts_raw, :tags_raw, :exclude_tags_raw)
|
||||||
|
end
|
||||||
|
|
||||||
|
def thin_resource_params
|
||||||
|
params.require(:antenna).permit(:title, :list)
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_body_classes
|
||||||
|
@body_classes = 'admin'
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_cache_headers
|
||||||
|
response.cache_control.replace(private: true, no_store: true)
|
||||||
|
end
|
||||||
|
end
|
203
app/models/antenna.rb
Normal file
203
app/models/antenna.rb
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: antennas
|
||||||
|
#
|
||||||
|
# id :bigint(8) not null, primary key
|
||||||
|
# account_id :bigint(8) not null
|
||||||
|
# list_id :bigint(8) not null
|
||||||
|
# title :string default(""), not null
|
||||||
|
# keywords :jsonb
|
||||||
|
# exclude_keywords :jsonb
|
||||||
|
# any_domains :boolean default(TRUE), not null
|
||||||
|
# any_tags :boolean default(TRUE), not null
|
||||||
|
# any_accounts :boolean default(TRUE), not null
|
||||||
|
# any_keywords :boolean default(TRUE), not null
|
||||||
|
# available :boolean default(TRUE), not null
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
# expires_at :datetime
|
||||||
|
#
|
||||||
|
class Antenna < ApplicationRecord
|
||||||
|
include Expireable
|
||||||
|
|
||||||
|
has_many :antenna_domains, inverse_of: :antenna, dependent: :destroy
|
||||||
|
has_many :antenna_tags, inverse_of: :antenna, dependent: :destroy
|
||||||
|
has_many :antenna_accounts, inverse_of: :antenna, dependent: :destroy
|
||||||
|
|
||||||
|
belongs_to :account
|
||||||
|
belongs_to :list
|
||||||
|
|
||||||
|
scope :all_keywords, -> { where(any_keywords: true) }
|
||||||
|
scope :all_domains, -> { where(any_domains: true) }
|
||||||
|
scope :all_accounts, -> { where(any_accounts: true) }
|
||||||
|
scope :all_tags, -> { where(any_tags: true) }
|
||||||
|
scope :availables, -> { where(available: true) }
|
||||||
|
|
||||||
|
def enabled?
|
||||||
|
available && !(any_keywords && any_domains && any_accounts && any_tags) && !expires?
|
||||||
|
end
|
||||||
|
|
||||||
|
def expires_in
|
||||||
|
return @expires_in if defined?(@expires_in)
|
||||||
|
return nil if expires_at.nil?
|
||||||
|
|
||||||
|
[30.minutes, 1.hour, 6.hours, 12.hours, 1.day, 1.week].find { |expires_in| expires_in.from_now >= expires_at }
|
||||||
|
end
|
||||||
|
|
||||||
|
def expires?
|
||||||
|
expires_at.present? && expires_at < Time.now.utc
|
||||||
|
end
|
||||||
|
|
||||||
|
def context
|
||||||
|
context = []
|
||||||
|
context << 'domain' if !any_domains
|
||||||
|
context << 'tag' if !any_tags
|
||||||
|
context << 'keyword' if !any_keywords
|
||||||
|
context << 'account' if !any_accounts
|
||||||
|
context
|
||||||
|
end
|
||||||
|
|
||||||
|
def list=(list_id)
|
||||||
|
list_id = list_id.to_i if list_id.is_a?(String)
|
||||||
|
if list_id.is_a?(Numeric)
|
||||||
|
self[:list_id] = list_id
|
||||||
|
else
|
||||||
|
self[:list] = list_id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def keywords_raw
|
||||||
|
return '' if !keywords.present?
|
||||||
|
|
||||||
|
keywords.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def keywords_raw=(raw)
|
||||||
|
keywords = raw.split(/\R/).filter { |r| r.present? }
|
||||||
|
self[:keywords] = keywords
|
||||||
|
self[:any_keywords] = !keywords.any? && !exclude_keywords&.any?
|
||||||
|
end
|
||||||
|
|
||||||
|
def exclude_keywords_raw
|
||||||
|
return '' if !exclude_keywords.present?
|
||||||
|
|
||||||
|
exclude_keywords.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def exclude_keywords_raw=(raw)
|
||||||
|
exclude_keywords = raw.split(/\R/).filter { |r| r.present? }
|
||||||
|
self[:exclude_keywords] = exclude_keywords
|
||||||
|
self[:any_keywords] = !keywords&.any? && !exclude_keywords.any?
|
||||||
|
end
|
||||||
|
|
||||||
|
def tags_raw
|
||||||
|
antenna_tags.where(exclude: false).map(&:tag).map(&:name).join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def tags_raw=(raw)
|
||||||
|
return if tags_raw == raw
|
||||||
|
|
||||||
|
tag_names = raw.split(/\R/).filter { |r| r.present? }
|
||||||
|
|
||||||
|
antenna_tags.where(exclude: false).destroy_all!
|
||||||
|
Tag.find_or_create_by_names(tag_names).each do |tag|
|
||||||
|
antenna_tags.create!(tag: tag, exclude: false)
|
||||||
|
end
|
||||||
|
self[:any_tags] = !tag_names.any? && !exclude_tags&.any?
|
||||||
|
end
|
||||||
|
|
||||||
|
def exclude_tags_raw
|
||||||
|
antenna_tags.where(exclude: true).map(&:tag).map(&:name).join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def exclude_tags_raw=(raw)
|
||||||
|
return if exclude_tags_raw == raw
|
||||||
|
|
||||||
|
tag_names = raw.split(/\R/).filter { |r| r.present? }
|
||||||
|
|
||||||
|
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)
|
||||||
|
end
|
||||||
|
self[:any_tags] = !tag_names.any? && !tags&.any?
|
||||||
|
end
|
||||||
|
|
||||||
|
def domains_raw
|
||||||
|
antenna_domains.where(exclude: false).map(&:name).join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def domains_raw=(raw)
|
||||||
|
return if domains_raw == raw
|
||||||
|
|
||||||
|
domain_names = raw.split(/\R/).filter { |r| r.present? }
|
||||||
|
|
||||||
|
antenna_domains.where(exclude: false).destroy_all!
|
||||||
|
domain_names.each do |domain|
|
||||||
|
antenna_domains.create!(name: domain, exclude: false)
|
||||||
|
end
|
||||||
|
self[:any_domains] = !domain_names.any? && !exclude_domains&.any?
|
||||||
|
end
|
||||||
|
|
||||||
|
def exclude_domains_raw
|
||||||
|
antenna_domains.where(exclude: true).map(&:name).join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def exclude_domains_raw=(raw)
|
||||||
|
return if exclude_domains_raw == raw
|
||||||
|
|
||||||
|
domain_names = raw.split(/\R/).filter { |r| r.present? }
|
||||||
|
|
||||||
|
antenna_domains.where(exclude: true).destroy_all!
|
||||||
|
domain_names.each do |domain|
|
||||||
|
antenna_domains.create!(name: domain, exclude: true)
|
||||||
|
end
|
||||||
|
self[:any_domains] = !domain_names.any? && !domains&.any?
|
||||||
|
end
|
||||||
|
|
||||||
|
def accounts_raw
|
||||||
|
antenna_accounts.where(exclude: false).map(&:account).map { |account| account.domain ? "@#{account.username}@#{account.domain}" : "@#{account.username}" }.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def accounts_raw=(raw)
|
||||||
|
return if accounts_raw == raw
|
||||||
|
|
||||||
|
account_names = raw.split(/\R/).filter { |r| r.present? }
|
||||||
|
|
||||||
|
hit = false
|
||||||
|
antenna_accounts.where(exclude: false).destroy_all!
|
||||||
|
account_names.each do |name|
|
||||||
|
name = name[1..-1] if name.start_with?('@')
|
||||||
|
username, domain = name.split('@')
|
||||||
|
account = Account.find_by(username: username, domain: domain)
|
||||||
|
if account.present?
|
||||||
|
antenna_accounts.create!(account: account, exclude: false)
|
||||||
|
hit = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self[:any_accounts] = !hit && !exclude_accounts&.any?
|
||||||
|
end
|
||||||
|
|
||||||
|
def exclude_accounts_raw
|
||||||
|
antenna_accounts.where(exclude: true).map(&:account).map { |account| account.domain ? "@#{account.username}@#{account.domain}" : "@#{account.username}" }.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def exclude_accounts_raw=(raw)
|
||||||
|
return if exclude_accounts_raw == raw
|
||||||
|
|
||||||
|
account_names = raw.split(/\R/).filter { |r| r.present? }
|
||||||
|
|
||||||
|
hit = false
|
||||||
|
antenna_accounts.where(exclude: true).destroy_all!
|
||||||
|
account_names.each do |name|
|
||||||
|
name = name[1..-1] if name.start_with?('@')
|
||||||
|
username, domain = name.split('@')
|
||||||
|
account = Account.find_by(username: username, domain: domain)
|
||||||
|
if account.present?
|
||||||
|
antenna_accounts.create!(account: account, exclude: true)
|
||||||
|
hit = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self[:any_accounts] = !hit && !accounts&.any?
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
17
app/models/antenna_account.rb
Normal file
17
app/models/antenna_account.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: antenna_accounts
|
||||||
|
#
|
||||||
|
# id :bigint(8) not null, primary key
|
||||||
|
# antenna_id :bigint(8) not null
|
||||||
|
# account_id :bigint(8) not null
|
||||||
|
# exclude :boolean default(FALSE), not null
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
#
|
||||||
|
class AntennaAccount < ApplicationRecord
|
||||||
|
|
||||||
|
belongs_to :antenna
|
||||||
|
belongs_to :account
|
||||||
|
|
||||||
|
end
|
16
app/models/antenna_domain.rb
Normal file
16
app/models/antenna_domain.rb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: antenna_domains
|
||||||
|
#
|
||||||
|
# id :bigint(8) not null, primary key
|
||||||
|
# antenna_id :bigint(8) not null
|
||||||
|
# name :string
|
||||||
|
# exclude :boolean default(FALSE), not null
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
#
|
||||||
|
class AntennaDomain < ApplicationRecord
|
||||||
|
|
||||||
|
belongs_to :antenna
|
||||||
|
|
||||||
|
end
|
17
app/models/antenna_tag.rb
Normal file
17
app/models/antenna_tag.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: antenna_tags
|
||||||
|
#
|
||||||
|
# id :bigint(8) not null, primary key
|
||||||
|
# antenna_id :bigint(8) not null
|
||||||
|
# tag_id :bigint(8) not null
|
||||||
|
# exclude :boolean default(FALSE), not null
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
#
|
||||||
|
class AntennaTag < ApplicationRecord
|
||||||
|
|
||||||
|
belongs_to :antenna
|
||||||
|
belongs_to :tag
|
||||||
|
|
||||||
|
end
|
|
@ -39,6 +39,8 @@ module AccountAssociations
|
||||||
|
|
||||||
has_many :report_notes, dependent: :destroy
|
has_many :report_notes, dependent: :destroy
|
||||||
has_many :custom_filters, inverse_of: :account, dependent: :destroy
|
has_many :custom_filters, inverse_of: :account, dependent: :destroy
|
||||||
|
has_many :antennas, inverse_of: :account, dependent: :destroy
|
||||||
|
has_many :antenna_accounts, inverse_of: :account, dependent: :destroy
|
||||||
|
|
||||||
# Moderation notes
|
# Moderation notes
|
||||||
has_many :account_moderation_notes, dependent: :destroy, inverse_of: :account
|
has_many :account_moderation_notes, dependent: :destroy, inverse_of: :account
|
||||||
|
|
|
@ -23,6 +23,7 @@ class List < ApplicationRecord
|
||||||
|
|
||||||
has_many :list_accounts, inverse_of: :list, dependent: :destroy
|
has_many :list_accounts, inverse_of: :list, dependent: :destroy
|
||||||
has_many :accounts, through: :list_accounts
|
has_many :accounts, through: :list_accounts
|
||||||
|
has_many :antennas, inverse_of: :list, dependent: :destroy
|
||||||
|
|
||||||
validates :title, presence: true
|
validates :title, presence: true
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,8 @@ class Tag < ApplicationRecord
|
||||||
has_many :featured_tags, dependent: :destroy, inverse_of: :tag
|
has_many :featured_tags, dependent: :destroy, inverse_of: :tag
|
||||||
has_many :followers, through: :passive_relationships, source: :account
|
has_many :followers, through: :passive_relationships, source: :account
|
||||||
|
|
||||||
|
has_one :antenna_tag, dependent: :destroy, inverse_of: :tag
|
||||||
|
|
||||||
HASHTAG_SEPARATORS = "_\u00B7\u30FB\u200c"
|
HASHTAG_SEPARATORS = "_\u00B7\u30FB\u200c"
|
||||||
HASHTAG_FIRST_SEQUENCE_CHUNK_ONE = "[[:word:]_][[:word:]#{HASHTAG_SEPARATORS}]*[[:alpha:]#{HASHTAG_SEPARATORS}]"
|
HASHTAG_FIRST_SEQUENCE_CHUNK_ONE = "[[:word:]_][[:word:]#{HASHTAG_SEPARATORS}]*[[:alpha:]#{HASHTAG_SEPARATORS}]"
|
||||||
HASHTAG_FIRST_SEQUENCE_CHUNK_TWO = "[[:word:]#{HASHTAG_SEPARATORS}]*[[:word:]_]"
|
HASHTAG_FIRST_SEQUENCE_CHUNK_TWO = "[[:word:]#{HASHTAG_SEPARATORS}]*[[:word:]_]"
|
||||||
|
|
|
@ -49,6 +49,7 @@ class FanOutOnWriteService < BaseService
|
||||||
when :public, :unlisted, :public_unlisted, :private
|
when :public, :unlisted, :public_unlisted, :private
|
||||||
deliver_to_all_followers!
|
deliver_to_all_followers!
|
||||||
deliver_to_lists!
|
deliver_to_lists!
|
||||||
|
deliver_to_antennas! if [:public, :public_unlisted].include?(@status.visibility.to_sym)
|
||||||
when :limited
|
when :limited
|
||||||
deliver_to_mentioned_followers!
|
deliver_to_mentioned_followers!
|
||||||
else
|
else
|
||||||
|
@ -115,6 +116,30 @@ class FanOutOnWriteService < BaseService
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def deliver_to_antennas!
|
||||||
|
lists = []
|
||||||
|
antennas = Antenna.availables
|
||||||
|
p '=========================== DEBUG A ' + antennas.size.to_s
|
||||||
|
antennas = antennas.merge!(Antenna.where(any_accounts: true).or(Antenna.joins(:antenna_accounts).where(antenna_accounts: { account: @status.account }).map(&:antenna)))
|
||||||
|
p '=========================== DEBUG B ' + antennas.size.to_s
|
||||||
|
p '=========================== DEBUG C ' + antennas.size.to_s
|
||||||
|
p '=========================== DEBUG D ' + antennas.size.to_s
|
||||||
|
antennas.in_batches do |ans|
|
||||||
|
ans.each do |antenna|
|
||||||
|
next if !antenna.enabled?
|
||||||
|
next if antenna.keywords.any? && !@status.text.include?(antenna.keywords)
|
||||||
|
next if antenna.exclude_keywords.any? && @status.text.include?(antenna.exclude_keywords)
|
||||||
|
lists << antenna.list
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if lists.any?
|
||||||
|
FeedInsertWorker.push_bulk(lists) do |list|
|
||||||
|
[@status.id, list.id, 'list', { 'update' => update? }]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def deliver_to_mentioned_followers!
|
def deliver_to_mentioned_followers!
|
||||||
@status.mentions.joins(:account).merge(@account.followers_for_local_distribution).select(:id, :account_id).reorder(nil).find_in_batches do |mentions|
|
@status.mentions.joins(:account).merge(@account.followers_for_local_distribution).select(:id, :account_id).reorder(nil).find_in_batches do |mentions|
|
||||||
FeedInsertWorker.push_bulk(mentions) do |mention|
|
FeedInsertWorker.push_bulk(mentions) do |mention|
|
||||||
|
|
65
app/views/antennas/_antenna.html.haml
Normal file
65
app/views/antennas/_antenna.html.haml
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
.filters-list__item{ class: [antenna.expired? && 'expired'] }
|
||||||
|
= link_to edit_antenna_path(antenna), class: 'filters-list__item__title' do
|
||||||
|
= antenna.title
|
||||||
|
|
||||||
|
- if antenna.expires?
|
||||||
|
.expiration{ title: t('antennas.index.expires_on', date: l(antenna.expires_at)) }
|
||||||
|
- if antenna.expired?
|
||||||
|
= t('invites.expired')
|
||||||
|
- else
|
||||||
|
= t('antennas.index.expires_in', distance: distance_of_time_in_words_to_now(antenna.expires_at))
|
||||||
|
|
||||||
|
.filters-list__item__permissions
|
||||||
|
%ul.permissions-list
|
||||||
|
- unless antenna.antenna_domains.empty?
|
||||||
|
%li.permissions-list__item
|
||||||
|
.permissions-list__item__icon
|
||||||
|
= fa_icon('sitemap')
|
||||||
|
.permissions-list__item__text
|
||||||
|
.permissions-list__item__text__title
|
||||||
|
= t('antennas.index.domains', count: antenna.antenna_domains.size)
|
||||||
|
.permissions-list__item__text__type
|
||||||
|
- domains = antenna.antenna_domains
|
||||||
|
- domains = domains.take(5) + ['…'] if domains.size > 5 # TODO
|
||||||
|
= domains.join(', ')
|
||||||
|
- unless antenna.antenna_accounts.empty?
|
||||||
|
%li.permissions-list__item
|
||||||
|
.permissions-list__item__icon
|
||||||
|
= fa_icon('users')
|
||||||
|
.permissions-list__item__text
|
||||||
|
.permissions-list__item__text__title
|
||||||
|
= t('antennas.index.accounts', count: antenna.antenna_accounts.size)
|
||||||
|
.permissions-list__item__text__type
|
||||||
|
- accounts = antenna.antenna_accounts.map { |account| account.account.domain ? "@#{account.account.username}@#{account.account.domain}" : "@#{account.account.username}" }
|
||||||
|
- accounts = accounts.take(5) + ['…'] if accounts.size > 5 # TODO
|
||||||
|
= accounts.join(', ')
|
||||||
|
- unless antenna.keywords.nil? || antenna.keywords.empty?
|
||||||
|
%li.permissions-list__item
|
||||||
|
.permissions-list__item__icon
|
||||||
|
= fa_icon('paragraph')
|
||||||
|
.permissions-list__item__text
|
||||||
|
.permissions-list__item__text__title
|
||||||
|
= t('antennas.index.keywords', count: antenna.keywords.size)
|
||||||
|
.permissions-list__item__text__type
|
||||||
|
- keywords = antenna.keywords
|
||||||
|
- keywords = keywords.take(5) + ['…'] if keywords.size > 5 # TODO
|
||||||
|
= keywords.join(', ')
|
||||||
|
- unless antenna.antenna_tags.empty?
|
||||||
|
%li.permissions-list__item
|
||||||
|
.permissions-list__item__icon
|
||||||
|
= fa_icon('hashtag')
|
||||||
|
.permissions-list__item__text
|
||||||
|
.permissions-list__item__text__title
|
||||||
|
= t('antennas.index.tags', count: antenna.antenna_tags.size)
|
||||||
|
.permissions-list__item__text__type
|
||||||
|
- tags = antenna.antenna_tags.map { |tag| tag.tag.name }
|
||||||
|
- tags = keywords.take(5) + ['…'] if tags.size > 5 # TODO
|
||||||
|
= tags.join(', ')
|
||||||
|
|
||||||
|
.announcements-list__item__action-bar
|
||||||
|
.announcements-list__item__meta
|
||||||
|
= t('antenna.index.contexts', contexts: antenna.context.map { |context| I18n.t("antenna.contexts.#{context}") }.join(', '))
|
||||||
|
|
||||||
|
%div
|
||||||
|
= table_link_to 'pencil', t('antennas.edit.title'), edit_antenna_path(antenna)
|
||||||
|
= table_link_to 'times', t('antennas.index.delete'), antenna_path(antenna), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') }
|
45
app/views/antennas/_antenna_fields.html.haml
Normal file
45
app/views/antennas/_antenna_fields.html.haml
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
.fields-row
|
||||||
|
.fields-row__column.fields-row__column-6.fields-group
|
||||||
|
= f.input :title, as: :string, wrapper: :with_label, hint: false
|
||||||
|
.fields-row__column.fields-row__column-6.fields-group
|
||||||
|
= f.input :expires_in, wrapper: :with_label, collection: [30.minutes, 1.hour, 6.hours, 12.hours, 1.day, 1.week].map(&:to_i), label_method: lambda { |i| I18n.t("invites.expires_in.#{i}") }, include_blank: I18n.t('invites.expires_in_prompt')
|
||||||
|
|
||||||
|
.fields-row
|
||||||
|
.fields-group.fields-row__column.fields-row__column-6
|
||||||
|
= f.input :list, collection: lists, wrapper: :with_label, label_method: lambda { |list| list.title }, selected: f.object.list&.id, hint: false
|
||||||
|
.fields-group.fields-row__column.fields-row__column-6
|
||||||
|
= f.input :available, wrapper: :with_label, hint: false
|
||||||
|
|
||||||
|
%hr.spacer/
|
||||||
|
|
||||||
|
%h4= t('antennas.edit.domains')
|
||||||
|
|
||||||
|
.fields-row
|
||||||
|
.fields-row__column.fields-row__column-6.fields-group
|
||||||
|
= f.input :domains_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 }
|
||||||
|
.fields-row__column.fields-row__column-6.fields-group
|
||||||
|
= f.input :exclude_domains_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 }
|
||||||
|
|
||||||
|
%h4= t('antennas.edit.accounts')
|
||||||
|
|
||||||
|
.fields-row
|
||||||
|
.fields-row__column.fields-row__column-6.fields-group
|
||||||
|
= f.input :accounts_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 }
|
||||||
|
.fields-row__column.fields-row__column-6.fields-group
|
||||||
|
= f.input :exclude_accounts_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 }
|
||||||
|
|
||||||
|
%h4= t('antennas.edit.tags')
|
||||||
|
|
||||||
|
.fields-row
|
||||||
|
.fields-row__column.fields-row__column-6.fields-group
|
||||||
|
= f.input :tags_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 }
|
||||||
|
.fields-row__column.fields-row__column-6.fields-group
|
||||||
|
= f.input :exclude_tags_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 }
|
||||||
|
|
||||||
|
%h4= t('antennas.edit.keywords')
|
||||||
|
|
||||||
|
.fields-row
|
||||||
|
.fields-row__column.fields-row__column-6.fields-group
|
||||||
|
= f.input :keywords_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 }
|
||||||
|
.fields-row__column.fields-row__column-6.fields-group
|
||||||
|
= f.input :exclude_keywords_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 }
|
8
app/views/antennas/_keyword_fields.html.haml
Normal file
8
app/views/antennas/_keyword_fields.html.haml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
%tr.nested-fields
|
||||||
|
%td= f.input :keyword, as: :string
|
||||||
|
%td
|
||||||
|
.label_input__wrapper= f.input_field :whole_word
|
||||||
|
%td
|
||||||
|
= f.hidden_field :id if f.object&.persisted? # Required so Rails doesn't put the field outside of the <tr/>
|
||||||
|
= link_to_remove_association(f, class: 'table-action-link') do
|
||||||
|
= safe_join([fa_icon('times'), t('antennas.index.delete')])
|
8
app/views/antennas/edit.html.haml
Normal file
8
app/views/antennas/edit.html.haml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
- content_for :page_title do
|
||||||
|
= t('antennas.edit.title')
|
||||||
|
|
||||||
|
= simple_form_for @antenna, url: antenna_path(@antenna), method: :put do |f|
|
||||||
|
= render 'antenna_fields', f: f, lists: @lists
|
||||||
|
|
||||||
|
.actions
|
||||||
|
= f.button :button, t('generic.save_changes'), type: :submit
|
11
app/views/antennas/index.html.haml
Normal file
11
app/views/antennas/index.html.haml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
- content_for :page_title do
|
||||||
|
= t('antennas.index.title')
|
||||||
|
|
||||||
|
- content_for :heading_actions do
|
||||||
|
= link_to t('antennas.new.title'), new_antenna_path, class: 'button'
|
||||||
|
|
||||||
|
- if @antennas.empty?
|
||||||
|
.muted-hint.center-text= t 'antennas.index.empty'
|
||||||
|
- else
|
||||||
|
.applications-list
|
||||||
|
= render partial: 'antenna', collection: @antennas
|
8
app/views/antennas/new.html.haml
Normal file
8
app/views/antennas/new.html.haml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
- content_for :page_title do
|
||||||
|
= t('antennas.new.title')
|
||||||
|
|
||||||
|
= simple_form_for @antenna, url: antennas_path do |f|
|
||||||
|
= render 'antenna_fields', f: f, lists: @lists
|
||||||
|
|
||||||
|
.actions
|
||||||
|
= f.button :button, t('antennas.new.save'), type: :submit
|
|
@ -17,6 +17,7 @@ SimpleNavigation::Configuration.run do |navigation|
|
||||||
|
|
||||||
n.item :relationships, safe_join([fa_icon('users fw'), t('settings.relationships')]), relationships_path, if: -> { current_user.functional? }
|
n.item :relationships, safe_join([fa_icon('users fw'), t('settings.relationships')]), relationships_path, if: -> { current_user.functional? }
|
||||||
n.item :filters, safe_join([fa_icon('filter fw'), t('filters.index.title')]), filters_path, highlights_on: %r{/filters}, if: -> { current_user.functional? }
|
n.item :filters, safe_join([fa_icon('filter fw'), t('filters.index.title')]), filters_path, highlights_on: %r{/filters}, if: -> { current_user.functional? }
|
||||||
|
n.item :antennas, safe_join([fa_icon('wifi fw'), t('antennas.index.title')]), antennas_path
|
||||||
n.item :statuses_cleanup, safe_join([fa_icon('history fw'), t('settings.statuses_cleanup')]), statuses_cleanup_path, if: -> { current_user.functional_or_moved? }
|
n.item :statuses_cleanup, safe_join([fa_icon('history fw'), t('settings.statuses_cleanup')]), statuses_cleanup_path, if: -> { current_user.functional_or_moved? }
|
||||||
|
|
||||||
n.item :security, safe_join([fa_icon('lock fw'), t('settings.account')]), edit_user_registration_path do |s|
|
n.item :security, safe_join([fa_icon('lock fw'), t('settings.account')]), edit_user_registration_path do |s|
|
||||||
|
|
|
@ -216,6 +216,7 @@ Rails.application.routes.draw do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
resources :antennas, except: [:show]
|
||||||
|
|
||||||
resource :relationships, only: [:show, :update]
|
resource :relationships, only: [:show, :update]
|
||||||
resource :statuses_cleanup, controller: :statuses_cleanup, only: [:show, :update]
|
resource :statuses_cleanup, controller: :statuses_cleanup, only: [:show, :update]
|
||||||
|
|
40
db/migrate/20230423002728_create_antennas.rb
Normal file
40
db/migrate/20230423002728_create_antennas.rb
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
class CreateAntennas < ActiveRecord::Migration[6.1]
|
||||||
|
def change
|
||||||
|
create_table :antennas do |t|
|
||||||
|
t.belongs_to :account, null: false, foreign_key: { on_delete: :cascade }
|
||||||
|
t.belongs_to :list, null: false, foreign_key: { on_delete: :cascade }
|
||||||
|
t.string :title, null: false, default: ''
|
||||||
|
t.jsonb :keywords
|
||||||
|
t.jsonb :exclude_keywords
|
||||||
|
t.boolean :any_domains, null: false, default: true, index: true
|
||||||
|
t.boolean :any_tags, null: false, default: true, index: true
|
||||||
|
t.boolean :any_accounts, null: false, default: true, index: true
|
||||||
|
t.boolean :any_keywords, null: false, default: true, index: true
|
||||||
|
t.boolean :available, null: false, default: true, index: true
|
||||||
|
t.datetime :created_at, null: false
|
||||||
|
t.datetime :updated_at, null: false
|
||||||
|
t.datetime :expires_at
|
||||||
|
end
|
||||||
|
create_table :antenna_domains do |t|
|
||||||
|
t.belongs_to :antenna, null: false, foreign_key: { on_delete: :cascade }
|
||||||
|
t.string :name, index: true
|
||||||
|
t.boolean :exclude, null: false, default: false, index: true
|
||||||
|
t.datetime :created_at, null: false
|
||||||
|
t.datetime :updated_at, null: false
|
||||||
|
end
|
||||||
|
create_table :antenna_tags do |t|
|
||||||
|
t.belongs_to :antenna, null: false, foreign_key: { on_delete: :cascade }
|
||||||
|
t.belongs_to :tag, null: false, foreign_key: { on_delete: :cascade }
|
||||||
|
t.boolean :exclude, null: false, default: false, index: true
|
||||||
|
t.datetime :created_at, null: false
|
||||||
|
t.datetime :updated_at, null: false
|
||||||
|
end
|
||||||
|
create_table :antenna_accounts do |t|
|
||||||
|
t.belongs_to :antenna, null: false, foreign_key: { on_delete: :cascade }
|
||||||
|
t.belongs_to :account, null: false, foreign_key: { on_delete: :cascade }
|
||||||
|
t.boolean :exclude, null: false, default: false, index: true
|
||||||
|
t.datetime :created_at, null: false
|
||||||
|
t.datetime :updated_at, null: false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
65
db/schema.rb
65
db/schema.rb
|
@ -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_20_081634) do
|
ActiveRecord::Schema.define(version: 2023_04_23_002728) 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"
|
||||||
|
@ -253,6 +253,62 @@ ActiveRecord::Schema.define(version: 2023_04_20_081634) do
|
||||||
t.bigint "status_ids", array: true
|
t.bigint "status_ids", array: true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "antenna_accounts", force: :cascade do |t|
|
||||||
|
t.bigint "antenna_id", null: false
|
||||||
|
t.bigint "account_id", null: false
|
||||||
|
t.boolean "exclude", default: false, null: false
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.index ["account_id"], name: "index_antenna_accounts_on_account_id"
|
||||||
|
t.index ["antenna_id"], name: "index_antenna_accounts_on_antenna_id"
|
||||||
|
t.index ["exclude"], name: "index_antenna_accounts_on_exclude"
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table "antenna_domains", force: :cascade do |t|
|
||||||
|
t.bigint "antenna_id", null: false
|
||||||
|
t.string "name"
|
||||||
|
t.boolean "exclude", default: false, null: false
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.index ["antenna_id"], name: "index_antenna_domains_on_antenna_id"
|
||||||
|
t.index ["exclude"], name: "index_antenna_domains_on_exclude"
|
||||||
|
t.index ["name"], name: "index_antenna_domains_on_name"
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table "antenna_tags", force: :cascade do |t|
|
||||||
|
t.bigint "antenna_id", null: false
|
||||||
|
t.bigint "tag_id", null: false
|
||||||
|
t.boolean "exclude", default: false, null: false
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.index ["antenna_id"], name: "index_antenna_tags_on_antenna_id"
|
||||||
|
t.index ["exclude"], name: "index_antenna_tags_on_exclude"
|
||||||
|
t.index ["tag_id"], name: "index_antenna_tags_on_tag_id"
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table "antennas", force: :cascade do |t|
|
||||||
|
t.bigint "account_id", null: false
|
||||||
|
t.bigint "list_id", null: false
|
||||||
|
t.string "title", default: "", null: false
|
||||||
|
t.jsonb "keywords"
|
||||||
|
t.jsonb "exclude_keywords"
|
||||||
|
t.boolean "any_domains", default: true, null: false
|
||||||
|
t.boolean "any_tags", default: true, null: false
|
||||||
|
t.boolean "any_accounts", default: true, null: false
|
||||||
|
t.boolean "any_keywords", default: true, null: false
|
||||||
|
t.boolean "available", default: true, null: false
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.datetime "expires_at"
|
||||||
|
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"
|
||||||
|
t.index ["any_keywords"], name: "index_antennas_on_any_keywords"
|
||||||
|
t.index ["any_tags"], name: "index_antennas_on_any_tags"
|
||||||
|
t.index ["available"], name: "index_antennas_on_available"
|
||||||
|
t.index ["list_id"], name: "index_antennas_on_list_id"
|
||||||
|
end
|
||||||
|
|
||||||
create_table "appeals", force: :cascade do |t|
|
create_table "appeals", force: :cascade do |t|
|
||||||
t.bigint "account_id", null: false
|
t.bigint "account_id", null: false
|
||||||
t.bigint "account_warning_id", null: false
|
t.bigint "account_warning_id", null: false
|
||||||
|
@ -1175,6 +1231,13 @@ ActiveRecord::Schema.define(version: 2023_04_20_081634) do
|
||||||
add_foreign_key "announcement_reactions", "accounts", on_delete: :cascade
|
add_foreign_key "announcement_reactions", "accounts", on_delete: :cascade
|
||||||
add_foreign_key "announcement_reactions", "announcements", on_delete: :cascade
|
add_foreign_key "announcement_reactions", "announcements", on_delete: :cascade
|
||||||
add_foreign_key "announcement_reactions", "custom_emojis", on_delete: :cascade
|
add_foreign_key "announcement_reactions", "custom_emojis", on_delete: :cascade
|
||||||
|
add_foreign_key "antenna_accounts", "accounts", on_delete: :cascade
|
||||||
|
add_foreign_key "antenna_accounts", "antennas", on_delete: :cascade
|
||||||
|
add_foreign_key "antenna_domains", "antennas", on_delete: :cascade
|
||||||
|
add_foreign_key "antenna_tags", "antennas", on_delete: :cascade
|
||||||
|
add_foreign_key "antenna_tags", "tags", on_delete: :cascade
|
||||||
|
add_foreign_key "antennas", "accounts", on_delete: :cascade
|
||||||
|
add_foreign_key "antennas", "lists", on_delete: :cascade
|
||||||
add_foreign_key "appeals", "account_warnings", on_delete: :cascade
|
add_foreign_key "appeals", "account_warnings", on_delete: :cascade
|
||||||
add_foreign_key "appeals", "accounts", column: "approved_by_account_id", on_delete: :nullify
|
add_foreign_key "appeals", "accounts", column: "approved_by_account_id", on_delete: :nullify
|
||||||
add_foreign_key "appeals", "accounts", column: "rejected_by_account_id", on_delete: :nullify
|
add_foreign_key "appeals", "accounts", column: "rejected_by_account_id", on_delete: :nullify
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue