Merge branch 'feature/promoted-gabs' of https://code.gab.com/gab/social/gab-social into develop
This commit is contained in:
commit
e856e2a136
56
app/controllers/settings/promotions_controller.rb
Normal file
56
app/controllers/settings/promotions_controller.rb
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
class Settings::PromotionsController < Admin::BaseController
|
||||||
|
before_action :set_promotion, except: [:index, :new, :create]
|
||||||
|
|
||||||
|
def index
|
||||||
|
@promotions = Promotion.all
|
||||||
|
end
|
||||||
|
|
||||||
|
def new
|
||||||
|
@promotion = Promotion.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@promotion = Promotion.new(resource_params)
|
||||||
|
|
||||||
|
if @promotion.save
|
||||||
|
log_action :create, @promotion
|
||||||
|
redirect_to settings_promotions_path, notice: I18n.t('promotions.created_msg')
|
||||||
|
else
|
||||||
|
render :new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
if @promotion.update(resource_params)
|
||||||
|
log_action :update, @promotion
|
||||||
|
flash[:notice] = I18n.t('promotions.updated_msg')
|
||||||
|
else
|
||||||
|
flash[:alert] = I18n.t('promotions.update_failed_msg')
|
||||||
|
end
|
||||||
|
redirect_to settings_promotions_path
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
@promotion.destroy!
|
||||||
|
log_action :destroy, @promotion
|
||||||
|
flash[:notice] = I18n.t('promotions.destroyed_msg')
|
||||||
|
redirect_to settings_promotions_path
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_promotion
|
||||||
|
@promotion = Promotion.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_filter_params
|
||||||
|
@filter_params = filter_params.to_hash.symbolize_keys
|
||||||
|
end
|
||||||
|
|
||||||
|
def resource_params
|
||||||
|
params.require(:promotion).permit(:expires_at, :status_id, :timeline_id, :position)
|
||||||
|
end
|
||||||
|
end
|
@ -92,6 +92,7 @@ class Status extends ImmutablePureComponent {
|
|||||||
cacheMediaWidth: PropTypes.func,
|
cacheMediaWidth: PropTypes.func,
|
||||||
cachedMediaWidth: PropTypes.number,
|
cachedMediaWidth: PropTypes.number,
|
||||||
group: ImmutablePropTypes.map,
|
group: ImmutablePropTypes.map,
|
||||||
|
promoted: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
// Avoid checking props that are functions (and whose equality will always
|
// Avoid checking props that are functions (and whose equality will always
|
||||||
@ -261,7 +262,7 @@ class Status extends ImmutablePureComponent {
|
|||||||
let media = null;
|
let media = null;
|
||||||
let statusAvatar, prepend, rebloggedByText, reblogContent;
|
let statusAvatar, prepend, rebloggedByText, reblogContent;
|
||||||
|
|
||||||
const { intl, hidden, featured, otherAccounts, unread, showThread, group } = this.props;
|
const { intl, hidden, featured, otherAccounts, unread, showThread, group, promoted } = this.props;
|
||||||
|
|
||||||
let { status, account, ...other } = this.props;
|
let { status, account, ...other } = this.props;
|
||||||
|
|
||||||
@ -293,7 +294,14 @@ class Status extends ImmutablePureComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (featured) {
|
if (promoted) {
|
||||||
|
prepend = (
|
||||||
|
<div className='status__prepend'>
|
||||||
|
<div className='status__prepend-icon-wrapper'><Icon id='star' className='status__prepend-icon' fixedWidth /></div>
|
||||||
|
<FormattedMessage id='status.promoted' defaultMessage='Promoted gab' />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (featured) {
|
||||||
prepend = (
|
prepend = (
|
||||||
<div className='status__prepend'>
|
<div className='status__prepend'>
|
||||||
<div className='status__prepend-icon-wrapper'><Icon id='thumb-tack' className='status__prepend-icon' fixedWidth /></div>
|
<div className='status__prepend-icon-wrapper'><Icon id='thumb-tack' className='status__prepend-icon' fixedWidth /></div>
|
||||||
|
@ -29,12 +29,24 @@ export default class StatusList extends ImmutablePureComponent {
|
|||||||
withGroupAdmin: PropTypes.bool,
|
withGroupAdmin: PropTypes.bool,
|
||||||
onScrollToTop: PropTypes.func,
|
onScrollToTop: PropTypes.func,
|
||||||
onScroll: PropTypes.func,
|
onScroll: PropTypes.func,
|
||||||
|
promotion: PropTypes.object,
|
||||||
|
promotedStatus: ImmutablePropTypes.map,
|
||||||
|
fetchStatus: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.handleDequeueTimeline();
|
this.handleDequeueTimeline();
|
||||||
|
this.fetchPromotedStatus();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fetchPromotedStatus() {
|
||||||
|
const { promotion, promotedStatus, fetchStatus } = this.props;
|
||||||
|
|
||||||
|
if (promotion && !promotedStatus) {
|
||||||
|
fetchStatus(promotion.status_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getFeaturedStatusCount = () => {
|
getFeaturedStatusCount = () => {
|
||||||
return this.props.featuredStatusIds ? this.props.featuredStatusIds.size : 0;
|
return this.props.featuredStatusIds ? this.props.featuredStatusIds.size : 0;
|
||||||
}
|
}
|
||||||
@ -86,7 +98,7 @@ export default class StatusList extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { statusIds, featuredStatusIds, onLoadMore, timelineId, totalQueuedItemsCount, isLoading, isPartial, withGroupAdmin, group, ...other } = this.props;
|
const { statusIds, featuredStatusIds, onLoadMore, timelineId, totalQueuedItemsCount, isLoading, isPartial, withGroupAdmin, group, promotion, promotedStatus, ...other } = this.props;
|
||||||
|
|
||||||
if (isPartial) {
|
if (isPartial) {
|
||||||
return (
|
return (
|
||||||
@ -110,8 +122,8 @@ export default class StatusList extends ImmutablePureComponent {
|
|||||||
onClick={onLoadMore}
|
onClick={onLoadMore}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
<React.Fragment key={statusId}>
|
||||||
<StatusContainer
|
<StatusContainer
|
||||||
key={statusId}
|
|
||||||
id={statusId}
|
id={statusId}
|
||||||
onMoveUp={this.handleMoveUp}
|
onMoveUp={this.handleMoveUp}
|
||||||
onMoveDown={this.handleMoveDown}
|
onMoveDown={this.handleMoveDown}
|
||||||
@ -120,6 +132,16 @@ export default class StatusList extends ImmutablePureComponent {
|
|||||||
withGroupAdmin={withGroupAdmin}
|
withGroupAdmin={withGroupAdmin}
|
||||||
showThread
|
showThread
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{promotedStatus && index === promotion.position && (
|
||||||
|
<StatusContainer
|
||||||
|
id={promotion.status_id}
|
||||||
|
contextType={timelineId}
|
||||||
|
promoted
|
||||||
|
showThread
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
))
|
))
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
|
@ -3,9 +3,11 @@ import StatusList from '../../../components/status_list';
|
|||||||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import { me } from '../../../initial_state';
|
import { me, promotions } from '../../../initial_state';
|
||||||
import { dequeueTimeline } from 'gabsocial/actions/timelines';
|
import { dequeueTimeline } from 'gabsocial/actions/timelines';
|
||||||
import { scrollTopTimeline } from '../../../actions/timelines';
|
import { scrollTopTimeline } from '../../../actions/timelines';
|
||||||
|
import { sample } from 'lodash';
|
||||||
|
import { fetchStatus } from '../../../actions/statuses';
|
||||||
|
|
||||||
const makeGetStatusIds = () => createSelector([
|
const makeGetStatusIds = () => createSelector([
|
||||||
(state, { type, id }) => state.getIn(['settings', type], ImmutableMap()),
|
(state, { type, id }) => state.getIn(['settings', type], ImmutableMap()),
|
||||||
@ -32,6 +34,7 @@ const makeGetStatusIds = () => createSelector([
|
|||||||
|
|
||||||
const mapStateToProps = (state, {timelineId}) => {
|
const mapStateToProps = (state, {timelineId}) => {
|
||||||
const getStatusIds = makeGetStatusIds();
|
const getStatusIds = makeGetStatusIds();
|
||||||
|
const promotion = promotions.length > 0 && sample(promotions.filter(p => p.timeline_id === timelineId));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusIds: getStatusIds(state, { type: timelineId.substring(0,5) === 'group' ? 'group' : timelineId, id: timelineId }),
|
statusIds: getStatusIds(state, { type: timelineId.substring(0,5) === 'group' ? 'group' : timelineId, id: timelineId }),
|
||||||
@ -39,6 +42,8 @@ const mapStateToProps = (state, {timelineId}) => {
|
|||||||
isPartial: state.getIn(['timelines', timelineId, 'isPartial'], false),
|
isPartial: state.getIn(['timelines', timelineId, 'isPartial'], false),
|
||||||
hasMore: state.getIn(['timelines', timelineId, 'hasMore']),
|
hasMore: state.getIn(['timelines', timelineId, 'hasMore']),
|
||||||
totalQueuedItemsCount: state.getIn(['timelines', timelineId, 'totalQueuedItemsCount']),
|
totalQueuedItemsCount: state.getIn(['timelines', timelineId, 'totalQueuedItemsCount']),
|
||||||
|
promotion: promotion,
|
||||||
|
promotedStatus: promotion && state.getIn(['statuses', promotion.status_id])
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -52,6 +57,9 @@ const mapDispatchToProps = (dispatch, ownProps) => ({
|
|||||||
onScroll: debounce(() => {
|
onScroll: debounce(() => {
|
||||||
dispatch(scrollTopTimeline(ownProps.timelineId, false));
|
dispatch(scrollTopTimeline(ownProps.timelineId, false));
|
||||||
}, 100),
|
}, 100),
|
||||||
|
fetchStatus(id) {
|
||||||
|
dispatch(fetchStatus(id));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(StatusList);
|
export default connect(mapStateToProps, mapDispatchToProps)(StatusList);
|
||||||
|
@ -23,5 +23,6 @@ export const mascot = getMeta('mascot');
|
|||||||
export const profile_directory = getMeta('profile_directory');
|
export const profile_directory = getMeta('profile_directory');
|
||||||
export const isStaff = getMeta('is_staff');
|
export const isStaff = getMeta('is_staff');
|
||||||
export const forceSingleColumn = !getMeta('advanced_layout');
|
export const forceSingleColumn = !getMeta('advanced_layout');
|
||||||
|
export const promotions = initialState && initialState.promotions;
|
||||||
|
|
||||||
export default initialState;
|
export default initialState;
|
||||||
|
18
app/models/promotion.rb
Normal file
18
app/models/promotion.rb
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: promotions
|
||||||
|
#
|
||||||
|
# id :bigint(8) not null, primary key
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
# expires_at :datetime
|
||||||
|
# status_id :bigint(8) not null
|
||||||
|
# timeline_id :string
|
||||||
|
# position :integer default(10)
|
||||||
|
#
|
||||||
|
|
||||||
|
class Promotion < ApplicationRecord
|
||||||
|
belongs_to :status
|
||||||
|
|
||||||
|
scope :active, -> { where('expires_at > ?', [Time.now]) }
|
||||||
|
end
|
@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
class InitialStateSerializer < ActiveModel::Serializer
|
class InitialStateSerializer < ActiveModel::Serializer
|
||||||
attributes :meta, :compose, :accounts,
|
attributes :meta, :compose, :accounts,
|
||||||
:media_attachments, :settings
|
:media_attachments, :settings,
|
||||||
|
:promotions
|
||||||
|
|
||||||
has_one :push_subscription, serializer: REST::WebPushSubscriptionSerializer
|
has_one :push_subscription, serializer: REST::WebPushSubscriptionSerializer
|
||||||
|
|
||||||
@ -65,6 +66,10 @@ class InitialStateSerializer < ActiveModel::Serializer
|
|||||||
{ accept_content_types: MediaAttachment::IMAGE_FILE_EXTENSIONS + MediaAttachment::VIDEO_FILE_EXTENSIONS + MediaAttachment::IMAGE_MIME_TYPES + MediaAttachment::VIDEO_MIME_TYPES }
|
{ accept_content_types: MediaAttachment::IMAGE_FILE_EXTENSIONS + MediaAttachment::VIDEO_FILE_EXTENSIONS + MediaAttachment::IMAGE_MIME_TYPES + MediaAttachment::VIDEO_MIME_TYPES }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def promotions
|
||||||
|
ActiveModelSerializers::SerializableResource.new(Promotion.active, each_serializer: REST::PromotionSerializer)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def instance_presenter
|
def instance_presenter
|
||||||
|
9
app/serializers/rest/promotion_serializer.rb
Normal file
9
app/serializers/rest/promotion_serializer.rb
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class REST::PromotionSerializer < ActiveModel::Serializer
|
||||||
|
attributes :status_id, :timeline_id, :position
|
||||||
|
|
||||||
|
def status_id
|
||||||
|
object.status_id.to_s
|
||||||
|
end
|
||||||
|
end
|
8
app/views/settings/promotions/_promotion.html.haml
Normal file
8
app/views/settings/promotions/_promotion.html.haml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
%tr
|
||||||
|
%td= promotion.timeline_id
|
||||||
|
%td= promotion.status_id
|
||||||
|
%td= promotion.expires_at
|
||||||
|
%td= promotion.position
|
||||||
|
%td
|
||||||
|
= table_link_to 'pencil', t('promotions.edit'), edit_settings_promotion_path(promotion)
|
||||||
|
= table_link_to 'trash', t('promotions.delete'), settings_promotion_path(promotion), method: :delete, data: { confirm: t('settings.promotions.are_you_sure') }
|
14
app/views/settings/promotions/edit.html.haml
Normal file
14
app/views/settings/promotions/edit.html.haml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
- content_for :page_title do
|
||||||
|
= t('promotions.title')
|
||||||
|
|
||||||
|
= simple_form_for @promotion, url: settings_promotion_path(@promotion) do |f|
|
||||||
|
= render 'shared/error_messages', object: @promotion
|
||||||
|
|
||||||
|
.fields-group
|
||||||
|
= f.input :timeline_id, wrapper: :with_label, label: t('promotions.timeline_id')
|
||||||
|
= f.input :status_id, wrapper: :with_label, label: t('promotions.status_id')
|
||||||
|
= f.input :expires_at, as: :string, wrapper: :with_label, label: t('promotions.expires_at')
|
||||||
|
= f.input :position, wrapper: :with_label, label: t('promotions.position')
|
||||||
|
|
||||||
|
.actions
|
||||||
|
= f.button :button, t('generic.save_changes'), type: :submit
|
16
app/views/settings/promotions/index.html.haml
Normal file
16
app/views/settings/promotions/index.html.haml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
- content_for :page_title do
|
||||||
|
= t('promotions.title')
|
||||||
|
|
||||||
|
.table-wrapper
|
||||||
|
%table.table
|
||||||
|
%thead
|
||||||
|
%tr
|
||||||
|
%th= t('promotions.timeline_id')
|
||||||
|
%th= t('promotions.status_id')
|
||||||
|
%th= t('promotions.expires_at')
|
||||||
|
%th= t('promotions.position')
|
||||||
|
%th
|
||||||
|
%tbody
|
||||||
|
= render @promotions
|
||||||
|
|
||||||
|
= link_to t('promotions.create'), new_settings_promotion_path, class: 'button'
|
14
app/views/settings/promotions/new.html.haml
Normal file
14
app/views/settings/promotions/new.html.haml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
- content_for :page_title do
|
||||||
|
= t('.title')
|
||||||
|
|
||||||
|
= simple_form_for @promotion, url: settings_promotions_path do |f|
|
||||||
|
= render 'shared/error_messages', object: @promotion
|
||||||
|
|
||||||
|
.fields-group
|
||||||
|
= f.input :timeline_id, wrapper: :with_label, label: t('promotions.timeline_id')
|
||||||
|
= f.input :status_id, wrapper: :with_label, label: t('promotions.status_id')
|
||||||
|
= f.input :expires_at, as: :string, wrapper: :with_label, label: t('promotions.expires_at')
|
||||||
|
= f.input :position, wrapper: :with_label, label: t('promotions.position')
|
||||||
|
|
||||||
|
.actions
|
||||||
|
= f.button :button, t('.create'), type: :submit
|
@ -70,6 +70,8 @@ en:
|
|||||||
moderator: Mod
|
moderator: Mod
|
||||||
unavailable: Profile unavailable
|
unavailable: Profile unavailable
|
||||||
unfollow: Unfollow
|
unfollow: Unfollow
|
||||||
|
promotions:
|
||||||
|
title: Promotions
|
||||||
admin:
|
admin:
|
||||||
account_actions:
|
account_actions:
|
||||||
action: Perform action
|
action: Perform action
|
||||||
|
@ -55,6 +55,7 @@ SimpleNavigation::Configuration.run do |navigation|
|
|||||||
s.item :sidekiq, safe_join([fa_icon('diamond fw'), 'Sidekiq']), sidekiq_url, link_html: { target: 'sidekiq' }, if: -> { current_user.admin? }
|
s.item :sidekiq, safe_join([fa_icon('diamond fw'), 'Sidekiq']), sidekiq_url, link_html: { target: 'sidekiq' }, if: -> { current_user.admin? }
|
||||||
s.item :pghero, safe_join([fa_icon('database fw'), 'PgHero']), pghero_url, link_html: { target: 'pghero' }, if: -> { current_user.admin? }
|
s.item :pghero, safe_join([fa_icon('database fw'), 'PgHero']), pghero_url, link_html: { target: 'pghero' }, if: -> { current_user.admin? }
|
||||||
s.item :moderation, safe_join([fa_icon('id-card-o fw'), t('verifications.moderation.title')]), settings_verifications_moderation_url, if: -> { current_user.admin? }
|
s.item :moderation, safe_join([fa_icon('id-card-o fw'), t('verifications.moderation.title')]), settings_verifications_moderation_url, if: -> { current_user.admin? }
|
||||||
|
s.item :promotions, safe_join([fa_icon('star fw'), t('promotions.title')]), settings_promotions_url, if: -> { current_user.admin? }
|
||||||
end
|
end
|
||||||
|
|
||||||
n.item :logout, safe_join([fa_icon('sign-out fw'), t('auth.logout')]), destroy_user_session_url, link_html: { 'data-method' => 'delete' }
|
n.item :logout, safe_join([fa_icon('sign-out fw'), t('auth.logout')]), destroy_user_session_url, link_html: { 'data-method' => 'delete' }
|
||||||
|
@ -89,6 +89,8 @@ Rails.application.routes.draw do
|
|||||||
post '/btcpay-notification', to: 'upgrade#btcpay_notification', as: :btcpay_notification
|
post '/btcpay-notification', to: 'upgrade#btcpay_notification', as: :btcpay_notification
|
||||||
end
|
end
|
||||||
|
|
||||||
|
resources :promotions, only: [:index, :new, :create, :edit, :update, :destroy]
|
||||||
|
|
||||||
namespace :verifications do
|
namespace :verifications do
|
||||||
get :moderation, to: 'moderation#index', as: :moderation
|
get :moderation, to: 'moderation#index', as: :moderation
|
||||||
get 'moderation/:id/approve', to: 'moderation#approve', as: :approve
|
get 'moderation/:id/approve', to: 'moderation#approve', as: :approve
|
||||||
|
11
db/migrate/20190903162122_create_promotions.rb
Normal file
11
db/migrate/20190903162122_create_promotions.rb
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
class CreatePromotions < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
create_table :promotions do |t|
|
||||||
|
t.timestamps
|
||||||
|
t.datetime :expires_at, null: true
|
||||||
|
t.bigint :status_id, null: false
|
||||||
|
t.string :timeline_id, null: true
|
||||||
|
t.integer :position, default: 10
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
21
db/schema.rb
21
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: 2019_08_04_115634) do
|
ActiveRecord::Schema.define(version: 2019_09_03_162122) 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"
|
||||||
@ -93,7 +93,7 @@ ActiveRecord::Schema.define(version: 2019_08_04_115634) do
|
|||||||
t.bigint "account_id"
|
t.bigint "account_id"
|
||||||
t.string "image_file_name"
|
t.string "image_file_name"
|
||||||
t.string "image_content_type"
|
t.string "image_content_type"
|
||||||
t.bigint "image_file_size"
|
t.integer "image_file_size"
|
||||||
t.datetime "image_updated_at"
|
t.datetime "image_updated_at"
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
@ -157,10 +157,10 @@ ActiveRecord::Schema.define(version: 2019_08_04_115634) do
|
|||||||
t.string "actor_type"
|
t.string "actor_type"
|
||||||
t.boolean "discoverable"
|
t.boolean "discoverable"
|
||||||
t.string "also_known_as", array: true
|
t.string "also_known_as", array: true
|
||||||
t.datetime "silenced_at"
|
|
||||||
t.datetime "suspended_at"
|
|
||||||
t.boolean "is_pro", default: false, null: false
|
t.boolean "is_pro", default: false, null: false
|
||||||
t.datetime "pro_expires_at"
|
t.datetime "pro_expires_at"
|
||||||
|
t.datetime "silenced_at"
|
||||||
|
t.datetime "suspended_at"
|
||||||
t.boolean "is_verified", default: false, null: false
|
t.boolean "is_verified", default: false, null: false
|
||||||
t.boolean "is_donor", default: false, null: false
|
t.boolean "is_donor", default: false, null: false
|
||||||
t.boolean "is_investor", default: false, null: false
|
t.boolean "is_investor", default: false, null: false
|
||||||
@ -578,6 +578,15 @@ ActiveRecord::Schema.define(version: 2019_08_04_115634) do
|
|||||||
t.index ["status_id", "preview_card_id"], name: "index_preview_cards_statuses_on_status_id_and_preview_card_id"
|
t.index ["status_id", "preview_card_id"], name: "index_preview_cards_statuses_on_status_id_and_preview_card_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "promotions", force: :cascade do |t|
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.datetime "expires_at"
|
||||||
|
t.bigint "status_id", null: false
|
||||||
|
t.string "timeline_id"
|
||||||
|
t.integer "position", default: 10
|
||||||
|
end
|
||||||
|
|
||||||
create_table "relays", force: :cascade do |t|
|
create_table "relays", force: :cascade do |t|
|
||||||
t.string "inbox_url", default: "", null: false
|
t.string "inbox_url", default: "", null: false
|
||||||
t.string "follow_activity_id"
|
t.string "follow_activity_id"
|
||||||
@ -658,8 +667,8 @@ ActiveRecord::Schema.define(version: 2019_08_04_115634) do
|
|||||||
create_table "status_pins", force: :cascade do |t|
|
create_table "status_pins", force: :cascade do |t|
|
||||||
t.bigint "account_id", null: false
|
t.bigint "account_id", null: false
|
||||||
t.bigint "status_id", null: false
|
t.bigint "status_id", null: false
|
||||||
t.datetime "created_at", default: -> { "now()" }, null: false
|
t.datetime "created_at", default: -> { "CURRENT_TIMESTAMP" }, null: false
|
||||||
t.datetime "updated_at", default: -> { "now()" }, null: false
|
t.datetime "updated_at", default: -> { "CURRENT_TIMESTAMP" }, null: false
|
||||||
t.index ["account_id", "status_id"], name: "index_status_pins_on_account_id_and_status_id", unique: true
|
t.index ["account_id", "status_id"], name: "index_status_pins_on_account_id_and_status_id", unique: true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user