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' },
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_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_domain: { id: 'status.admin_domain', defaultMessage: 'Open moderation interface for {domain}' },
languages: { id: 'account.languages', defaultMessage: 'Change subscribed languages' },
@ -105,6 +106,7 @@ class Header extends ImmutablePureComponent {
onEndorseToggle: PropTypes.func.isRequired,
onAddToList: PropTypes.func.isRequired,
onAddToAntenna: PropTypes.func.isRequired,
onAddToCircle: PropTypes.func.isRequired,
onEditAccountNote: PropTypes.func.isRequired,
onChangeLanguages: 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_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);
if (account.getIn(['relationship', 'muting'])) {

View file

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

View file

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

View file

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

View file

@ -52,6 +52,10 @@ module AccountAssociations
has_many :antenna_accounts, inverse_of: :account, dependent: :destroy
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)
has_many :list_accounts, inverse_of: :account, dependent: :destroy
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 :lists, only: :index, controller: 'accounts/lists'
resources :antennas, only: :index, controller: 'accounts/antennas'
resources :circles, only: :index, controller: 'accounts/circles'
resources :identity_proofs, only: :index, controller: 'accounts/identity_proofs'
resources :featured_tags, only: :index, controller: 'accounts/featured_tags'