Add unread indicator to conversations (#9009)
This commit is contained in:
		
							parent
							
								
									bebe8ec887
								
							
						
					
					
						commit
						a38a452481
					
				
					 13 changed files with 98 additions and 11 deletions
				
			
		| 
						 | 
					@ -3,9 +3,11 @@
 | 
				
			||||||
class Api::V1::ConversationsController < Api::BaseController
 | 
					class Api::V1::ConversationsController < Api::BaseController
 | 
				
			||||||
  LIMIT = 20
 | 
					  LIMIT = 20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  before_action -> { doorkeeper_authorize! :read, :'read:statuses' }
 | 
					  before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: :index
 | 
				
			||||||
 | 
					  before_action -> { doorkeeper_authorize! :write, :'write:conversations' }, except: :index
 | 
				
			||||||
  before_action :require_user!
 | 
					  before_action :require_user!
 | 
				
			||||||
  after_action :insert_pagination_headers
 | 
					  before_action :set_conversation, except: :index
 | 
				
			||||||
 | 
					  after_action :insert_pagination_headers, only: :index
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  respond_to :json
 | 
					  respond_to :json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,8 +16,22 @@ class Api::V1::ConversationsController < Api::BaseController
 | 
				
			||||||
    render json: @conversations, each_serializer: REST::ConversationSerializer
 | 
					    render json: @conversations, each_serializer: REST::ConversationSerializer
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def read
 | 
				
			||||||
 | 
					    @conversation.update!(unread: false)
 | 
				
			||||||
 | 
					    render json: @conversation, serializer: REST::ConversationSerializer
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def destroy
 | 
				
			||||||
 | 
					    @conversation.destroy!
 | 
				
			||||||
 | 
					    render_empty
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def set_conversation
 | 
				
			||||||
 | 
					    @conversation = AccountConversation.where(account: current_account).find(params[:id])
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def paginated_conversations
 | 
					  def paginated_conversations
 | 
				
			||||||
    AccountConversation.where(account: current_account)
 | 
					    AccountConversation.where(account: current_account)
 | 
				
			||||||
                       .paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
 | 
					                       .paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,6 @@
 | 
				
			||||||
# frozen_string_literal: true
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Api::V1::ReportsController < Api::BaseController
 | 
					class Api::V1::ReportsController < Api::BaseController
 | 
				
			||||||
  before_action -> { doorkeeper_authorize! :read, :'read:reports' }, except: [:create]
 | 
					 | 
				
			||||||
  before_action -> { doorkeeper_authorize! :write, :'write:reports' }, only: [:create]
 | 
					  before_action -> { doorkeeper_authorize! :write, :'write:reports' }, only: [:create]
 | 
				
			||||||
  before_action :require_user!
 | 
					  before_action :require_user!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,6 +13,8 @@ export const CONVERSATIONS_FETCH_SUCCESS = 'CONVERSATIONS_FETCH_SUCCESS';
 | 
				
			||||||
export const CONVERSATIONS_FETCH_FAIL    = 'CONVERSATIONS_FETCH_FAIL';
 | 
					export const CONVERSATIONS_FETCH_FAIL    = 'CONVERSATIONS_FETCH_FAIL';
 | 
				
			||||||
export const CONVERSATIONS_UPDATE        = 'CONVERSATIONS_UPDATE';
 | 
					export const CONVERSATIONS_UPDATE        = 'CONVERSATIONS_UPDATE';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const CONVERSATIONS_READ = 'CONVERSATIONS_READ';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const mountConversations = () => ({
 | 
					export const mountConversations = () => ({
 | 
				
			||||||
  type: CONVERSATIONS_MOUNT,
 | 
					  type: CONVERSATIONS_MOUNT,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -21,6 +23,15 @@ export const unmountConversations = () => ({
 | 
				
			||||||
  type: CONVERSATIONS_UNMOUNT,
 | 
					  type: CONVERSATIONS_UNMOUNT,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const markConversationRead = conversationId => (dispatch, getState) => {
 | 
				
			||||||
 | 
					  dispatch({
 | 
				
			||||||
 | 
					    type: CONVERSATIONS_READ,
 | 
				
			||||||
 | 
					    id: conversationId,
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  api(getState).post(`/api/v1/conversations/${conversationId}/read`);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const expandConversations = ({ maxId } = {}) => (dispatch, getState) => {
 | 
					export const expandConversations = ({ maxId } = {}) => (dispatch, getState) => {
 | 
				
			||||||
  dispatch(expandConversationsRequest());
 | 
					  dispatch(expandConversationsRequest());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,7 @@ import DisplayName from '../../../components/display_name';
 | 
				
			||||||
import Avatar from '../../../components/avatar';
 | 
					import Avatar from '../../../components/avatar';
 | 
				
			||||||
import AttachmentList from '../../../components/attachment_list';
 | 
					import AttachmentList from '../../../components/attachment_list';
 | 
				
			||||||
import { HotKeys } from 'react-hotkeys';
 | 
					import { HotKeys } from 'react-hotkeys';
 | 
				
			||||||
 | 
					import classNames from 'classnames';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class Conversation extends ImmutablePureComponent {
 | 
					export default class Conversation extends ImmutablePureComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,8 +20,10 @@ export default class Conversation extends ImmutablePureComponent {
 | 
				
			||||||
    conversationId: PropTypes.string.isRequired,
 | 
					    conversationId: PropTypes.string.isRequired,
 | 
				
			||||||
    accounts: ImmutablePropTypes.list.isRequired,
 | 
					    accounts: ImmutablePropTypes.list.isRequired,
 | 
				
			||||||
    lastStatus: ImmutablePropTypes.map.isRequired,
 | 
					    lastStatus: ImmutablePropTypes.map.isRequired,
 | 
				
			||||||
 | 
					    unread:PropTypes.bool.isRequired,
 | 
				
			||||||
    onMoveUp: PropTypes.func,
 | 
					    onMoveUp: PropTypes.func,
 | 
				
			||||||
    onMoveDown: PropTypes.func,
 | 
					    onMoveDown: PropTypes.func,
 | 
				
			||||||
 | 
					    markRead: PropTypes.func.isRequired,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleClick = () => {
 | 
					  handleClick = () => {
 | 
				
			||||||
| 
						 | 
					@ -28,7 +31,12 @@ export default class Conversation extends ImmutablePureComponent {
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const { lastStatus } = this.props;
 | 
					    const { lastStatus, unread, markRead } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (unread) {
 | 
				
			||||||
 | 
					      markRead();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.context.router.history.push(`/statuses/${lastStatus.get('id')}`);
 | 
					    this.context.router.history.push(`/statuses/${lastStatus.get('id')}`);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,7 +49,7 @@ export default class Conversation extends ImmutablePureComponent {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render () {
 | 
					  render () {
 | 
				
			||||||
    const { accounts, lastStatus, lastAccount } = this.props;
 | 
					    const { accounts, lastStatus, lastAccount, unread } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (lastStatus === null) {
 | 
					    if (lastStatus === null) {
 | 
				
			||||||
      return null;
 | 
					      return null;
 | 
				
			||||||
| 
						 | 
					@ -61,7 +69,7 @@ export default class Conversation extends ImmutablePureComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <HotKeys handlers={handlers}>
 | 
					      <HotKeys handlers={handlers}>
 | 
				
			||||||
        <div className='conversation focusable' tabIndex='0' onClick={this.handleClick} role='button'>
 | 
					        <div className={classNames('conversation', 'focusable', { 'conversation--unread': unread })} tabIndex='0' onClick={this.handleClick} role='button'>
 | 
				
			||||||
          <div className='conversation__header'>
 | 
					          <div className='conversation__header'>
 | 
				
			||||||
            <div className='conversation__avatars'>
 | 
					            <div className='conversation__avatars'>
 | 
				
			||||||
              <div>{accounts.map(account => <Avatar key={account.get('id')} size={36} account={account} />)}</div>
 | 
					              <div>{accounts.map(account => <Avatar key={account.get('id')} size={36} account={account} />)}</div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
import { connect } from 'react-redux';
 | 
					import { connect } from 'react-redux';
 | 
				
			||||||
import Conversation from '../components/conversation';
 | 
					import Conversation from '../components/conversation';
 | 
				
			||||||
 | 
					import { markConversationRead } from '../../../actions/conversations';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const mapStateToProps = (state, { conversationId }) => {
 | 
					const mapStateToProps = (state, { conversationId }) => {
 | 
				
			||||||
  const conversation = state.getIn(['conversations', 'items']).find(x => x.get('id') === conversationId);
 | 
					  const conversation = state.getIn(['conversations', 'items']).find(x => x.get('id') === conversationId);
 | 
				
			||||||
| 
						 | 
					@ -7,9 +8,14 @@ const mapStateToProps = (state, { conversationId }) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    accounts: conversation.get('accounts').map(accountId => state.getIn(['accounts', accountId], null)),
 | 
					    accounts: conversation.get('accounts').map(accountId => state.getIn(['accounts', accountId], null)),
 | 
				
			||||||
 | 
					    unread: conversation.get('unread'),
 | 
				
			||||||
    lastStatus,
 | 
					    lastStatus,
 | 
				
			||||||
    lastAccount: lastStatus === null ? null : state.getIn(['accounts', lastStatus.get('account')], null),
 | 
					    lastAccount: lastStatus === null ? null : state.getIn(['accounts', lastStatus.get('account')], null),
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default connect(mapStateToProps)(Conversation);
 | 
					const mapDispatchToProps = (dispatch, { conversationId }) => ({
 | 
				
			||||||
 | 
					  markRead: () => dispatch(markConversationRead(conversationId)),
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default connect(mapStateToProps, mapDispatchToProps)(Conversation);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ import {
 | 
				
			||||||
  CONVERSATIONS_FETCH_SUCCESS,
 | 
					  CONVERSATIONS_FETCH_SUCCESS,
 | 
				
			||||||
  CONVERSATIONS_FETCH_FAIL,
 | 
					  CONVERSATIONS_FETCH_FAIL,
 | 
				
			||||||
  CONVERSATIONS_UPDATE,
 | 
					  CONVERSATIONS_UPDATE,
 | 
				
			||||||
 | 
					  CONVERSATIONS_READ,
 | 
				
			||||||
} from '../actions/conversations';
 | 
					} from '../actions/conversations';
 | 
				
			||||||
import compareId from '../compare_id';
 | 
					import compareId from '../compare_id';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,6 +19,7 @@ const initialState = ImmutableMap({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const conversationToMap = item => ImmutableMap({
 | 
					const conversationToMap = item => ImmutableMap({
 | 
				
			||||||
  id: item.id,
 | 
					  id: item.id,
 | 
				
			||||||
 | 
					  unread: item.unread,
 | 
				
			||||||
  accounts: ImmutableList(item.accounts.map(a => a.id)),
 | 
					  accounts: ImmutableList(item.accounts.map(a => a.id)),
 | 
				
			||||||
  last_status: item.last_status.id,
 | 
					  last_status: item.last_status.id,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -80,6 +82,14 @@ export default function conversations(state = initialState, action) {
 | 
				
			||||||
    return state.update('mounted', count => count + 1);
 | 
					    return state.update('mounted', count => count + 1);
 | 
				
			||||||
  case CONVERSATIONS_UNMOUNT:
 | 
					  case CONVERSATIONS_UNMOUNT:
 | 
				
			||||||
    return state.update('mounted', count => count - 1);
 | 
					    return state.update('mounted', count => count - 1);
 | 
				
			||||||
 | 
					  case CONVERSATIONS_READ:
 | 
				
			||||||
 | 
					    return state.update('items', list => list.map(item => {
 | 
				
			||||||
 | 
					      if (item.get('id') === action.id) {
 | 
				
			||||||
 | 
					        return item.set('unread', false);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return item;
 | 
				
			||||||
 | 
					    }));
 | 
				
			||||||
  default:
 | 
					  default:
 | 
				
			||||||
    return state;
 | 
					    return state;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5503,6 +5503,11 @@ noscript {
 | 
				
			||||||
  border-bottom: 1px solid lighten($ui-base-color, 8%);
 | 
					  border-bottom: 1px solid lighten($ui-base-color, 8%);
 | 
				
			||||||
  cursor: pointer;
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &--unread {
 | 
				
			||||||
 | 
					    background: lighten($ui-base-color, 8%);
 | 
				
			||||||
 | 
					    border-bottom-color: lighten($ui-base-color, 12%);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &__header {
 | 
					  &__header {
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    margin-bottom: 15px;
 | 
					    margin-bottom: 15px;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,7 @@
 | 
				
			||||||
#  status_ids              :bigint(8)        default([]), not null, is an Array
 | 
					#  status_ids              :bigint(8)        default([]), not null, is an Array
 | 
				
			||||||
#  last_status_id          :bigint(8)
 | 
					#  last_status_id          :bigint(8)
 | 
				
			||||||
#  lock_version            :integer          default(0), not null
 | 
					#  lock_version            :integer          default(0), not null
 | 
				
			||||||
 | 
					#  unread                  :boolean          default(FALSE), not null
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AccountConversation < ApplicationRecord
 | 
					class AccountConversation < ApplicationRecord
 | 
				
			||||||
| 
						 | 
					@ -58,6 +59,7 @@ class AccountConversation < ApplicationRecord
 | 
				
			||||||
    def add_status(recipient, status)
 | 
					    def add_status(recipient, status)
 | 
				
			||||||
      conversation = find_or_initialize_by(account: recipient, conversation_id: status.conversation_id, participant_account_ids: participants_from_status(recipient, status))
 | 
					      conversation = find_or_initialize_by(account: recipient, conversation_id: status.conversation_id, participant_account_ids: participants_from_status(recipient, status))
 | 
				
			||||||
      conversation.status_ids << status.id
 | 
					      conversation.status_ids << status.id
 | 
				
			||||||
 | 
					      conversation.unread = status.account_id != recipient.id
 | 
				
			||||||
      conversation.save
 | 
					      conversation.save
 | 
				
			||||||
      conversation
 | 
					      conversation
 | 
				
			||||||
    rescue ActiveRecord::StaleObjectError
 | 
					    rescue ActiveRecord::StaleObjectError
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,8 @@
 | 
				
			||||||
# frozen_string_literal: true
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class REST::ConversationSerializer < ActiveModel::Serializer
 | 
					class REST::ConversationSerializer < ActiveModel::Serializer
 | 
				
			||||||
  attribute :id
 | 
					  attributes :id, :unread
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  has_many :participant_accounts, key: :accounts, serializer: REST::AccountSerializer
 | 
					  has_many :participant_accounts, key: :accounts, serializer: REST::AccountSerializer
 | 
				
			||||||
  has_one :last_status, serializer: REST::StatusSerializer
 | 
					  has_one :last_status, serializer: REST::StatusSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -58,6 +58,7 @@ Doorkeeper.configure do
 | 
				
			||||||
  optional_scopes :write,
 | 
					  optional_scopes :write,
 | 
				
			||||||
                  :'write:accounts',
 | 
					                  :'write:accounts',
 | 
				
			||||||
                  :'write:blocks',
 | 
					                  :'write:blocks',
 | 
				
			||||||
 | 
					                  :'write:conversations',
 | 
				
			||||||
                  :'write:favourites',
 | 
					                  :'write:favourites',
 | 
				
			||||||
                  :'write:filters',
 | 
					                  :'write:filters',
 | 
				
			||||||
                  :'write:follows',
 | 
					                  :'write:follows',
 | 
				
			||||||
| 
						 | 
					@ -76,7 +77,6 @@ Doorkeeper.configure do
 | 
				
			||||||
                  :'read:lists',
 | 
					                  :'read:lists',
 | 
				
			||||||
                  :'read:mutes',
 | 
					                  :'read:mutes',
 | 
				
			||||||
                  :'read:notifications',
 | 
					                  :'read:notifications',
 | 
				
			||||||
                  :'read:reports',
 | 
					 | 
				
			||||||
                  :'read:search',
 | 
					                  :'read:search',
 | 
				
			||||||
                  :'read:statuses',
 | 
					                  :'read:statuses',
 | 
				
			||||||
                  :follow,
 | 
					                  :follow,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -261,7 +261,12 @@ Rails.application.routes.draw do
 | 
				
			||||||
      resources :streaming, only: [:index]
 | 
					      resources :streaming, only: [:index]
 | 
				
			||||||
      resources :custom_emojis, only: [:index]
 | 
					      resources :custom_emojis, only: [:index]
 | 
				
			||||||
      resources :suggestions, only: [:index, :destroy]
 | 
					      resources :suggestions, only: [:index, :destroy]
 | 
				
			||||||
      resources :conversations, only: [:index]
 | 
					
 | 
				
			||||||
 | 
					      resources :conversations, only: [:index, :destroy] do
 | 
				
			||||||
 | 
					        member do
 | 
				
			||||||
 | 
					          post :read
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      get '/search', to: 'search#index', as: :search
 | 
					      get '/search', to: 'search#index', as: :search
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,23 @@
 | 
				
			||||||
 | 
					require Rails.root.join('lib', 'mastodon', 'migration_helpers')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AddUnreadToAccountConversations < ActiveRecord::Migration[5.2]
 | 
				
			||||||
 | 
					  include Mastodon::MigrationHelpers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  disable_ddl_transaction!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def up
 | 
				
			||||||
 | 
					    safety_assured do
 | 
				
			||||||
 | 
					      add_column_with_default(
 | 
				
			||||||
 | 
					        :account_conversations,
 | 
				
			||||||
 | 
					        :unread,
 | 
				
			||||||
 | 
					        :boolean,
 | 
				
			||||||
 | 
					        allow_null: false,
 | 
				
			||||||
 | 
					        default: false
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def down
 | 
				
			||||||
 | 
					    remove_column :account_conversations, :unread, :boolean
 | 
				
			||||||
 | 
					  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_10_10_141500) do
 | 
					ActiveRecord::Schema.define(version: 2018_10_18_205649) 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"
 | 
				
			||||||
| 
						 | 
					@ -22,6 +22,7 @@ ActiveRecord::Schema.define(version: 2018_10_10_141500) do
 | 
				
			||||||
    t.bigint "status_ids", default: [], null: false, array: true
 | 
					    t.bigint "status_ids", default: [], null: false, array: true
 | 
				
			||||||
    t.bigint "last_status_id"
 | 
					    t.bigint "last_status_id"
 | 
				
			||||||
    t.integer "lock_version", default: 0, null: false
 | 
					    t.integer "lock_version", default: 0, null: false
 | 
				
			||||||
 | 
					    t.boolean "unread", default: false, null: false
 | 
				
			||||||
    t.index ["account_id", "conversation_id", "participant_account_ids"], name: "index_unique_conversations", unique: true
 | 
					    t.index ["account_id", "conversation_id", "participant_account_ids"], name: "index_unique_conversations", unique: true
 | 
				
			||||||
    t.index ["account_id"], name: "index_account_conversations_on_account_id"
 | 
					    t.index ["account_id"], name: "index_account_conversations_on_account_id"
 | 
				
			||||||
    t.index ["conversation_id"], name: "index_account_conversations_on_conversation_id"
 | 
					    t.index ["conversation_id"], name: "index_account_conversations_on_conversation_id"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue