@@ -10,8 +10,9 @@ class AccountsController < ApplicationController | |||||
def show | def show | ||||
respond_to do |format| | respond_to do |format| | ||||
format.html do | format.html do | ||||
@body_classes = 'with-modals' | |||||
@pinned_statuses = [] | |||||
@body_classes = 'with-modals' | |||||
@pinned_statuses = [] | |||||
@endorsed_accounts = @account.endorsed_accounts.to_a.sample(4) | |||||
if current_account && @account.blocking?(current_account) | if current_account && @account.blocking?(current_account) | ||||
@statuses = [] | @statuses = [] | ||||
@@ -0,0 +1,32 @@ | |||||
# frozen_string_literal: true | |||||
class Api::V1::Accounts::PinsController < Api::BaseController | |||||
include Authorization | |||||
before_action -> { doorkeeper_authorize! :write, :'write:accounts' } | |||||
before_action :require_user! | |||||
before_action :set_account | |||||
respond_to :json | |||||
def create | |||||
AccountPin.create!(account: current_account, target_account: @account) | |||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships_presenter | |||||
end | |||||
def destroy | |||||
pin = AccountPin.find_by(account: current_account, target_account: @account) | |||||
pin&.destroy! | |||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships_presenter | |||||
end | |||||
private | |||||
def set_account | |||||
@account = Account.find(params[:account_id]) | |||||
end | |||||
def relationships_presenter | |||||
AccountRelationshipsPresenter.new([@account.id], current_user.account_id) | |||||
end | |||||
end |
@@ -6,4 +6,36 @@ module HomeHelper | |||||
locale: I18n.locale, | locale: I18n.locale, | ||||
} | } | ||||
end | end | ||||
def account_link_to(account, button = '') | |||||
content_tag(:div, class: 'account') do | |||||
content_tag(:div, class: 'account__wrapper') do | |||||
section = if account.nil? | |||||
content_tag(:div, class: 'account__display-name') do | |||||
content_tag(:div, class: 'account__avatar-wrapper') do | |||||
content_tag(:div, '', class: 'account__avatar', style: "background-image: url(#{full_asset_url('avatars/original/missing.png', skip_pipeline: true)})") | |||||
end + | |||||
content_tag(:span, class: 'display-name') do | |||||
content_tag(:strong, t('about.contact_missing')) + | |||||
content_tag(:span, t('about.contact_unavailable'), class: 'display-name__account') | |||||
end | |||||
end | |||||
else | |||||
link_to(TagManager.instance.url_for(account), class: 'account__display-name') do | |||||
content_tag(:div, class: 'account__avatar-wrapper') do | |||||
content_tag(:div, '', class: 'account__avatar', style: "background-image: url(#{account.avatar.url})") | |||||
end + | |||||
content_tag(:span, class: 'display-name') do | |||||
content_tag(:bdi) do | |||||
content_tag(:strong, display_name(account, custom_emojify: true), class: 'display-name__html emojify') | |||||
end + | |||||
content_tag(:span, "@#{account.acct}", class: 'display-name__account') | |||||
end | |||||
end | |||||
end | |||||
section + button | |||||
end | |||||
end | |||||
end | |||||
end | end |
@@ -30,6 +30,14 @@ export const ACCOUNT_UNMUTE_REQUEST = 'ACCOUNT_UNMUTE_REQUEST'; | |||||
export const ACCOUNT_UNMUTE_SUCCESS = 'ACCOUNT_UNMUTE_SUCCESS'; | export const ACCOUNT_UNMUTE_SUCCESS = 'ACCOUNT_UNMUTE_SUCCESS'; | ||||
export const ACCOUNT_UNMUTE_FAIL = 'ACCOUNT_UNMUTE_FAIL'; | export const ACCOUNT_UNMUTE_FAIL = 'ACCOUNT_UNMUTE_FAIL'; | ||||
export const ACCOUNT_PIN_REQUEST = 'ACCOUNT_PIN_REQUEST'; | |||||
export const ACCOUNT_PIN_SUCCESS = 'ACCOUNT_PIN_SUCCESS'; | |||||
export const ACCOUNT_PIN_FAIL = 'ACCOUNT_PIN_FAIL'; | |||||
export const ACCOUNT_UNPIN_REQUEST = 'ACCOUNT_UNPIN_REQUEST'; | |||||
export const ACCOUNT_UNPIN_SUCCESS = 'ACCOUNT_UNPIN_SUCCESS'; | |||||
export const ACCOUNT_UNPIN_FAIL = 'ACCOUNT_UNPIN_FAIL'; | |||||
export const FOLLOWERS_FETCH_REQUEST = 'FOLLOWERS_FETCH_REQUEST'; | export const FOLLOWERS_FETCH_REQUEST = 'FOLLOWERS_FETCH_REQUEST'; | ||||
export const FOLLOWERS_FETCH_SUCCESS = 'FOLLOWERS_FETCH_SUCCESS'; | export const FOLLOWERS_FETCH_SUCCESS = 'FOLLOWERS_FETCH_SUCCESS'; | ||||
export const FOLLOWERS_FETCH_FAIL = 'FOLLOWERS_FETCH_FAIL'; | export const FOLLOWERS_FETCH_FAIL = 'FOLLOWERS_FETCH_FAIL'; | ||||
@@ -694,3 +702,69 @@ export function rejectFollowRequestFail(id, error) { | |||||
error, | error, | ||||
}; | }; | ||||
}; | }; | ||||
export function pinAccount(id) { | |||||
return (dispatch, getState) => { | |||||
dispatch(pinAccountRequest(id)); | |||||
api(getState).post(`/api/v1/accounts/${id}/pin`).then(response => { | |||||
dispatch(pinAccountSuccess(response.data)); | |||||
}).catch(error => { | |||||
dispatch(pinAccountFail(error)); | |||||
}); | |||||
}; | |||||
}; | |||||
export function unpinAccount(id) { | |||||
return (dispatch, getState) => { | |||||
dispatch(unpinAccountRequest(id)); | |||||
api(getState).post(`/api/v1/accounts/${id}/unpin`).then(response => { | |||||
dispatch(unpinAccountSuccess(response.data)); | |||||
}).catch(error => { | |||||
dispatch(unpinAccountFail(error)); | |||||
}); | |||||
}; | |||||
}; | |||||
export function pinAccountRequest(id) { | |||||
return { | |||||
type: ACCOUNT_PIN_REQUEST, | |||||
id, | |||||
}; | |||||
}; | |||||
export function pinAccountSuccess(relationship) { | |||||
return { | |||||
type: ACCOUNT_PIN_SUCCESS, | |||||
relationship, | |||||
}; | |||||
}; | |||||
export function pinAccountFail(error) { | |||||
return { | |||||
type: ACCOUNT_PIN_FAIL, | |||||
error, | |||||
}; | |||||
}; | |||||
export function unpinAccountRequest(id) { | |||||
return { | |||||
type: ACCOUNT_UNPIN_REQUEST, | |||||
id, | |||||
}; | |||||
}; | |||||
export function unpinAccountSuccess(relationship) { | |||||
return { | |||||
type: ACCOUNT_UNPIN_SUCCESS, | |||||
relationship, | |||||
}; | |||||
}; | |||||
export function unpinAccountFail(error) { | |||||
return { | |||||
type: ACCOUNT_UNPIN_FAIL, | |||||
error, | |||||
}; | |||||
}; |
@@ -32,6 +32,8 @@ const messages = defineMessages({ | |||||
blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' }, | blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' }, | ||||
domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Hidden domains' }, | domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Hidden domains' }, | ||||
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' }, | mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' }, | ||||
endorse: { id: 'account.endorse', defaultMessage: 'Feature on profile' }, | |||||
unendorse: { id: 'account.unendorse', defaultMessage: 'Don\'t feature on profile' }, | |||||
}); | }); | ||||
@injectIntl | @injectIntl | ||||
@@ -48,6 +50,7 @@ export default class ActionBar extends React.PureComponent { | |||||
onMute: PropTypes.func.isRequired, | onMute: PropTypes.func.isRequired, | ||||
onBlockDomain: PropTypes.func.isRequired, | onBlockDomain: PropTypes.func.isRequired, | ||||
onUnblockDomain: PropTypes.func.isRequired, | onUnblockDomain: PropTypes.func.isRequired, | ||||
onEndorseToggle: PropTypes.func.isRequired, | |||||
intl: PropTypes.object.isRequired, | intl: PropTypes.object.isRequired, | ||||
}; | }; | ||||
@@ -93,6 +96,9 @@ export default class ActionBar extends React.PureComponent { | |||||
} else { | } else { | ||||
menu.push({ text: intl.formatMessage(messages.showReblogs, { name: account.get('username') }), action: this.props.onReblogToggle }); | menu.push({ text: intl.formatMessage(messages.showReblogs, { name: account.get('username') }), action: this.props.onReblogToggle }); | ||||
} | } | ||||
menu.push({ text: intl.formatMessage(account.getIn(['relationship', 'endorsed']) ? messages.unendorse : messages.endorse), action: this.props.onEndorseToggle }); | |||||
menu.push(null); | |||||
} | } | ||||
if (account.getIn(['relationship', 'muting'])) { | if (account.getIn(['relationship', 'muting'])) { | ||||
@@ -22,6 +22,7 @@ export default class Header extends ImmutablePureComponent { | |||||
onMute: PropTypes.func.isRequired, | onMute: PropTypes.func.isRequired, | ||||
onBlockDomain: PropTypes.func.isRequired, | onBlockDomain: PropTypes.func.isRequired, | ||||
onUnblockDomain: PropTypes.func.isRequired, | onUnblockDomain: PropTypes.func.isRequired, | ||||
onEndorseToggle: PropTypes.func.isRequired, | |||||
hideTabs: PropTypes.bool, | hideTabs: PropTypes.bool, | ||||
}; | }; | ||||
@@ -73,6 +74,10 @@ export default class Header extends ImmutablePureComponent { | |||||
this.props.onUnblockDomain(domain); | this.props.onUnblockDomain(domain); | ||||
} | } | ||||
handleEndorseToggle = () => { | |||||
this.props.onEndorseToggle(this.props.account); | |||||
} | |||||
render () { | render () { | ||||
const { account, hideTabs } = this.props; | const { account, hideTabs } = this.props; | ||||
@@ -100,6 +105,7 @@ export default class Header extends ImmutablePureComponent { | |||||
onMute={this.handleMute} | onMute={this.handleMute} | ||||
onBlockDomain={this.handleBlockDomain} | onBlockDomain={this.handleBlockDomain} | ||||
onUnblockDomain={this.handleUnblockDomain} | onUnblockDomain={this.handleUnblockDomain} | ||||
onEndorseToggle={this.handleEndorseToggle} | |||||
/> | /> | ||||
{!hideTabs && ( | {!hideTabs && ( | ||||
@@ -8,6 +8,8 @@ import { | |||||
blockAccount, | blockAccount, | ||||
unblockAccount, | unblockAccount, | ||||
unmuteAccount, | unmuteAccount, | ||||
pinAccount, | |||||
unpinAccount, | |||||
} from '../../../actions/accounts'; | } from '../../../actions/accounts'; | ||||
import { | import { | ||||
mentionCompose, | mentionCompose, | ||||
@@ -82,6 +84,14 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ | |||||
} | } | ||||
}, | }, | ||||
onEndorseToggle (account) { | |||||
if (account.getIn(['relationship', 'endorsed'])) { | |||||
dispatch(unpinAccount(account.get('id'))); | |||||
} else { | |||||
dispatch(pinAccount(account.get('id'))); | |||||
} | |||||
}, | |||||
onReport (account) { | onReport (account) { | ||||
dispatch(initReport(account)); | dispatch(initReport(account)); | ||||
}, | }, | ||||
@@ -5,6 +5,8 @@ import { | |||||
ACCOUNT_UNBLOCK_SUCCESS, | ACCOUNT_UNBLOCK_SUCCESS, | ||||
ACCOUNT_MUTE_SUCCESS, | ACCOUNT_MUTE_SUCCESS, | ||||
ACCOUNT_UNMUTE_SUCCESS, | ACCOUNT_UNMUTE_SUCCESS, | ||||
ACCOUNT_PIN_SUCCESS, | |||||
ACCOUNT_UNPIN_SUCCESS, | |||||
RELATIONSHIPS_FETCH_SUCCESS, | RELATIONSHIPS_FETCH_SUCCESS, | ||||
} from '../actions/accounts'; | } from '../actions/accounts'; | ||||
import { | import { | ||||
@@ -41,6 +43,8 @@ export default function relationships(state = initialState, action) { | |||||
case ACCOUNT_UNBLOCK_SUCCESS: | case ACCOUNT_UNBLOCK_SUCCESS: | ||||
case ACCOUNT_MUTE_SUCCESS: | case ACCOUNT_MUTE_SUCCESS: | ||||
case ACCOUNT_UNMUTE_SUCCESS: | case ACCOUNT_UNMUTE_SUCCESS: | ||||
case ACCOUNT_PIN_SUCCESS: | |||||
case ACCOUNT_UNPIN_SUCCESS: | |||||
return normalizeRelationship(state, action.relationship); | return normalizeRelationship(state, action.relationship); | ||||
case RELATIONSHIPS_FETCH_SUCCESS: | case RELATIONSHIPS_FETCH_SUCCESS: | ||||
return normalizeRelationships(state, action.relationships); | return normalizeRelationships(state, action.relationships); | ||||
@@ -71,6 +71,38 @@ | |||||
} | } | ||||
} | } | ||||
.endorsements-widget { | |||||
margin-bottom: 10px; | |||||
padding-bottom: 10px; | |||||
h4 { | |||||
padding: 10px; | |||||
text-transform: uppercase; | |||||
font-weight: 700; | |||||
font-size: 13px; | |||||
color: $darker-text-color; | |||||
} | |||||
.account { | |||||
padding: 10px 0; | |||||
&:last-child { | |||||
border-bottom: 0; | |||||
} | |||||
.account__display-name { | |||||
display: flex; | |||||
align-items: center; | |||||
} | |||||
.account__avatar { | |||||
width: 44px; | |||||
height: 44px; | |||||
background-size: 44px 44px; | |||||
} | |||||
} | |||||
} | |||||
.moved-account-widget { | .moved-account-widget { | ||||
padding: 15px; | padding: 15px; | ||||
padding-bottom: 20px; | padding-bottom: 20px; | ||||
@@ -89,6 +89,10 @@ class Account < ApplicationRecord | |||||
has_many :status_pins, inverse_of: :account, dependent: :destroy | has_many :status_pins, inverse_of: :account, dependent: :destroy | ||||
has_many :pinned_statuses, -> { reorder('status_pins.created_at DESC') }, through: :status_pins, class_name: 'Status', source: :status | has_many :pinned_statuses, -> { reorder('status_pins.created_at DESC') }, through: :status_pins, class_name: 'Status', source: :status | ||||
# Endorsements | |||||
has_many :account_pins, inverse_of: :account, dependent: :destroy | |||||
has_many :endorsed_accounts, through: :account_pins, class_name: 'Account', source: :target_account | |||||
# Media | # Media | ||||
has_many :media_attachments, dependent: :destroy | has_many :media_attachments, dependent: :destroy | ||||
@@ -0,0 +1,26 @@ | |||||
# frozen_string_literal: true | |||||
# == Schema Information | |||||
# | |||||
# Table name: account_pins | |||||
# | |||||
# id :bigint(8) not null, primary key | |||||
# account_id :bigint(8) | |||||
# target_account_id :bigint(8) | |||||
# created_at :datetime not null | |||||
# updated_at :datetime not null | |||||
# | |||||
class AccountPin < ApplicationRecord | |||||
include RelationshipCacheable | |||||
belongs_to :account | |||||
belongs_to :target_account, class_name: 'Account' | |||||
validate :validate_follow_relationship | |||||
private | |||||
def validate_follow_relationship | |||||
errors.add(:base, I18n.t('accounts.pin_errors.following')) unless account.following?(target_account) | |||||
end | |||||
end |
@@ -40,6 +40,10 @@ module AccountInteractions | |||||
end | end | ||||
end | end | ||||
def endorsed_map(target_account_ids, account_id) | |||||
follow_mapping(AccountPin.where(account_id: account_id, target_account_id: target_account_ids), :target_account_id) | |||||
end | |||||
def domain_blocking_map(target_account_ids, account_id) | def domain_blocking_map(target_account_ids, account_id) | ||||
accounts_map = Account.where(id: target_account_ids).select('id, domain').map { |a| [a.id, a.domain] }.to_h | accounts_map = Account.where(id: target_account_ids).select('id, domain').map { |a| [a.id, a.domain] }.to_h | ||||
blocked_domains = domain_blocking_map_by_domain(accounts_map.values.compact, account_id) | blocked_domains = domain_blocking_map_by_domain(accounts_map.values.compact, account_id) | ||||
@@ -190,6 +194,10 @@ module AccountInteractions | |||||
status_pins.where(status: status).exists? | status_pins.where(status: status).exists? | ||||
end | end | ||||
def endorsed?(account) | |||||
account_pins.where(target_account: account).exists? | |||||
end | |||||
def followers_for_local_distribution | def followers_for_local_distribution | ||||
followers.local | followers.local | ||||
.joins(:user) | .joins(:user) | ||||
@@ -2,7 +2,8 @@ | |||||
class AccountRelationshipsPresenter | class AccountRelationshipsPresenter | ||||
attr_reader :following, :followed_by, :blocking, | attr_reader :following, :followed_by, :blocking, | ||||
:muting, :requested, :domain_blocking | |||||
:muting, :requested, :domain_blocking, | |||||
:endorsed | |||||
def initialize(account_ids, current_account_id, **options) | def initialize(account_ids, current_account_id, **options) | ||||
@account_ids = account_ids.map { |a| a.is_a?(Account) ? a.id : a } | @account_ids = account_ids.map { |a| a.is_a?(Account) ? a.id : a } | ||||
@@ -14,6 +15,7 @@ class AccountRelationshipsPresenter | |||||
@muting = cached[:muting].merge(Account.muting_map(@uncached_account_ids, @current_account_id)) | @muting = cached[:muting].merge(Account.muting_map(@uncached_account_ids, @current_account_id)) | ||||
@requested = cached[:requested].merge(Account.requested_map(@uncached_account_ids, @current_account_id)) | @requested = cached[:requested].merge(Account.requested_map(@uncached_account_ids, @current_account_id)) | ||||
@domain_blocking = cached[:domain_blocking].merge(Account.domain_blocking_map(@uncached_account_ids, @current_account_id)) | @domain_blocking = cached[:domain_blocking].merge(Account.domain_blocking_map(@uncached_account_ids, @current_account_id)) | ||||
@endorsed = cached[:endorsed].merge(Account.endorsed_map(@uncached_account_ids, @current_account_id)) | |||||
cache_uncached! | cache_uncached! | ||||
@@ -23,6 +25,7 @@ class AccountRelationshipsPresenter | |||||
@muting.merge!(options[:muting_map] || {}) | @muting.merge!(options[:muting_map] || {}) | ||||
@requested.merge!(options[:requested_map] || {}) | @requested.merge!(options[:requested_map] || {}) | ||||
@domain_blocking.merge!(options[:domain_blocking_map] || {}) | @domain_blocking.merge!(options[:domain_blocking_map] || {}) | ||||
@endorsed.merge!(options[:endorsed_map] || {}) | |||||
end | end | ||||
private | private | ||||
@@ -37,6 +40,7 @@ class AccountRelationshipsPresenter | |||||
muting: {}, | muting: {}, | ||||
requested: {}, | requested: {}, | ||||
domain_blocking: {}, | domain_blocking: {}, | ||||
endorsed: {}, | |||||
} | } | ||||
@uncached_account_ids = [] | @uncached_account_ids = [] | ||||
@@ -63,6 +67,7 @@ class AccountRelationshipsPresenter | |||||
muting: { account_id => muting[account_id] }, | muting: { account_id => muting[account_id] }, | ||||
requested: { account_id => requested[account_id] }, | requested: { account_id => requested[account_id] }, | ||||
domain_blocking: { account_id => domain_blocking[account_id] }, | domain_blocking: { account_id => domain_blocking[account_id] }, | ||||
endorsed: { account_id => endorsed[account_id] }, | |||||
} | } | ||||
Rails.cache.write("relationship:#{@current_account_id}:#{account_id}", maps_for_account, expires_in: 1.day) | Rails.cache.write("relationship:#{@current_account_id}:#{account_id}", maps_for_account, expires_in: 1.day) | ||||
@@ -2,7 +2,8 @@ | |||||
class REST::RelationshipSerializer < ActiveModel::Serializer | class REST::RelationshipSerializer < ActiveModel::Serializer | ||||
attributes :id, :following, :showing_reblogs, :followed_by, :blocking, | attributes :id, :following, :showing_reblogs, :followed_by, :blocking, | ||||
:muting, :muting_notifications, :requested, :domain_blocking | |||||
:muting, :muting_notifications, :requested, :domain_blocking, | |||||
:endorsed | |||||
def id | def id | ||||
object.id.to_s | object.id.to_s | ||||
@@ -41,4 +42,8 @@ class REST::RelationshipSerializer < ActiveModel::Serializer | |||||
def domain_blocking | def domain_blocking | ||||
instance_options[:relationships].domain_blocking[object.id] || false | instance_options[:relationships].domain_blocking[object.id] || false | ||||
end | end | ||||
def endorsed | |||||
instance_options[:relationships].endorsed[object.id] || false | |||||
end | |||||
end | end |
@@ -55,4 +55,12 @@ | |||||
= render 'moved', account: @account | = render 'moved', account: @account | ||||
= render 'bio', account: @account | = render 'bio', account: @account | ||||
- unless @endorsed_accounts.empty? | |||||
.endorsements-widget | |||||
%h4= t 'accounts.choices_html', name: content_tag(:bdi, display_name(@account, custom_emojify: true)) | |||||
- @endorsed_accounts.each do |account| | |||||
= account_link_to account | |||||
= render 'application/sidebar' | = render 'application/sidebar' |
@@ -39,6 +39,7 @@ en: | |||||
user_count_before: Home to | user_count_before: Home to | ||||
what_is_mastodon: What is Mastodon? | what_is_mastodon: What is Mastodon? | ||||
accounts: | accounts: | ||||
choices_html: "%{name}'s choices:" | |||||
follow: Follow | follow: Follow | ||||
followers: Followers | followers: Followers | ||||
following: Following | following: Following | ||||
@@ -49,6 +50,8 @@ en: | |||||
nothing_here: There is nothing here! | nothing_here: There is nothing here! | ||||
people_followed_by: People whom %{name} follows | people_followed_by: People whom %{name} follows | ||||
people_who_follow: People who follow %{name} | people_who_follow: People who follow %{name} | ||||
pin_errors: | |||||
following: You must be already following the person you want to endorse | |||||
posts: Toots | posts: Toots | ||||
posts_with_replies: Toots and replies | posts_with_replies: Toots and replies | ||||
reserved_username: The username is reserved | reserved_username: The username is reserved | ||||
@@ -309,6 +309,9 @@ Rails.application.routes.draw do | |||||
post :mute | post :mute | ||||
post :unmute | post :unmute | ||||
end | end | ||||
resource :pin, only: :create, controller: 'accounts/pins' | |||||
post :unpin, to: 'accounts/pins#destroy' | |||||
end | end | ||||
resources :lists, only: [:index, :create, :show, :update, :destroy] do | resources :lists, only: [:index, :create, :show, :update, :destroy] do | ||||
@@ -0,0 +1,12 @@ | |||||
class CreateAccountPins < ActiveRecord::Migration[5.2] | |||||
def change | |||||
create_table :account_pins do |t| | |||||
t.belongs_to :account, foreign_key: { on_delete: :cascade } | |||||
t.belongs_to :target_account, foreign_key: { on_delete: :cascade, to_table: :accounts } | |||||
t.timestamps | |||||
end | |||||
add_index :account_pins, [:account_id, :target_account_id], unique: true | |||||
end | |||||
end |
@@ -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: 2018_07_11_152640) do | |||||
ActiveRecord::Schema.define(version: 2018_08_08_175627) 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" | ||||
@@ -33,6 +33,16 @@ ActiveRecord::Schema.define(version: 2018_07_11_152640) do | |||||
t.index ["target_account_id"], name: "index_account_moderation_notes_on_target_account_id" | t.index ["target_account_id"], name: "index_account_moderation_notes_on_target_account_id" | ||||
end | end | ||||
create_table "account_pins", force: :cascade do |t| | |||||
t.bigint "account_id" | |||||
t.bigint "target_account_id" | |||||
t.datetime "created_at", null: false | |||||
t.datetime "updated_at", null: false | |||||
t.index ["account_id", "target_account_id"], name: "index_account_pins_on_account_id_and_target_account_id", unique: true | |||||
t.index ["account_id"], name: "index_account_pins_on_account_id" | |||||
t.index ["target_account_id"], name: "index_account_pins_on_target_account_id" | |||||
end | |||||
create_table "accounts", force: :cascade do |t| | create_table "accounts", force: :cascade do |t| | ||||
t.string "username", default: "", null: false | t.string "username", default: "", null: false | ||||
t.string "domain" | t.string "domain" | ||||
@@ -149,9 +159,9 @@ ActiveRecord::Schema.define(version: 2018_07_11_152640) do | |||||
t.text "phrase", default: "", null: false | t.text "phrase", default: "", null: false | ||||
t.string "context", default: [], null: false, array: true | t.string "context", default: [], null: false, array: true | ||||
t.boolean "irreversible", default: false, null: false | t.boolean "irreversible", default: false, null: false | ||||
t.boolean "whole_word", default: true, null: false | |||||
t.datetime "created_at", null: false | t.datetime "created_at", null: false | ||||
t.datetime "updated_at", null: false | t.datetime "updated_at", null: false | ||||
t.boolean "whole_word", default: true, null: false | |||||
t.index ["account_id"], name: "index_custom_filters_on_account_id" | t.index ["account_id"], name: "index_custom_filters_on_account_id" | ||||
end | end | ||||
@@ -575,6 +585,8 @@ ActiveRecord::Schema.define(version: 2018_07_11_152640) do | |||||
add_foreign_key "account_domain_blocks", "accounts", name: "fk_206c6029bd", on_delete: :cascade | add_foreign_key "account_domain_blocks", "accounts", name: "fk_206c6029bd", on_delete: :cascade | ||||
add_foreign_key "account_moderation_notes", "accounts" | add_foreign_key "account_moderation_notes", "accounts" | ||||
add_foreign_key "account_moderation_notes", "accounts", column: "target_account_id" | add_foreign_key "account_moderation_notes", "accounts", column: "target_account_id" | ||||
add_foreign_key "account_pins", "accounts", column: "target_account_id", on_delete: :cascade | |||||
add_foreign_key "account_pins", "accounts", on_delete: :cascade | |||||
add_foreign_key "accounts", "accounts", column: "moved_to_account_id", on_delete: :nullify | add_foreign_key "accounts", "accounts", column: "moved_to_account_id", on_delete: :nullify | ||||
add_foreign_key "admin_action_logs", "accounts", on_delete: :cascade | add_foreign_key "admin_action_logs", "accounts", on_delete: :cascade | ||||
add_foreign_key "backups", "users", on_delete: :nullify | add_foreign_key "backups", "users", on_delete: :nullify | ||||
@@ -0,0 +1,4 @@ | |||||
Fabricator(:account_pin) do | |||||
account nil | |||||
target_account nil | |||||
end |
@@ -0,0 +1,5 @@ | |||||
require 'rails_helper' | |||||
RSpec.describe AccountPin, type: :model do | |||||
pending "add some examples to (or delete) #{__FILE__}" | |||||
end |