Add circle adder

This commit is contained in:
KMY 2023-08-22 15:41:22 +09:00
parent df0b1a4632
commit 9c4577ab7c
8 changed files with 89 additions and 46 deletions

View file

@ -0,0 +1,18 @@
# frozen_string_literal: true
class Api::V1::Accounts::CirclesController < Api::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:lists' }
before_action :require_user!
before_action :set_account
def index
@circles = @account.suspended? ? [] : @account.joined_circles.where(account: current_account)
render json: @circles, each_serializer: REST::CircleSerializer
end
private
def set_account
@account = Account.find(params[:account_id])
end
end

View file

@ -59,6 +59,7 @@ const messages = defineMessages({
unendorse: { id: 'account.unendorse', defaultMessage: 'Don\'t feature on profile' }, unendorse: { id: 'account.unendorse', defaultMessage: 'Don\'t feature on profile' },
add_or_remove_from_list: { id: 'account.add_or_remove_from_list', defaultMessage: 'Add or Remove from lists' }, add_or_remove_from_list: { id: 'account.add_or_remove_from_list', defaultMessage: 'Add or Remove from lists' },
add_or_remove_from_antenna: { id: 'account.add_or_remove_from_antenna', defaultMessage: 'Add or Remove from antennas' }, add_or_remove_from_antenna: { id: 'account.add_or_remove_from_antenna', defaultMessage: 'Add or Remove from antennas' },
add_or_remove_from_circle: { id: 'account.add_or_remove_from_circle', defaultMessage: 'Add or Remove from circles' },
admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' }, admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
admin_domain: { id: 'status.admin_domain', defaultMessage: 'Open moderation interface for {domain}' }, admin_domain: { id: 'status.admin_domain', defaultMessage: 'Open moderation interface for {domain}' },
languages: { id: 'account.languages', defaultMessage: 'Change subscribed languages' }, languages: { id: 'account.languages', defaultMessage: 'Change subscribed languages' },
@ -105,6 +106,7 @@ class Header extends ImmutablePureComponent {
onEndorseToggle: PropTypes.func.isRequired, onEndorseToggle: PropTypes.func.isRequired,
onAddToList: PropTypes.func.isRequired, onAddToList: PropTypes.func.isRequired,
onAddToAntenna: PropTypes.func.isRequired, onAddToAntenna: PropTypes.func.isRequired,
onAddToCircle: PropTypes.func.isRequired,
onEditAccountNote: PropTypes.func.isRequired, onEditAccountNote: PropTypes.func.isRequired,
onChangeLanguages: PropTypes.func.isRequired, onChangeLanguages: PropTypes.func.isRequired,
onInteractionModal: PropTypes.func.isRequired, onInteractionModal: PropTypes.func.isRequired,
@ -330,6 +332,9 @@ class Header extends ImmutablePureComponent {
menu.push({ text: intl.formatMessage(messages.add_or_remove_from_list), action: this.props.onAddToList }); menu.push({ text: intl.formatMessage(messages.add_or_remove_from_list), action: this.props.onAddToList });
} }
menu.push({ text: intl.formatMessage(messages.add_or_remove_from_antenna), action: this.props.onAddToAntenna }); menu.push({ text: intl.formatMessage(messages.add_or_remove_from_antenna), action: this.props.onAddToAntenna });
if (account.getIn(['relationship', 'followed_by'])) {
menu.push({ text: intl.formatMessage(messages.add_or_remove_from_circle), action: this.props.onAddToCircle });
}
menu.push(null); menu.push(null);
if (account.getIn(['relationship', 'muting'])) { if (account.getIn(['relationship', 'muting'])) {

View file

@ -28,6 +28,7 @@ export default class Header extends ImmutablePureComponent {
onEndorseToggle: PropTypes.func.isRequired, onEndorseToggle: PropTypes.func.isRequired,
onAddToList: PropTypes.func.isRequired, onAddToList: PropTypes.func.isRequired,
onAddToAntenna: PropTypes.func.isRequired, onAddToAntenna: PropTypes.func.isRequired,
onAddToCircle: PropTypes.func.isRequired,
onChangeLanguages: PropTypes.func.isRequired, onChangeLanguages: PropTypes.func.isRequired,
onInteractionModal: PropTypes.func.isRequired, onInteractionModal: PropTypes.func.isRequired,
onOpenAvatar: PropTypes.func.isRequired, onOpenAvatar: PropTypes.func.isRequired,
@ -101,6 +102,10 @@ export default class Header extends ImmutablePureComponent {
this.props.onAddToAntenna(this.props.account); this.props.onAddToAntenna(this.props.account);
}; };
handleAddToCircle = () => {
this.props.onAddToCircle(this.props.account);
};
handleEditAccountNote = () => { handleEditAccountNote = () => {
this.props.onEditAccountNote(this.props.account); this.props.onEditAccountNote(this.props.account);
}; };
@ -144,6 +149,7 @@ export default class Header extends ImmutablePureComponent {
onEndorseToggle={this.handleEndorseToggle} onEndorseToggle={this.handleEndorseToggle}
onAddToList={this.handleAddToList} onAddToList={this.handleAddToList}
onAddToAntenna={this.handleAddToAntenna} onAddToAntenna={this.handleAddToAntenna}
onAddToCircle={this.handleAddToCircle}
onEditAccountNote={this.handleEditAccountNote} onEditAccountNote={this.handleEditAccountNote}
onChangeLanguages={this.handleChangeLanguages} onChangeLanguages={this.handleChangeLanguages}
onInteractionModal={this.handleInteractionModal} onInteractionModal={this.handleInteractionModal}

View file

@ -173,6 +173,15 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
})); }));
}, },
onAddToCircle (account) {
dispatch(openModal({
modalType: 'CIRCLE_ADDER',
modalProps: {
accountId: account.get('id'),
},
}));
},
onChangeLanguages (account) { onChangeLanguages (account) {
dispatch(openModal({ dispatch(openModal({
modalType: 'SUBSCRIBED_LANGUAGES', modalType: 'SUBSCRIBED_LANGUAGES',

View file

@ -1,47 +1,47 @@
import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
import { import {
LIST_ADDER_RESET, CIRCLE_ADDER_RESET,
LIST_ADDER_SETUP, CIRCLE_ADDER_SETUP,
LIST_ADDER_LISTS_FETCH_REQUEST, CIRCLE_ADDER_CIRCLES_FETCH_REQUEST,
LIST_ADDER_LISTS_FETCH_SUCCESS, CIRCLE_ADDER_CIRCLES_FETCH_SUCCESS,
LIST_ADDER_LISTS_FETCH_FAIL, CIRCLE_ADDER_CIRCLES_FETCH_FAIL,
LIST_EDITOR_ADD_SUCCESS, CIRCLE_EDITOR_ADD_SUCCESS,
LIST_EDITOR_REMOVE_SUCCESS, CIRCLE_EDITOR_REMOVE_SUCCESS,
} from '../actions/lists'; } from '../actions/circles';
const initialState = ImmutableMap({ const initialState = ImmutableMap({
accountId: null, accountId: null,
lists: ImmutableMap({ circles: ImmutableMap({
items: ImmutableList(), items: ImmutableList(),
loaded: false, loaded: false,
isLoading: false, isLoading: false,
}), }),
}); });
export default function listAdderReducer(state = initialState, action) { export default function circleAdderReducer(state = initialState, action) {
switch(action.type) { switch(action.type) {
case LIST_ADDER_RESET: case CIRCLE_ADDER_RESET:
return initialState; return initialState;
case LIST_ADDER_SETUP: case CIRCLE_ADDER_SETUP:
return state.withMutations(map => { return state.withMutations(map => {
map.set('accountId', action.account.get('id')); map.set('accountId', action.account.get('id'));
}); });
case LIST_ADDER_LISTS_FETCH_REQUEST: case CIRCLE_ADDER_CIRCLES_FETCH_REQUEST:
return state.setIn(['lists', 'isLoading'], true); return state.setIn(['circles', 'isLoading'], true);
case LIST_ADDER_LISTS_FETCH_FAIL: case CIRCLE_ADDER_CIRCLES_FETCH_FAIL:
return state.setIn(['lists', 'isLoading'], false); return state.setIn(['circles', 'isLoading'], false);
case LIST_ADDER_LISTS_FETCH_SUCCESS: case CIRCLE_ADDER_CIRCLES_FETCH_SUCCESS:
return state.update('lists', lists => lists.withMutations(map => { return state.update('circles', circles => circles.withMutations(map => {
map.set('isLoading', false); map.set('isLoading', false);
map.set('loaded', true); map.set('loaded', true);
map.set('items', ImmutableList(action.lists.map(item => item.id))); map.set('items', ImmutableList(action.circles.map(item => item.id)));
})); }));
case LIST_EDITOR_ADD_SUCCESS: case CIRCLE_EDITOR_ADD_SUCCESS:
return state.updateIn(['lists', 'items'], list => list.unshift(action.listId)); return state.updateIn(['circles', 'items'], circle => circle.unshift(action.circleId));
case LIST_EDITOR_REMOVE_SUCCESS: case CIRCLE_EDITOR_REMOVE_SUCCESS:
return state.updateIn(['lists', 'items'], list => list.filterNot(item => item === action.listId)); return state.updateIn(['circles', 'items'], circle => circle.filterNot(item => item === action.circleId));
default: default:
return state; return state;
} }

View file

@ -1,47 +1,47 @@
import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
import { import {
CIRCLE_ADDER_RESET, LIST_ADDER_RESET,
CIRCLE_ADDER_SETUP, LIST_ADDER_SETUP,
CIRCLE_ADDER_CIRCLES_FETCH_REQUEST, LIST_ADDER_LISTS_FETCH_REQUEST,
CIRCLE_ADDER_CIRCLES_FETCH_SUCCESS, LIST_ADDER_LISTS_FETCH_SUCCESS,
CIRCLE_ADDER_CIRCLES_FETCH_FAIL, LIST_ADDER_LISTS_FETCH_FAIL,
CIRCLE_EDITOR_ADD_SUCCESS, LIST_EDITOR_ADD_SUCCESS,
CIRCLE_EDITOR_REMOVE_SUCCESS, LIST_EDITOR_REMOVE_SUCCESS,
} from '../actions/circles'; } from '../actions/lists';
const initialState = ImmutableMap({ const initialState = ImmutableMap({
accountId: null, accountId: null,
circles: ImmutableMap({ lists: ImmutableMap({
items: ImmutableList(), items: ImmutableList(),
loaded: false, loaded: false,
isLoading: false, isLoading: false,
}), }),
}); });
export default function circleAdderReducer(state = initialState, action) { export default function listAdderReducer(state = initialState, action) {
switch(action.type) { switch(action.type) {
case CIRCLE_ADDER_RESET: case LIST_ADDER_RESET:
return initialState; return initialState;
case CIRCLE_ADDER_SETUP: case LIST_ADDER_SETUP:
return state.withMutations(map => { return state.withMutations(map => {
map.set('accountId', action.account.get('id')); map.set('accountId', action.account.get('id'));
}); });
case CIRCLE_ADDER_CIRCLES_FETCH_REQUEST: case LIST_ADDER_LISTS_FETCH_REQUEST:
return state.setIn(['circles', 'isLoading'], true); return state.setIn(['lists', 'isLoading'], true);
case CIRCLE_ADDER_CIRCLES_FETCH_FAIL: case LIST_ADDER_LISTS_FETCH_FAIL:
return state.setIn(['circles', 'isLoading'], false); return state.setIn(['lists', 'isLoading'], false);
case CIRCLE_ADDER_CIRCLES_FETCH_SUCCESS: case LIST_ADDER_LISTS_FETCH_SUCCESS:
return state.update('circles', circles => circles.withMutations(map => { return state.update('lists', lists => lists.withMutations(map => {
map.set('isLoading', false); map.set('isLoading', false);
map.set('loaded', true); map.set('loaded', true);
map.set('items', ImmutableList(action.circles.map(item => item.id))); map.set('items', ImmutableList(action.lists.map(item => item.id)));
})); }));
case CIRCLE_EDITOR_ADD_SUCCESS: case LIST_EDITOR_ADD_SUCCESS:
return state.updateIn(['circles', 'items'], circle => circle.unshift(action.circleId)); return state.updateIn(['lists', 'items'], list => list.unshift(action.listId));
case CIRCLE_EDITOR_REMOVE_SUCCESS: case LIST_EDITOR_REMOVE_SUCCESS:
return state.updateIn(['circles', 'items'], circle => circle.filterNot(item => item === action.circleId)); return state.updateIn(['lists', 'items'], list => list.filterNot(item => item === action.listId));
default: default:
return state; return state;
} }

View file

@ -52,6 +52,10 @@ module AccountAssociations
has_many :antenna_accounts, inverse_of: :account, dependent: :destroy has_many :antenna_accounts, inverse_of: :account, dependent: :destroy
has_many :joined_antennas, class_name: 'Antenna', through: :antenna_accounts, source: :antenna has_many :joined_antennas, class_name: 'Antenna', through: :antenna_accounts, source: :antenna
# Circles (that the account is on, not owned by the account)
has_many :circle_accounts, inverse_of: :account, dependent: :destroy
has_many :joined_circles, class_name: 'Circle', through: :circle_accounts, source: :circle
# Lists (that the account is on, not owned by the account) # Lists (that the account is on, not owned by the account)
has_many :list_accounts, inverse_of: :account, dependent: :destroy has_many :list_accounts, inverse_of: :account, dependent: :destroy
has_many :lists, through: :list_accounts has_many :lists, through: :list_accounts

View file

@ -180,6 +180,7 @@ namespace :api, format: false do
resources :following, only: :index, controller: 'accounts/following_accounts' resources :following, only: :index, controller: 'accounts/following_accounts'
resources :lists, only: :index, controller: 'accounts/lists' resources :lists, only: :index, controller: 'accounts/lists'
resources :antennas, only: :index, controller: 'accounts/antennas' resources :antennas, only: :index, controller: 'accounts/antennas'
resources :circles, only: :index, controller: 'accounts/circles'
resources :identity_proofs, only: :index, controller: 'accounts/identity_proofs' resources :identity_proofs, only: :index, controller: 'accounts/identity_proofs'
resources :featured_tags, only: :index, controller: 'accounts/featured_tags' resources :featured_tags, only: :index, controller: 'accounts/featured_tags'