Add account subscribe support to WebUI
This commit is contained in:
parent
3d6eaf638d
commit
9fdfc2d8d5
50 changed files with 658 additions and 85 deletions
|
@ -37,6 +37,7 @@ class AccountsIndex < Chewy::Index
|
||||||
|
|
||||||
field :following_count, type: 'long', value: ->(account) { account.following.local.count }
|
field :following_count, type: 'long', value: ->(account) { account.following.local.count }
|
||||||
field :followers_count, type: 'long', value: ->(account) { account.followers.local.count }
|
field :followers_count, type: 'long', value: ->(account) { account.followers.local.count }
|
||||||
|
field :subscribing_count, type: 'long', value: ->(account) { account.subscribing.local.count }
|
||||||
field :last_status_at, type: 'date', value: ->(account) { account.last_status_at || account.created_at }
|
field :last_status_at, type: 'date', value: ->(account) { account.last_status_at || account.created_at }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Api::V1::AccountSubscribesController < Api::BaseController
|
|
||||||
before_action -> { doorkeeper_authorize! :read, :'read:follows' }, only: [:index, :show]
|
|
||||||
before_action -> { doorkeeper_authorize! :write, :'write:follows' }, except: [:index, :show]
|
|
||||||
|
|
||||||
before_action :require_user!
|
|
||||||
before_action :set_account_subscribe, except: [:index, :create]
|
|
||||||
|
|
||||||
def index
|
|
||||||
@account_subscribes = AccountSubscribe.where(account: current_account).all
|
|
||||||
render json: @account_subscribes, each_serializer: REST::AccountSubscribeSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def show
|
|
||||||
render json: @account_subscribe, serializer: REST::AccountSubscribeSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
@account_subscribe = AccountSubscribe.create!(account_subscribe_params.merge(account: current_account))
|
|
||||||
render json: @account_subscribe, serializer: REST::AccountSubscribeSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
@account_subscribe.update!(account_subscribe_params)
|
|
||||||
render json: @account_subscribe, serializer: REST::AccountSubscribeSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
@account_subscribe.destroy!
|
|
||||||
render_empty
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_account_subscribe
|
|
||||||
@account_subscribe = AccountSubscribe.where(account: current_account).find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def account_subscribe_params
|
|
||||||
params.permit(:acct)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::V1::Accounts::SubscribingAccountsController < Api::BaseController
|
||||||
|
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }
|
||||||
|
before_action :require_user!
|
||||||
|
after_action :insert_pagination_headers
|
||||||
|
|
||||||
|
respond_to :json
|
||||||
|
|
||||||
|
def index
|
||||||
|
@accounts = load_accounts
|
||||||
|
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def load_accounts
|
||||||
|
default_accounts.merge(paginated_subscribings).to_a
|
||||||
|
end
|
||||||
|
|
||||||
|
def default_accounts
|
||||||
|
Account.includes(:passive_subscribes, :account_stat).references(:passive_subscribes)
|
||||||
|
end
|
||||||
|
|
||||||
|
def paginated_subscribings
|
||||||
|
AccountSubscribe.where(account_id: current_user.account_id).paginate_by_max_id(
|
||||||
|
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||||
|
params[:max_id],
|
||||||
|
params[:since_id]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def insert_pagination_headers
|
||||||
|
set_pagination_headers(next_path, prev_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def next_path
|
||||||
|
if records_continue?
|
||||||
|
api_v1_accounts_subscribing_index_url pagination_params(max_id: pagination_max_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def prev_path
|
||||||
|
unless @accounts.empty?
|
||||||
|
api_v1_accounts_subscribing_index_url pagination_params(since_id: pagination_since_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_max_id
|
||||||
|
@accounts.last.passive_subscribes.first.id
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_since_id
|
||||||
|
@accounts.first.passive_subscribes.first.id
|
||||||
|
end
|
||||||
|
|
||||||
|
def records_continue?
|
||||||
|
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_params(core_params)
|
||||||
|
params.slice(:limit).permit(:limit).merge(core_params)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,8 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::V1::AccountsController < Api::BaseController
|
class Api::V1::AccountsController < Api::BaseController
|
||||||
before_action -> { authorize_if_got_token! :read, :'read:accounts' }, except: [:create, :follow, :unfollow, :block, :unblock, :mute, :unmute]
|
before_action -> { authorize_if_got_token! :read, :'read:accounts' }, except: [:create, :follow, :unfollow, :subscribe, :unsubscribe, :block, :unblock, :mute, :unmute]
|
||||||
before_action -> { doorkeeper_authorize! :follow, :'write:follows' }, only: [:follow, :unfollow]
|
before_action -> { doorkeeper_authorize! :follow, :'write:follows' }, only: [:follow, :unfollow, :subscribe, :unsubscribe]
|
||||||
before_action -> { doorkeeper_authorize! :follow, :'write:mutes' }, only: [:mute, :unmute]
|
before_action -> { doorkeeper_authorize! :follow, :'write:mutes' }, only: [:mute, :unmute]
|
||||||
before_action -> { doorkeeper_authorize! :follow, :'write:blocks' }, only: [:block, :unblock]
|
before_action -> { doorkeeper_authorize! :follow, :'write:blocks' }, only: [:block, :unblock]
|
||||||
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:create]
|
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:create]
|
||||||
|
@ -38,6 +38,11 @@ class Api::V1::AccountsController < Api::BaseController
|
||||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships(**options)
|
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships(**options)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def subscribe
|
||||||
|
AccountSubscribeService.new.call(current_user.account, @account)
|
||||||
|
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
|
||||||
|
end
|
||||||
|
|
||||||
def block
|
def block
|
||||||
BlockService.new.call(current_user.account, @account)
|
BlockService.new.call(current_user.account, @account)
|
||||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
|
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
|
||||||
|
@ -53,6 +58,11 @@ class Api::V1::AccountsController < Api::BaseController
|
||||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
|
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def unsubscribe
|
||||||
|
UnsubscribeAccountService.new.call(current_user.account, @account)
|
||||||
|
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
|
||||||
|
end
|
||||||
|
|
||||||
def unblock
|
def unblock
|
||||||
UnblockService.new.call(current_user.account, @account)
|
UnblockService.new.call(current_user.account, @account)
|
||||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
|
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
|
||||||
|
|
|
@ -37,6 +37,7 @@ class Settings::PreferencesController < Settings::BaseController
|
||||||
:setting_default_sensitive,
|
:setting_default_sensitive,
|
||||||
:setting_default_language,
|
:setting_default_language,
|
||||||
:setting_unfollow_modal,
|
:setting_unfollow_modal,
|
||||||
|
:setting_unsubscribe_modal,
|
||||||
:setting_boost_modal,
|
:setting_boost_modal,
|
||||||
:setting_delete_modal,
|
:setting_delete_modal,
|
||||||
:setting_auto_play_gif,
|
:setting_auto_play_gif,
|
||||||
|
|
|
@ -13,6 +13,14 @@ export const ACCOUNT_UNFOLLOW_REQUEST = 'ACCOUNT_UNFOLLOW_REQUEST';
|
||||||
export const ACCOUNT_UNFOLLOW_SUCCESS = 'ACCOUNT_UNFOLLOW_SUCCESS';
|
export const ACCOUNT_UNFOLLOW_SUCCESS = 'ACCOUNT_UNFOLLOW_SUCCESS';
|
||||||
export const ACCOUNT_UNFOLLOW_FAIL = 'ACCOUNT_UNFOLLOW_FAIL';
|
export const ACCOUNT_UNFOLLOW_FAIL = 'ACCOUNT_UNFOLLOW_FAIL';
|
||||||
|
|
||||||
|
export const ACCOUNT_SUBSCRIBE_REQUEST = 'ACCOUNT_SUBSCRIBE_REQUEST';
|
||||||
|
export const ACCOUNT_SUBSCRIBE_SUCCESS = 'ACCOUNT_SUBSCRIBE_SUCCESS';
|
||||||
|
export const ACCOUNT_SUBSCRIBE_FAIL = 'ACCOUNT_SUBSCRIBE_FAIL';
|
||||||
|
|
||||||
|
export const ACCOUNT_UNSUBSCRIBE_REQUEST = 'ACCOUNT_UNSUBSCRIBE_REQUEST';
|
||||||
|
export const ACCOUNT_UNSUBSCRIBE_SUCCESS = 'ACCOUNT_UNSUBSCRIBE_SUCCESS';
|
||||||
|
export const ACCOUNT_UNSUBSCRIBE_FAIL = 'ACCOUNT_UNSUBSCRIBE_FAIL';
|
||||||
|
|
||||||
export const ACCOUNT_BLOCK_REQUEST = 'ACCOUNT_BLOCK_REQUEST';
|
export const ACCOUNT_BLOCK_REQUEST = 'ACCOUNT_BLOCK_REQUEST';
|
||||||
export const ACCOUNT_BLOCK_SUCCESS = 'ACCOUNT_BLOCK_SUCCESS';
|
export const ACCOUNT_BLOCK_SUCCESS = 'ACCOUNT_BLOCK_SUCCESS';
|
||||||
export const ACCOUNT_BLOCK_FAIL = 'ACCOUNT_BLOCK_FAIL';
|
export const ACCOUNT_BLOCK_FAIL = 'ACCOUNT_BLOCK_FAIL';
|
||||||
|
@ -53,6 +61,14 @@ export const FOLLOWING_EXPAND_REQUEST = 'FOLLOWING_EXPAND_REQUEST';
|
||||||
export const FOLLOWING_EXPAND_SUCCESS = 'FOLLOWING_EXPAND_SUCCESS';
|
export const FOLLOWING_EXPAND_SUCCESS = 'FOLLOWING_EXPAND_SUCCESS';
|
||||||
export const FOLLOWING_EXPAND_FAIL = 'FOLLOWING_EXPAND_FAIL';
|
export const FOLLOWING_EXPAND_FAIL = 'FOLLOWING_EXPAND_FAIL';
|
||||||
|
|
||||||
|
export const SUBSCRIBING_FETCH_REQUEST = 'SUBSCRIBING_FETCH_REQUEST';
|
||||||
|
export const SUBSCRIBING_FETCH_SUCCESS = 'SUBSCRIBING_FETCH_SUCCESS';
|
||||||
|
export const SUBSCRIBING_FETCH_FAIL = 'SUBSCRIBING_FETCH_FAIL';
|
||||||
|
|
||||||
|
export const SUBSCRIBING_EXPAND_REQUEST = 'SUBSCRIBING_EXPAND_REQUEST';
|
||||||
|
export const SUBSCRIBING_EXPAND_SUCCESS = 'SUBSCRIBING_EXPAND_SUCCESS';
|
||||||
|
export const SUBSCRIBING_EXPAND_FAIL = 'SUBSCRIBING_EXPAND_FAIL';
|
||||||
|
|
||||||
export const RELATIONSHIPS_FETCH_REQUEST = 'RELATIONSHIPS_FETCH_REQUEST';
|
export const RELATIONSHIPS_FETCH_REQUEST = 'RELATIONSHIPS_FETCH_REQUEST';
|
||||||
export const RELATIONSHIPS_FETCH_SUCCESS = 'RELATIONSHIPS_FETCH_SUCCESS';
|
export const RELATIONSHIPS_FETCH_SUCCESS = 'RELATIONSHIPS_FETCH_SUCCESS';
|
||||||
export const RELATIONSHIPS_FETCH_FAIL = 'RELATIONSHIPS_FETCH_FAIL';
|
export const RELATIONSHIPS_FETCH_FAIL = 'RELATIONSHIPS_FETCH_FAIL';
|
||||||
|
@ -188,6 +204,85 @@ export function unfollowAccountFail(error) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function subscribeAccount(id, reblogs = true) {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
const alreadySubscribe = getState().getIn(['relationships', id, 'subscribing']);
|
||||||
|
const locked = getState().getIn(['accounts', id, 'locked'], false);
|
||||||
|
|
||||||
|
dispatch(subscribeAccountRequest(id, locked));
|
||||||
|
|
||||||
|
api(getState).post(`/api/v1/accounts/${id}/subscribe`).then(response => {
|
||||||
|
dispatch(subscribeAccountSuccess(response.data, alreadySubscribe));
|
||||||
|
}).catch(error => {
|
||||||
|
dispatch(subscribeAccountFail(error, locked));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function unsubscribeAccount(id) {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
dispatch(unsubscribeAccountRequest(id));
|
||||||
|
|
||||||
|
api(getState).post(`/api/v1/accounts/${id}/unsubscribe`).then(response => {
|
||||||
|
dispatch(unsubscribeAccountSuccess(response.data, getState().get('statuses')));
|
||||||
|
}).catch(error => {
|
||||||
|
dispatch(unsubscribeAccountFail(error));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function subscribeAccountRequest(id, locked) {
|
||||||
|
return {
|
||||||
|
type: ACCOUNT_SUBSCRIBE_REQUEST,
|
||||||
|
id,
|
||||||
|
locked,
|
||||||
|
skipLoading: true,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function subscribeAccountSuccess(relationship, alreadySubscribe) {
|
||||||
|
return {
|
||||||
|
type: ACCOUNT_SUBSCRIBE_SUCCESS,
|
||||||
|
relationship,
|
||||||
|
alreadySubscribe,
|
||||||
|
skipLoading: true,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function subscribeAccountFail(error, locked) {
|
||||||
|
return {
|
||||||
|
type: ACCOUNT_SUBSCRIBE_FAIL,
|
||||||
|
error,
|
||||||
|
locked,
|
||||||
|
skipLoading: true,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function unsubscribeAccountRequest(id) {
|
||||||
|
return {
|
||||||
|
type: ACCOUNT_UNSUBSCRIBE_REQUEST,
|
||||||
|
id,
|
||||||
|
skipLoading: true,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function unsubscribeAccountSuccess(relationship, statuses) {
|
||||||
|
return {
|
||||||
|
type: ACCOUNT_UNSUBSCRIBE_SUCCESS,
|
||||||
|
relationship,
|
||||||
|
statuses,
|
||||||
|
skipLoading: true,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function unsubscribeAccountFail(error) {
|
||||||
|
return {
|
||||||
|
type: ACCOUNT_UNSUBSCRIBE_FAIL,
|
||||||
|
error,
|
||||||
|
skipLoading: true,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export function blockAccount(id) {
|
export function blockAccount(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
dispatch(blockAccountRequest(id));
|
dispatch(blockAccountRequest(id));
|
||||||
|
@ -500,6 +595,92 @@ export function expandFollowingFail(id, error) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function fetchSubscribing(id) {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
dispatch(fetchSubscribeRequest(id));
|
||||||
|
|
||||||
|
api(getState).get(`/api/v1/accounts/subscribing`).then(response => {
|
||||||
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
|
|
||||||
|
dispatch(importFetchedAccounts(response.data));
|
||||||
|
dispatch(fetchSubscribeSuccess(id, response.data, next ? next.uri : null));
|
||||||
|
dispatch(fetchRelationships(response.data.map(item => item.id)));
|
||||||
|
}).catch(error => {
|
||||||
|
dispatch(fetchSubscribeFail(id, error));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function fetchSubscribeRequest(id) {
|
||||||
|
return {
|
||||||
|
type: SUBSCRIBING_FETCH_REQUEST,
|
||||||
|
id,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function fetchSubscribeSuccess(id, accounts, next) {
|
||||||
|
return {
|
||||||
|
type: SUBSCRIBING_FETCH_SUCCESS,
|
||||||
|
id,
|
||||||
|
accounts,
|
||||||
|
next,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function fetchSubscribeFail(id, error) {
|
||||||
|
return {
|
||||||
|
type: SUBSCRIBING_FETCH_FAIL,
|
||||||
|
id,
|
||||||
|
error,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function expandSubscribing(id) {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
const url = getState().getIn(['user_lists', 'subscribing', id, 'next']);
|
||||||
|
|
||||||
|
if (url === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(expandSubscribeRequest(id));
|
||||||
|
|
||||||
|
api(getState).get(url).then(response => {
|
||||||
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
|
|
||||||
|
dispatch(importFetchedAccounts(response.data));
|
||||||
|
dispatch(expandSubscribeSuccess(id, response.data, next ? next.uri : null));
|
||||||
|
dispatch(fetchRelationships(response.data.map(item => item.id)));
|
||||||
|
}).catch(error => {
|
||||||
|
dispatch(expandSubscribeFail(id, error));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function expandSubscribeRequest(id) {
|
||||||
|
return {
|
||||||
|
type: SUBSCRIBING_EXPAND_REQUEST,
|
||||||
|
id,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function expandSubscribeSuccess(id, accounts, next) {
|
||||||
|
return {
|
||||||
|
type: SUBSCRIBING_EXPAND_SUCCESS,
|
||||||
|
id,
|
||||||
|
accounts,
|
||||||
|
next,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function expandSubscribeFail(id, error) {
|
||||||
|
return {
|
||||||
|
type: SUBSCRIBING_EXPAND_FAIL,
|
||||||
|
id,
|
||||||
|
error,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export function fetchRelationships(accountIds) {
|
export function fetchRelationships(accountIds) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const loadedRelationships = getState().get('relationships');
|
const loadedRelationships = getState().get('relationships');
|
||||||
|
|
|
@ -13,6 +13,8 @@ import RelativeTimestamp from './relative_timestamp';
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
follow: { id: 'account.follow', defaultMessage: 'Follow' },
|
follow: { id: 'account.follow', defaultMessage: 'Follow' },
|
||||||
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
|
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
|
||||||
|
unsubscribe: { id: 'account.unsubscribe', defaultMessage: 'Unsubscribe' },
|
||||||
|
subscribe: { id: 'account.subscribe', defaultMessage: 'Subscribe' },
|
||||||
requested: { id: 'account.requested', defaultMessage: 'Awaiting approval' },
|
requested: { id: 'account.requested', defaultMessage: 'Awaiting approval' },
|
||||||
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
|
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
|
||||||
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
|
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
|
||||||
|
@ -26,6 +28,7 @@ class Account extends ImmutablePureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
account: ImmutablePropTypes.map.isRequired,
|
account: ImmutablePropTypes.map.isRequired,
|
||||||
onFollow: PropTypes.func.isRequired,
|
onFollow: PropTypes.func.isRequired,
|
||||||
|
onSubscribe: PropTypes.func.isRequired,
|
||||||
onBlock: PropTypes.func.isRequired,
|
onBlock: PropTypes.func.isRequired,
|
||||||
onMute: PropTypes.func.isRequired,
|
onMute: PropTypes.func.isRequired,
|
||||||
onMuteNotifications: PropTypes.func.isRequired,
|
onMuteNotifications: PropTypes.func.isRequired,
|
||||||
|
@ -40,6 +43,10 @@ class Account extends ImmutablePureComponent {
|
||||||
this.props.onFollow(this.props.account);
|
this.props.onFollow(this.props.account);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleSubscribe = () => {
|
||||||
|
this.props.onSubscribe(this.props.account);
|
||||||
|
}
|
||||||
|
|
||||||
handleBlock = () => {
|
handleBlock = () => {
|
||||||
this.props.onBlock(this.props.account);
|
this.props.onBlock(this.props.account);
|
||||||
}
|
}
|
||||||
|
@ -83,10 +90,11 @@ class Account extends ImmutablePureComponent {
|
||||||
buttons = <IconButton icon={actionIcon} title={actionTitle} onClick={this.handleAction} />;
|
buttons = <IconButton icon={actionIcon} title={actionTitle} onClick={this.handleAction} />;
|
||||||
}
|
}
|
||||||
} else if (account.get('id') !== me && account.get('relationship', null) !== null) {
|
} else if (account.get('id') !== me && account.get('relationship', null) !== null) {
|
||||||
const following = account.getIn(['relationship', 'following']);
|
const following = account.getIn(['relationship', 'following']);
|
||||||
const requested = account.getIn(['relationship', 'requested']);
|
const subscribing = account.getIn(['relationship', 'subscribing']);
|
||||||
const blocking = account.getIn(['relationship', 'blocking']);
|
const requested = account.getIn(['relationship', 'requested']);
|
||||||
const muting = account.getIn(['relationship', 'muting']);
|
const blocking = account.getIn(['relationship', 'blocking']);
|
||||||
|
const muting = account.getIn(['relationship', 'muting']);
|
||||||
|
|
||||||
if (requested) {
|
if (requested) {
|
||||||
buttons = <IconButton disabled icon='hourglass' title={intl.formatMessage(messages.requested)} />;
|
buttons = <IconButton disabled icon='hourglass' title={intl.formatMessage(messages.requested)} />;
|
||||||
|
@ -105,8 +113,15 @@ class Account extends ImmutablePureComponent {
|
||||||
{hidingNotificationsButton}
|
{hidingNotificationsButton}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
} else if (!account.get('moved') || following) {
|
} else {
|
||||||
buttons = <IconButton icon={following ? 'user-times' : 'user-plus'} title={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={this.handleFollow} active={following} />;
|
let following_buttons, subscribing_buttons;
|
||||||
|
if (!account.get('moved') || subscribing ) {
|
||||||
|
subscribing_buttons = <IconButton icon='rss-square' title={intl.formatMessage(subscribing ? messages.unsubscribe : messages.subscribe)} onClick={this.handleSubscribe} active={subscribing} />;
|
||||||
|
}
|
||||||
|
if (!account.get('moved') || following) {
|
||||||
|
following_buttons = <IconButton icon={following ? 'user-times' : 'user-plus'} title={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={this.handleFollow} active={following} />;
|
||||||
|
}
|
||||||
|
buttons = <span>{subscribing_buttons}{following_buttons}</span>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ import Account from '../components/account';
|
||||||
import {
|
import {
|
||||||
followAccount,
|
followAccount,
|
||||||
unfollowAccount,
|
unfollowAccount,
|
||||||
|
subscribeAccount,
|
||||||
|
unsubscribeAccount,
|
||||||
blockAccount,
|
blockAccount,
|
||||||
unblockAccount,
|
unblockAccount,
|
||||||
muteAccount,
|
muteAccount,
|
||||||
|
@ -13,10 +15,11 @@ import {
|
||||||
} from '../actions/accounts';
|
} from '../actions/accounts';
|
||||||
import { openModal } from '../actions/modal';
|
import { openModal } from '../actions/modal';
|
||||||
import { initMuteModal } from '../actions/mutes';
|
import { initMuteModal } from '../actions/mutes';
|
||||||
import { unfollowModal } from '../initial_state';
|
import { unfollowModal, unsubscribeModal } from '../initial_state';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' },
|
unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' },
|
||||||
|
unsubscribeConfirm: { id: 'confirmations.unsubscribe.confirm', defaultMessage: 'Unsubscribe' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const makeMapStateToProps = () => {
|
const makeMapStateToProps = () => {
|
||||||
|
@ -47,6 +50,22 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onSubscribe (account) {
|
||||||
|
if (account.getIn(['relationship', 'subscribing'])) {
|
||||||
|
if (unsubscribeModal) {
|
||||||
|
dispatch(openModal('CONFIRM', {
|
||||||
|
message: <FormattedMessage id='confirmations.unsubscribe.message' defaultMessage='Are you sure you want to unsubscribe {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
|
||||||
|
confirm: intl.formatMessage(messages.unsubscribeConfirm),
|
||||||
|
onConfirm: () => dispatch(unsubscribeAccount(account.get('id'))),
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
dispatch(unsubscribeAccount(account.get('id')));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dispatch(subscribeAccount(account.get('id')));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
onBlock (account) {
|
onBlock (account) {
|
||||||
if (account.getIn(['relationship', 'blocking'])) {
|
if (account.getIn(['relationship', 'blocking'])) {
|
||||||
dispatch(unblockAccount(account.get('id')));
|
dispatch(unblockAccount(account.get('id')));
|
||||||
|
|
|
@ -18,6 +18,8 @@ import AccountNoteContainer from '../containers/account_note_container';
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
|
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
|
||||||
follow: { id: 'account.follow', defaultMessage: 'Follow' },
|
follow: { id: 'account.follow', defaultMessage: 'Follow' },
|
||||||
|
unsubscribe: { id: 'account.unsubscribe', defaultMessage: 'Unsubscribe' },
|
||||||
|
subscribe: { id: 'account.subscribe', defaultMessage: 'Subscribe' },
|
||||||
cancel_follow_request: { id: 'account.cancel_follow_request', defaultMessage: 'Cancel follow request' },
|
cancel_follow_request: { id: 'account.cancel_follow_request', defaultMessage: 'Cancel follow request' },
|
||||||
requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' },
|
requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' },
|
||||||
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
|
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
|
||||||
|
@ -68,6 +70,7 @@ class Header extends ImmutablePureComponent {
|
||||||
account: ImmutablePropTypes.map,
|
account: ImmutablePropTypes.map,
|
||||||
identity_props: ImmutablePropTypes.list,
|
identity_props: ImmutablePropTypes.list,
|
||||||
onFollow: PropTypes.func.isRequired,
|
onFollow: PropTypes.func.isRequired,
|
||||||
|
onSubscribe: PropTypes.func.isRequired,
|
||||||
onBlock: PropTypes.func.isRequired,
|
onBlock: PropTypes.func.isRequired,
|
||||||
onMention: PropTypes.func.isRequired,
|
onMention: PropTypes.func.isRequired,
|
||||||
onDirect: PropTypes.func.isRequired,
|
onDirect: PropTypes.func.isRequired,
|
||||||
|
@ -260,6 +263,22 @@ class Header extends ImmutablePureComponent {
|
||||||
badge = null;
|
badge = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const following = account.getIn(['relationship', 'following']);
|
||||||
|
const subscribing = account.getIn(['relationship', 'subscribing']);
|
||||||
|
const blockd_by = account.getIn(['relationship', 'blocked_by']);
|
||||||
|
let buttons;
|
||||||
|
|
||||||
|
if(me !== account.get('id') && !blockd_by) {
|
||||||
|
let following_buttons, subscribing_buttons;
|
||||||
|
if(!account.get('moved') || subscribing) {
|
||||||
|
subscribing_buttons = <IconButton icon='rss-square' title={intl.formatMessage(subscribing ? messages.unsubscribe : messages.subscribe)} onClick={this.props.onSubscribe} active={subscribing} />;
|
||||||
|
}
|
||||||
|
if(!account.get('moved') || following) {
|
||||||
|
following_buttons = <IconButton icon={following ? 'user-times' : 'user-plus'} title={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={this.props.onFollow} active={following} />;
|
||||||
|
}
|
||||||
|
buttons = <span>{subscribing_buttons}{following_buttons}</span>
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames('account__header', { inactive: !!account.get('moved') })} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
|
<div className={classNames('account__header', { inactive: !!account.get('moved') })} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
|
||||||
<div className='account__header__image'>
|
<div className='account__header__image'>
|
||||||
|
@ -293,6 +312,9 @@ class Header extends ImmutablePureComponent {
|
||||||
<span dangerouslySetInnerHTML={displayNameHtml} /> {badge}
|
<span dangerouslySetInnerHTML={displayNameHtml} /> {badge}
|
||||||
<small>@{acct} {lockedIcon}</small>
|
<small>@{acct} {lockedIcon}</small>
|
||||||
</h1>
|
</h1>
|
||||||
|
<div className='account__header__tabs__name__relationship account__relationship'>
|
||||||
|
{buttons}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='account__header__extra'>
|
<div className='account__header__extra'>
|
||||||
|
@ -352,6 +374,12 @@ class Header extends ImmutablePureComponent {
|
||||||
renderer={counterRenderer('followers')}
|
renderer={counterRenderer('followers')}
|
||||||
/>
|
/>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|
||||||
|
{ (me === account.get('id')) && (
|
||||||
|
<NavLink exact activeClassName='active' to={`/accounts/${account.get('id')}/subscribing`} title={intl.formatNumber(account.get('subscribing_count'))}>
|
||||||
|
<strong>{shortNumberFormat(account.get('subscribing_count'))}</strong> <FormattedMessage id='account.subscribes' defaultMessage='Subscribes' />
|
||||||
|
</NavLink>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -13,6 +13,7 @@ export default class Header extends ImmutablePureComponent {
|
||||||
account: ImmutablePropTypes.map,
|
account: ImmutablePropTypes.map,
|
||||||
identity_proofs: ImmutablePropTypes.list,
|
identity_proofs: ImmutablePropTypes.list,
|
||||||
onFollow: PropTypes.func.isRequired,
|
onFollow: PropTypes.func.isRequired,
|
||||||
|
onSubscribe: PropTypes.func.isRequired,
|
||||||
onBlock: PropTypes.func.isRequired,
|
onBlock: PropTypes.func.isRequired,
|
||||||
onMention: PropTypes.func.isRequired,
|
onMention: PropTypes.func.isRequired,
|
||||||
onDirect: PropTypes.func.isRequired,
|
onDirect: PropTypes.func.isRequired,
|
||||||
|
@ -35,6 +36,10 @@ export default class Header extends ImmutablePureComponent {
|
||||||
this.props.onFollow(this.props.account);
|
this.props.onFollow(this.props.account);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleSubscribe = () => {
|
||||||
|
this.props.onSubscribe(this.props.account);
|
||||||
|
}
|
||||||
|
|
||||||
handleBlock = () => {
|
handleBlock = () => {
|
||||||
this.props.onBlock(this.props.account);
|
this.props.onBlock(this.props.account);
|
||||||
}
|
}
|
||||||
|
@ -106,6 +111,7 @@ export default class Header extends ImmutablePureComponent {
|
||||||
account={account}
|
account={account}
|
||||||
identity_proofs={identity_proofs}
|
identity_proofs={identity_proofs}
|
||||||
onFollow={this.handleFollow}
|
onFollow={this.handleFollow}
|
||||||
|
onSubscribe={this.handleSubscribe}
|
||||||
onBlock={this.handleBlock}
|
onBlock={this.handleBlock}
|
||||||
onMention={this.handleMention}
|
onMention={this.handleMention}
|
||||||
onDirect={this.handleDirect}
|
onDirect={this.handleDirect}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import Header from '../components/header';
|
||||||
import {
|
import {
|
||||||
followAccount,
|
followAccount,
|
||||||
unfollowAccount,
|
unfollowAccount,
|
||||||
|
subscribeAccount,
|
||||||
|
unsubscribeAccount,
|
||||||
unblockAccount,
|
unblockAccount,
|
||||||
unmuteAccount,
|
unmuteAccount,
|
||||||
pinAccount,
|
pinAccount,
|
||||||
|
@ -20,11 +22,12 @@ import { initReport } from '../../../actions/reports';
|
||||||
import { openModal } from '../../../actions/modal';
|
import { openModal } from '../../../actions/modal';
|
||||||
import { blockDomain, unblockDomain } from '../../../actions/domain_blocks';
|
import { blockDomain, unblockDomain } from '../../../actions/domain_blocks';
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import { unfollowModal } from '../../../initial_state';
|
import { unfollowModal, unsubscribeModal } from '../../../initial_state';
|
||||||
import { List as ImmutableList } from 'immutable';
|
import { List as ImmutableList } from 'immutable';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' },
|
unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' },
|
||||||
|
unsubscribeConfirm: { id: 'confirmations.unsubscribe.confirm', defaultMessage: 'Unsubscribe' },
|
||||||
blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
|
blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -58,6 +61,22 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onSubscribe (account) {
|
||||||
|
if (account.getIn(['relationship', 'subscribing'])) {
|
||||||
|
if (unsubscribeModal) {
|
||||||
|
dispatch(openModal('CONFIRM', {
|
||||||
|
message: <FormattedMessage id='confirmations.unsubscribe.message' defaultMessage='Are you sure you want to unsubscribe {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
|
||||||
|
confirm: intl.formatMessage(messages.unsubscribeConfirm),
|
||||||
|
onConfirm: () => dispatch(unsubscribeAccount(account.get('id'))),
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
dispatch(unsubscribeAccount(account.get('id')));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dispatch(subscribeAccount(account.get('id')));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
onBlock (account) {
|
onBlock (account) {
|
||||||
if (account.getIn(['relationship', 'blocking'])) {
|
if (account.getIn(['relationship', 'blocking'])) {
|
||||||
dispatch(unblockAccount(account.get('id')));
|
dispatch(unblockAccount(account.get('id')));
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
@ -10,14 +10,16 @@ import Permalink from 'mastodon/components/permalink';
|
||||||
import RelativeTimestamp from 'mastodon/components/relative_timestamp';
|
import RelativeTimestamp from 'mastodon/components/relative_timestamp';
|
||||||
import IconButton from 'mastodon/components/icon_button';
|
import IconButton from 'mastodon/components/icon_button';
|
||||||
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
|
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
|
||||||
import { autoPlayGif, me, unfollowModal } from 'mastodon/initial_state';
|
import { autoPlayGif, me, unfollowModal, unsubscribeModal } from 'mastodon/initial_state';
|
||||||
import ShortNumber from 'mastodon/components/short_number';
|
import ShortNumber from 'mastodon/components/short_number';
|
||||||
import {
|
import {
|
||||||
followAccount,
|
followAccount,
|
||||||
unfollowAccount,
|
unfollowAccount,
|
||||||
|
subscribeAccount,
|
||||||
|
unsubscribeAccount,
|
||||||
blockAccount,
|
blockAccount,
|
||||||
unblockAccount,
|
unblockAccount,
|
||||||
unmuteAccount,
|
unmuteAccount
|
||||||
} from 'mastodon/actions/accounts';
|
} from 'mastodon/actions/accounts';
|
||||||
import { openModal } from 'mastodon/actions/modal';
|
import { openModal } from 'mastodon/actions/modal';
|
||||||
import { initMuteModal } from 'mastodon/actions/mutes';
|
import { initMuteModal } from 'mastodon/actions/mutes';
|
||||||
|
@ -25,6 +27,8 @@ import { initMuteModal } from 'mastodon/actions/mutes';
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
follow: { id: 'account.follow', defaultMessage: 'Follow' },
|
follow: { id: 'account.follow', defaultMessage: 'Follow' },
|
||||||
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
|
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
|
||||||
|
unsubscribe: { id: 'account.unsubscribe', defaultMessage: 'Unsubscribe' },
|
||||||
|
subscribe: { id: 'account.subscribe', defaultMessage: 'Subscribe' },
|
||||||
requested: { id: 'account.requested', defaultMessage: 'Awaiting approval' },
|
requested: { id: 'account.requested', defaultMessage: 'Awaiting approval' },
|
||||||
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
|
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
|
||||||
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
|
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
|
||||||
|
@ -72,6 +76,22 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onSubscribe(account) {
|
||||||
|
if (account.getIn(['relationship', 'subscribing'])) {
|
||||||
|
if (unsubscribeModal) {
|
||||||
|
dispatch(openModal('CONFIRM', {
|
||||||
|
message: <FormattedMessage id='confirmations.unsubscribe.message' defaultMessage='Are you sure you want to unsubscribe {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
|
||||||
|
confirm: intl.formatMessage(messages.unsubscribeConfirm),
|
||||||
|
onConfirm: () => dispatch(unsubscribeAccount(account.get('id'))),
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
dispatch(unsubscribeAccount(account.get('id')));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dispatch(subscribeAccount(account.get('id')));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
onBlock(account) {
|
onBlock(account) {
|
||||||
if (account.getIn(['relationship', 'blocking'])) {
|
if (account.getIn(['relationship', 'blocking'])) {
|
||||||
dispatch(unblockAccount(account.get('id')));
|
dispatch(unblockAccount(account.get('id')));
|
||||||
|
@ -98,6 +118,7 @@ class AccountCard extends ImmutablePureComponent {
|
||||||
account: ImmutablePropTypes.map.isRequired,
|
account: ImmutablePropTypes.map.isRequired,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
onFollow: PropTypes.func.isRequired,
|
onFollow: PropTypes.func.isRequired,
|
||||||
|
onSubscribe: PropTypes.func.isRequired,
|
||||||
onBlock: PropTypes.func.isRequired,
|
onBlock: PropTypes.func.isRequired,
|
||||||
onMute: PropTypes.func.isRequired,
|
onMute: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
@ -132,6 +153,10 @@ class AccountCard extends ImmutablePureComponent {
|
||||||
this.props.onFollow(this.props.account);
|
this.props.onFollow(this.props.account);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleSubscribe = () => {
|
||||||
|
this.props.onSubscribe(this.props.account);
|
||||||
|
}
|
||||||
|
|
||||||
handleBlock = () => {
|
handleBlock = () => {
|
||||||
this.props.onBlock(this.props.account);
|
this.props.onBlock(this.props.account);
|
||||||
};
|
};
|
||||||
|
@ -150,6 +175,7 @@ class AccountCard extends ImmutablePureComponent {
|
||||||
account.get('relationship', null) !== null
|
account.get('relationship', null) !== null
|
||||||
) {
|
) {
|
||||||
const following = account.getIn(['relationship', 'following']);
|
const following = account.getIn(['relationship', 'following']);
|
||||||
|
const subscribing = account.getIn(['relationship', 'subscribing']);
|
||||||
const requested = account.getIn(['relationship', 'requested']);
|
const requested = account.getIn(['relationship', 'requested']);
|
||||||
const blocking = account.getIn(['relationship', 'blocking']);
|
const blocking = account.getIn(['relationship', 'blocking']);
|
||||||
const muting = account.getIn(['relationship', 'muting']);
|
const muting = account.getIn(['relationship', 'muting']);
|
||||||
|
@ -179,22 +205,38 @@ class AccountCard extends ImmutablePureComponent {
|
||||||
active
|
active
|
||||||
icon='volume-up'
|
icon='volume-up'
|
||||||
title={intl.formatMessage(messages.unmute, {
|
title={intl.formatMessage(messages.unmute, {
|
||||||
name: account.get('username'),
|
name: account.get('username')
|
||||||
})}
|
})}
|
||||||
onClick={this.handleMute}
|
onClick={this.handleMute}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (!account.get('moved') || following) {
|
} else {
|
||||||
buttons = (
|
let following_buttons, subscribing_buttons;
|
||||||
<IconButton
|
if(!account.get('moved') || subscribing) {
|
||||||
icon={following ? 'user-times' : 'user-plus'}
|
subscribing_buttons = (
|
||||||
title={intl.formatMessage(
|
<IconButton
|
||||||
following ? messages.unfollow : messages.follow,
|
icon='rss-square'
|
||||||
)}
|
title={intl.formatMessage(
|
||||||
onClick={this.handleFollow}
|
subscribing ? messages.unsubscribe : messages.subscribe
|
||||||
active={following}
|
)}
|
||||||
/>
|
onClick={this.handleSubscribe}
|
||||||
);
|
active={subscribing}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if(!account.get('moved') || following) {
|
||||||
|
following_buttons = (
|
||||||
|
<IconButton
|
||||||
|
icon={following ? 'user-times' : 'user-plus'}
|
||||||
|
title={intl.formatMessage(
|
||||||
|
following ? messages.unfollow : messages.follow
|
||||||
|
)}
|
||||||
|
onClick={this.handleFollow}
|
||||||
|
active={following}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
buttons = <Fragment>{subscribing_buttons}{following_buttons}</Fragment>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
103
app/javascript/mastodon/features/subscribing/index.js
Normal file
103
app/javascript/mastodon/features/subscribing/index.js
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
import { debounce } from 'lodash';
|
||||||
|
import LoadingIndicator from '../../components/loading_indicator';
|
||||||
|
import {
|
||||||
|
fetchAccount,
|
||||||
|
fetchSubscribing,
|
||||||
|
expandSubscribing,
|
||||||
|
} from '../../actions/accounts';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
import AccountContainer from '../../containers/account_container';
|
||||||
|
import Column from '../ui/components/column';
|
||||||
|
import HeaderContainer from '../account_timeline/containers/header_container';
|
||||||
|
import ColumnBackButton from '../../components/column_back_button';
|
||||||
|
import ScrollableList from '../../components/scrollable_list';
|
||||||
|
import MissingIndicator from 'mastodon/components/missing_indicator';
|
||||||
|
|
||||||
|
const mapStateToProps = (state, props) => ({
|
||||||
|
isAccount: !!state.getIn(['accounts', props.params.accountId]),
|
||||||
|
accountIds: state.getIn(['user_lists', 'subscribing', props.params.accountId, 'items']),
|
||||||
|
hasMore: !!state.getIn(['user_lists', 'subscribing', props.params.accountId, 'next']),
|
||||||
|
isLoading: state.getIn(['user_lists', 'subscribing', props.params.accountId, 'isLoading'], true),
|
||||||
|
blockedBy: state.getIn(['relationships', props.params.accountId, 'blocked_by'], false),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default @connect(mapStateToProps)
|
||||||
|
class Subscribing extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
params: PropTypes.object.isRequired,
|
||||||
|
dispatch: PropTypes.func.isRequired,
|
||||||
|
accountIds: ImmutablePropTypes.list,
|
||||||
|
hasMore: PropTypes.bool,
|
||||||
|
blockedBy: PropTypes.bool,
|
||||||
|
isAccount: PropTypes.bool,
|
||||||
|
multiColumn: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
componentWillMount () {
|
||||||
|
if (!this.props.accountIds) {
|
||||||
|
this.props.dispatch(fetchAccount(this.props.params.accountId));
|
||||||
|
this.props.dispatch(fetchSubscribing(this.props.params.accountId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps (nextProps) {
|
||||||
|
if (nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) {
|
||||||
|
this.props.dispatch(fetchAccount(nextProps.params.accountId));
|
||||||
|
this.props.dispatch(fetchSubscribing(nextProps.params.accountId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleLoadMore = debounce(() => {
|
||||||
|
this.props.dispatch(expandSubscribing());
|
||||||
|
}, 300, { leading: true });
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { accountIds, hasMore, blockedBy, isAccount, multiColumn, isLoading } = this.props;
|
||||||
|
|
||||||
|
if (!isAccount) {
|
||||||
|
return (
|
||||||
|
<Column>
|
||||||
|
<MissingIndicator />
|
||||||
|
</Column>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!accountIds) {
|
||||||
|
return (
|
||||||
|
<Column>
|
||||||
|
<LoadingIndicator />
|
||||||
|
</Column>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const emptyMessage = blockedBy ? <FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' /> : <FormattedMessage id='account.subscribes.empty' defaultMessage="This user doesn't subscribe anyone yet." />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Column>
|
||||||
|
<ColumnBackButton multiColumn={multiColumn} />
|
||||||
|
|
||||||
|
<ScrollableList
|
||||||
|
scrollKey='subscribing'
|
||||||
|
hasMore={hasMore}
|
||||||
|
isLoading={isLoading}
|
||||||
|
onLoadMore={this.handleLoadMore}
|
||||||
|
prepend={<HeaderContainer accountId={this.props.params.accountId} hideTabs />}
|
||||||
|
alwaysPrepend
|
||||||
|
emptyMessage={emptyMessage}
|
||||||
|
bindToDocument={!multiColumn}
|
||||||
|
>
|
||||||
|
{blockedBy ? [] : accountIds.map(id =>
|
||||||
|
<AccountContainer key={id} id={id} withNote={false} />
|
||||||
|
)}
|
||||||
|
</ScrollableList>
|
||||||
|
</Column>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ import {
|
||||||
HomeTimeline,
|
HomeTimeline,
|
||||||
Followers,
|
Followers,
|
||||||
Following,
|
Following,
|
||||||
|
Subscribing,
|
||||||
Reblogs,
|
Reblogs,
|
||||||
Favourites,
|
Favourites,
|
||||||
DirectTimeline,
|
DirectTimeline,
|
||||||
|
@ -179,6 +180,7 @@ class SwitchingColumnsArea extends React.PureComponent {
|
||||||
<WrappedRoute path='/accounts/:accountId/with_replies' component={AccountTimeline} content={children} componentParams={{ withReplies: true }} />
|
<WrappedRoute path='/accounts/:accountId/with_replies' component={AccountTimeline} content={children} componentParams={{ withReplies: true }} />
|
||||||
<WrappedRoute path='/accounts/:accountId/followers' component={Followers} content={children} />
|
<WrappedRoute path='/accounts/:accountId/followers' component={Followers} content={children} />
|
||||||
<WrappedRoute path='/accounts/:accountId/following' component={Following} content={children} />
|
<WrappedRoute path='/accounts/:accountId/following' component={Following} content={children} />
|
||||||
|
<WrappedRoute path='/accounts/:accountId/subscribing' component={Subscribing} content={children} />
|
||||||
<WrappedRoute path='/accounts/:accountId/media' component={AccountGallery} content={children} />
|
<WrappedRoute path='/accounts/:accountId/media' component={AccountGallery} content={children} />
|
||||||
|
|
||||||
<WrappedRoute path='/follow_requests' component={FollowRequests} content={children} />
|
<WrappedRoute path='/follow_requests' component={FollowRequests} content={children} />
|
||||||
|
|
|
@ -74,6 +74,10 @@ export function Following () {
|
||||||
return import(/* webpackChunkName: "features/following" */'../../following');
|
return import(/* webpackChunkName: "features/following" */'../../following');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function Subscribing () {
|
||||||
|
return import(/* webpackChunkName: "features/subscribing" */'../../subscribing');
|
||||||
|
}
|
||||||
|
|
||||||
export function Reblogs () {
|
export function Reblogs () {
|
||||||
return import(/* webpackChunkName: "features/reblogs" */'../../reblogs');
|
return import(/* webpackChunkName: "features/reblogs" */'../../reblogs');
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ export const autoPlayGif = getMeta('auto_play_gif');
|
||||||
export const displayMedia = getMeta('display_media');
|
export const displayMedia = getMeta('display_media');
|
||||||
export const expandSpoilers = getMeta('expand_spoilers');
|
export const expandSpoilers = getMeta('expand_spoilers');
|
||||||
export const unfollowModal = getMeta('unfollow_modal');
|
export const unfollowModal = getMeta('unfollow_modal');
|
||||||
|
export const unsubscribeModal = getMeta('unsubscribe_modal');
|
||||||
export const boostModal = getMeta('boost_modal');
|
export const boostModal = getMeta('boost_modal');
|
||||||
export const deleteModal = getMeta('delete_modal');
|
export const deleteModal = getMeta('delete_modal');
|
||||||
export const me = getMeta('me');
|
export const me = getMeta('me');
|
||||||
|
|
|
@ -41,10 +41,14 @@
|
||||||
"account.share": "Share @{name}'s profile",
|
"account.share": "Share @{name}'s profile",
|
||||||
"account.show_reblogs": "Show boosts from @{name}",
|
"account.show_reblogs": "Show boosts from @{name}",
|
||||||
"account.statuses_counter": "{count, plural, one {{counter} Post} other {{counter} Posts}}",
|
"account.statuses_counter": "{count, plural, one {{counter} Post} other {{counter} Posts}}",
|
||||||
|
"account.subscribe": "Subscribe",
|
||||||
|
"account.subscribes": "Subscribes",
|
||||||
|
"account.subscribes.empty": "This user doesn't subscribe anyone yet.",
|
||||||
"account.unblock": "Unblock @{name}",
|
"account.unblock": "Unblock @{name}",
|
||||||
"account.unblock_domain": "Unblock domain {domain}",
|
"account.unblock_domain": "Unblock domain {domain}",
|
||||||
"account.unendorse": "Don't feature on profile",
|
"account.unendorse": "Don't feature on profile",
|
||||||
"account.unfollow": "Unfollow",
|
"account.unfollow": "Unfollow",
|
||||||
|
"account.unsubscribe": "Unsubscribe",
|
||||||
"account.unmute": "Unmute @{name}",
|
"account.unmute": "Unmute @{name}",
|
||||||
"account.unmute_notifications": "Unmute notifications from @{name}",
|
"account.unmute_notifications": "Unmute notifications from @{name}",
|
||||||
"account_note.placeholder": "Click to add note",
|
"account_note.placeholder": "Click to add note",
|
||||||
|
@ -129,6 +133,8 @@
|
||||||
"confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
|
"confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
|
||||||
"confirmations.unfollow.confirm": "Unfollow",
|
"confirmations.unfollow.confirm": "Unfollow",
|
||||||
"confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
|
"confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
|
||||||
|
"confirmations.unsubscribe.confirm": "Unsubscribe",
|
||||||
|
"confirmations.unsubscribe.message": "Are you sure you want to unsubscribe {name}?",
|
||||||
"conversation.delete": "Delete conversation",
|
"conversation.delete": "Delete conversation",
|
||||||
"conversation.mark_as_read": "Mark as read",
|
"conversation.mark_as_read": "Mark as read",
|
||||||
"conversation.open": "View conversation",
|
"conversation.open": "View conversation",
|
||||||
|
|
|
@ -41,10 +41,14 @@
|
||||||
"account.share": "@{name}さんのプロフィールを共有する",
|
"account.share": "@{name}さんのプロフィールを共有する",
|
||||||
"account.show_reblogs": "@{name}さんからのブーストを表示",
|
"account.show_reblogs": "@{name}さんからのブーストを表示",
|
||||||
"account.statuses_counter": "{counter} 投稿",
|
"account.statuses_counter": "{counter} 投稿",
|
||||||
|
"account.subscribe": "購読",
|
||||||
|
"account.subscribes": "購読",
|
||||||
|
"account.subscribes.empty": "まだ誰も購読していません。",
|
||||||
"account.unblock": "@{name}さんのブロックを解除",
|
"account.unblock": "@{name}さんのブロックを解除",
|
||||||
"account.unblock_domain": "{domain}のブロックを解除",
|
"account.unblock_domain": "{domain}のブロックを解除",
|
||||||
"account.unendorse": "プロフィールから外す",
|
"account.unendorse": "プロフィールから外す",
|
||||||
"account.unfollow": "フォロー解除",
|
"account.unfollow": "フォロー解除",
|
||||||
|
"account.unsubscribe": "購読解除",
|
||||||
"account.unmute": "@{name}さんのミュートを解除",
|
"account.unmute": "@{name}さんのミュートを解除",
|
||||||
"account.unmute_notifications": "@{name}さんからの通知を受け取るようにする",
|
"account.unmute_notifications": "@{name}さんからの通知を受け取るようにする",
|
||||||
"account_note.placeholder": "クリックしてメモを追加",
|
"account_note.placeholder": "クリックしてメモを追加",
|
||||||
|
@ -129,6 +133,8 @@
|
||||||
"confirmations.reply.message": "今返信すると現在作成中のメッセージが上書きされます。本当に実行しますか?",
|
"confirmations.reply.message": "今返信すると現在作成中のメッセージが上書きされます。本当に実行しますか?",
|
||||||
"confirmations.unfollow.confirm": "フォロー解除",
|
"confirmations.unfollow.confirm": "フォロー解除",
|
||||||
"confirmations.unfollow.message": "本当に{name}さんのフォローを解除しますか?",
|
"confirmations.unfollow.message": "本当に{name}さんのフォローを解除しますか?",
|
||||||
|
"confirmations.unsubscribe.confirm": "購読解除",
|
||||||
|
"confirmations.unsubscribe.message": "本当に{name}さんの購読を解除しますか?",
|
||||||
"conversation.delete": "会話を削除",
|
"conversation.delete": "会話を削除",
|
||||||
"conversation.mark_as_read": "既読にする",
|
"conversation.mark_as_read": "既読にする",
|
||||||
"conversation.open": "会話を表示",
|
"conversation.open": "会話を表示",
|
||||||
|
|
|
@ -8,6 +8,7 @@ const normalizeAccount = (state, account) => {
|
||||||
|
|
||||||
delete account.followers_count;
|
delete account.followers_count;
|
||||||
delete account.following_count;
|
delete account.following_count;
|
||||||
|
delete account.subscribing_count;
|
||||||
delete account.statuses_count;
|
delete account.statuses_count;
|
||||||
|
|
||||||
return state.set(account.id, fromJS(account));
|
return state.set(account.id, fromJS(account));
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||||
const normalizeAccount = (state, account) => state.set(account.id, fromJS({
|
const normalizeAccount = (state, account) => state.set(account.id, fromJS({
|
||||||
followers_count: account.followers_count,
|
followers_count: account.followers_count,
|
||||||
following_count: account.following_count,
|
following_count: account.following_count,
|
||||||
|
subscribing_count: account.subscribing_count,
|
||||||
statuses_count: account.statuses_count,
|
statuses_count: account.statuses_count,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,12 @@ import {
|
||||||
ACCOUNT_UNFOLLOW_SUCCESS,
|
ACCOUNT_UNFOLLOW_SUCCESS,
|
||||||
ACCOUNT_UNFOLLOW_REQUEST,
|
ACCOUNT_UNFOLLOW_REQUEST,
|
||||||
ACCOUNT_UNFOLLOW_FAIL,
|
ACCOUNT_UNFOLLOW_FAIL,
|
||||||
|
ACCOUNT_SUBSCRIBE_SUCCESS,
|
||||||
|
ACCOUNT_SUBSCRIBE_REQUEST,
|
||||||
|
ACCOUNT_SUBSCRIBE_FAIL,
|
||||||
|
ACCOUNT_UNSUBSCRIBE_SUCCESS,
|
||||||
|
ACCOUNT_UNSUBSCRIBE_REQUEST,
|
||||||
|
ACCOUNT_UNSUBSCRIBE_FAIL,
|
||||||
ACCOUNT_BLOCK_SUCCESS,
|
ACCOUNT_BLOCK_SUCCESS,
|
||||||
ACCOUNT_UNBLOCK_SUCCESS,
|
ACCOUNT_UNBLOCK_SUCCESS,
|
||||||
ACCOUNT_MUTE_SUCCESS,
|
ACCOUNT_MUTE_SUCCESS,
|
||||||
|
@ -52,8 +58,18 @@ export default function relationships(state = initialState, action) {
|
||||||
return state.setIn([action.id, 'following'], false);
|
return state.setIn([action.id, 'following'], false);
|
||||||
case ACCOUNT_UNFOLLOW_FAIL:
|
case ACCOUNT_UNFOLLOW_FAIL:
|
||||||
return state.setIn([action.id, 'following'], true);
|
return state.setIn([action.id, 'following'], true);
|
||||||
|
case ACCOUNT_SUBSCRIBE_REQUEST:
|
||||||
|
return state.setIn([action.id, 'subscribing'], true);
|
||||||
|
case ACCOUNT_SUBSCRIBE_FAIL:
|
||||||
|
return state.setIn([action.id, 'subscribing'], false);
|
||||||
|
case ACCOUNT_UNSUBSCRIBE_REQUEST:
|
||||||
|
return state.setIn([action.id, 'subscribing'], false);
|
||||||
|
case ACCOUNT_UNSUBSCRIBE_FAIL:
|
||||||
|
return state.setIn([action.id, 'subscribing'], true);
|
||||||
case ACCOUNT_FOLLOW_SUCCESS:
|
case ACCOUNT_FOLLOW_SUCCESS:
|
||||||
case ACCOUNT_UNFOLLOW_SUCCESS:
|
case ACCOUNT_UNFOLLOW_SUCCESS:
|
||||||
|
case ACCOUNT_SUBSCRIBE_SUCCESS:
|
||||||
|
case ACCOUNT_UNSUBSCRIBE_SUCCESS:
|
||||||
case ACCOUNT_BLOCK_SUCCESS:
|
case ACCOUNT_BLOCK_SUCCESS:
|
||||||
case ACCOUNT_UNBLOCK_SUCCESS:
|
case ACCOUNT_UNBLOCK_SUCCESS:
|
||||||
case ACCOUNT_MUTE_SUCCESS:
|
case ACCOUNT_MUTE_SUCCESS:
|
||||||
|
|
|
@ -15,6 +15,7 @@ import {
|
||||||
ACCOUNT_BLOCK_SUCCESS,
|
ACCOUNT_BLOCK_SUCCESS,
|
||||||
ACCOUNT_MUTE_SUCCESS,
|
ACCOUNT_MUTE_SUCCESS,
|
||||||
ACCOUNT_UNFOLLOW_SUCCESS,
|
ACCOUNT_UNFOLLOW_SUCCESS,
|
||||||
|
ACCOUNT_UNSUBSCRIBE_SUCCESS,
|
||||||
} from '../actions/accounts';
|
} from '../actions/accounts';
|
||||||
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
|
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
|
||||||
import compareId from '../compare_id';
|
import compareId from '../compare_id';
|
||||||
|
@ -158,6 +159,7 @@ export default function timelines(state = initialState, action) {
|
||||||
case ACCOUNT_MUTE_SUCCESS:
|
case ACCOUNT_MUTE_SUCCESS:
|
||||||
return filterTimelines(state, action.relationship, action.statuses);
|
return filterTimelines(state, action.relationship, action.statuses);
|
||||||
case ACCOUNT_UNFOLLOW_SUCCESS:
|
case ACCOUNT_UNFOLLOW_SUCCESS:
|
||||||
|
case ACCOUNT_UNSUBSCRIBE_SUCCESS:
|
||||||
return filterTimeline('home', state, action.relationship, action.statuses);
|
return filterTimeline('home', state, action.relationship, action.statuses);
|
||||||
case TIMELINE_SCROLL_TOP:
|
case TIMELINE_SCROLL_TOP:
|
||||||
return updateTop(state, action.timeline, action.top);
|
return updateTop(state, action.timeline, action.top);
|
||||||
|
|
|
@ -15,6 +15,12 @@ import {
|
||||||
FOLLOWING_EXPAND_SUCCESS,
|
FOLLOWING_EXPAND_SUCCESS,
|
||||||
FOLLOWING_EXPAND_FAIL,
|
FOLLOWING_EXPAND_FAIL,
|
||||||
FOLLOW_REQUESTS_FETCH_REQUEST,
|
FOLLOW_REQUESTS_FETCH_REQUEST,
|
||||||
|
SUBSCRIBING_FETCH_REQUEST,
|
||||||
|
SUBSCRIBING_FETCH_SUCCESS,
|
||||||
|
SUBSCRIBING_FETCH_FAIL,
|
||||||
|
SUBSCRIBING_EXPAND_REQUEST,
|
||||||
|
SUBSCRIBING_EXPAND_SUCCESS,
|
||||||
|
SUBSCRIBING_EXPAND_FAIL,
|
||||||
FOLLOW_REQUESTS_FETCH_SUCCESS,
|
FOLLOW_REQUESTS_FETCH_SUCCESS,
|
||||||
FOLLOW_REQUESTS_FETCH_FAIL,
|
FOLLOW_REQUESTS_FETCH_FAIL,
|
||||||
FOLLOW_REQUESTS_EXPAND_REQUEST,
|
FOLLOW_REQUESTS_EXPAND_REQUEST,
|
||||||
|
@ -62,6 +68,7 @@ const initialListState = ImmutableMap({
|
||||||
const initialState = ImmutableMap({
|
const initialState = ImmutableMap({
|
||||||
followers: initialListState,
|
followers: initialListState,
|
||||||
following: initialListState,
|
following: initialListState,
|
||||||
|
subscribing: initialListState,
|
||||||
reblogged_by: initialListState,
|
reblogged_by: initialListState,
|
||||||
favourited_by: initialListState,
|
favourited_by: initialListState,
|
||||||
follow_requests: initialListState,
|
follow_requests: initialListState,
|
||||||
|
@ -111,6 +118,16 @@ export default function userLists(state = initialState, action) {
|
||||||
case FOLLOWING_FETCH_FAIL:
|
case FOLLOWING_FETCH_FAIL:
|
||||||
case FOLLOWING_EXPAND_FAIL:
|
case FOLLOWING_EXPAND_FAIL:
|
||||||
return state.setIn(['following', action.id, 'isLoading'], false);
|
return state.setIn(['following', action.id, 'isLoading'], false);
|
||||||
|
case SUBSCRIBING_FETCH_SUCCESS:
|
||||||
|
return normalizeList(state, 'subscribing', action.id, action.accounts, action.next);
|
||||||
|
case SUBSCRIBING_EXPAND_SUCCESS:
|
||||||
|
return appendToList(state, 'subscribing', action.id, action.accounts, action.next);
|
||||||
|
case SUBSCRIBING_FETCH_REQUEST:
|
||||||
|
case SUBSCRIBING_EXPAND_REQUEST:
|
||||||
|
return state.setIn(['subscribing', action.id, 'isLoading'], true);
|
||||||
|
case SUBSCRIBING_FETCH_FAIL:
|
||||||
|
case SUBSCRIBING_EXPAND_FAIL:
|
||||||
|
return state.setIn(['subscribing', action.id, 'isLoading'], false);
|
||||||
case REBLOGS_FETCH_SUCCESS:
|
case REBLOGS_FETCH_SUCCESS:
|
||||||
return state.setIn(['reblogged_by', action.id], ImmutableList(action.accounts.map(item => item.id)));
|
return state.setIn(['reblogged_by', action.id], ImmutableList(action.accounts.map(item => item.id)));
|
||||||
case FAVOURITES_FETCH_SUCCESS:
|
case FAVOURITES_FETCH_SUCCESS:
|
||||||
|
|
|
@ -5991,7 +5991,7 @@ a.status-card.compact:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
&__relationship {
|
&__relationship {
|
||||||
width: 23px;
|
width: 46px;
|
||||||
min-height: 1px;
|
min-height: 1px;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
}
|
}
|
||||||
|
@ -6761,6 +6761,7 @@ noscript {
|
||||||
|
|
||||||
&__name {
|
&__name {
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
.account-role {
|
.account-role {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
|
@ -6779,6 +6780,7 @@ noscript {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
|
||||||
small {
|
small {
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -6789,6 +6791,9 @@ noscript {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&__relationship {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.spacer {
|
.spacer {
|
||||||
|
|
|
@ -21,6 +21,7 @@ class UserSettingsDecorator
|
||||||
user.settings['default_sensitive'] = default_sensitive_preference if change?('setting_default_sensitive')
|
user.settings['default_sensitive'] = default_sensitive_preference if change?('setting_default_sensitive')
|
||||||
user.settings['default_language'] = default_language_preference if change?('setting_default_language')
|
user.settings['default_language'] = default_language_preference if change?('setting_default_language')
|
||||||
user.settings['unfollow_modal'] = unfollow_modal_preference if change?('setting_unfollow_modal')
|
user.settings['unfollow_modal'] = unfollow_modal_preference if change?('setting_unfollow_modal')
|
||||||
|
user.settings['unsubscribe_modal'] = unsubscribe_modal_preference if change?('setting_unsubscribe_modal')
|
||||||
user.settings['boost_modal'] = boost_modal_preference if change?('setting_boost_modal')
|
user.settings['boost_modal'] = boost_modal_preference if change?('setting_boost_modal')
|
||||||
user.settings['delete_modal'] = delete_modal_preference if change?('setting_delete_modal')
|
user.settings['delete_modal'] = delete_modal_preference if change?('setting_delete_modal')
|
||||||
user.settings['auto_play_gif'] = auto_play_gif_preference if change?('setting_auto_play_gif')
|
user.settings['auto_play_gif'] = auto_play_gif_preference if change?('setting_auto_play_gif')
|
||||||
|
@ -61,6 +62,10 @@ class UserSettingsDecorator
|
||||||
boolean_cast_setting 'setting_unfollow_modal'
|
boolean_cast_setting 'setting_unfollow_modal'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def unsubscribe_modal_preference
|
||||||
|
boolean_cast_setting 'setting_unsubscribe_modal'
|
||||||
|
end
|
||||||
|
|
||||||
def boost_modal_preference
|
def boost_modal_preference
|
||||||
boolean_cast_setting 'setting_boost_modal'
|
boolean_cast_setting 'setting_boost_modal'
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
# statuses_count :bigint(8) default(0), not null
|
# statuses_count :bigint(8) default(0), not null
|
||||||
# following_count :bigint(8) default(0), not null
|
# following_count :bigint(8) default(0), not null
|
||||||
# followers_count :bigint(8) default(0), not null
|
# followers_count :bigint(8) default(0), not null
|
||||||
|
# subscribing_count :bigint(8) default(0), not null
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
# last_status_at :datetime
|
# last_status_at :datetime
|
||||||
|
|
|
@ -11,6 +11,9 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
class AccountSubscribe < ApplicationRecord
|
class AccountSubscribe < ApplicationRecord
|
||||||
|
include Paginable
|
||||||
|
include RelationshipCacheable
|
||||||
|
|
||||||
belongs_to :account
|
belongs_to :account
|
||||||
belongs_to :target_account, class_name: 'Account'
|
belongs_to :target_account, class_name: 'Account'
|
||||||
belongs_to :list, optional: true
|
belongs_to :list, optional: true
|
||||||
|
@ -20,4 +23,17 @@ class AccountSubscribe < ApplicationRecord
|
||||||
scope :recent, -> { reorder(id: :desc) }
|
scope :recent, -> { reorder(id: :desc) }
|
||||||
scope :subscribed_lists, ->(account) { AccountSubscribe.where(target_account_id: account.id).where.not(list_id: nil).select(:list_id).uniq }
|
scope :subscribed_lists, ->(account) { AccountSubscribe.where(target_account_id: account.id).where.not(list_id: nil).select(:list_id).uniq }
|
||||||
|
|
||||||
|
after_create :increment_cache_counters
|
||||||
|
after_destroy :decrement_cache_counters
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def increment_cache_counters
|
||||||
|
account&.increment_count!(:subscribing_count)
|
||||||
|
end
|
||||||
|
|
||||||
|
def decrement_cache_counters
|
||||||
|
account&.decrement_count!(:subscribing_count)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,6 +16,8 @@ module AccountCounters
|
||||||
:following_count=,
|
:following_count=,
|
||||||
:followers_count,
|
:followers_count,
|
||||||
:followers_count=,
|
:followers_count=,
|
||||||
|
:subscribing_count,
|
||||||
|
:subscribing_count=,
|
||||||
:last_status_at,
|
:last_status_at,
|
||||||
to: :account_stat
|
to: :account_stat
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,10 @@ module AccountInteractions
|
||||||
follow_mapping(Follow.where(account_id: target_account_ids, target_account_id: account_id), :account_id)
|
follow_mapping(Follow.where(account_id: target_account_ids, target_account_id: account_id), :account_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def subscribing_map(target_account_ids, account_id)
|
||||||
|
follow_mapping(AccountSubscribe.where(target_account_id: target_account_ids, account_id: account_id), :target_account_id)
|
||||||
|
end
|
||||||
|
|
||||||
def blocking_map(target_account_ids, account_id)
|
def blocking_map(target_account_ids, account_id)
|
||||||
follow_mapping(Block.where(target_account_id: target_account_ids, account_id: account_id), :target_account_id)
|
follow_mapping(Block.where(target_account_id: target_account_ids, account_id: account_id), :target_account_id)
|
||||||
end
|
end
|
||||||
|
@ -191,7 +195,11 @@ module AccountInteractions
|
||||||
end
|
end
|
||||||
|
|
||||||
def subscribe!(other_account)
|
def subscribe!(other_account)
|
||||||
active_subscribes.find_or_create_by!(target_account: other_account)
|
rel = active_subscribes.find_or_create_by!(target_account: other_account)
|
||||||
|
|
||||||
|
remove_potential_friendship(other_account)
|
||||||
|
|
||||||
|
rel
|
||||||
end
|
end
|
||||||
|
|
||||||
def following?(other_account)
|
def following?(other_account)
|
||||||
|
|
|
@ -122,6 +122,7 @@ module StatusThreadingConcern
|
||||||
blocked_by: Account.blocked_by_map(account_ids, account.id),
|
blocked_by: Account.blocked_by_map(account_ids, account.id),
|
||||||
muting: Account.muting_map(account_ids, account.id),
|
muting: Account.muting_map(account_ids, account.id),
|
||||||
following: Account.following_map(account_ids, account.id),
|
following: Account.following_map(account_ids, account.id),
|
||||||
|
subscribing: Account.subscribing_map(account_ids, account.id),
|
||||||
domain_blocking_by_domain: Account.domain_blocking_map_by_domain(domains, account.id),
|
domain_blocking_by_domain: Account.domain_blocking_map_by_domain(domains, account.id),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -71,6 +71,10 @@ class Export
|
||||||
account.following_count
|
account.following_count
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def total_subscribes
|
||||||
|
account.subscribing_count
|
||||||
|
end
|
||||||
|
|
||||||
def total_lists
|
def total_lists
|
||||||
account.owned_lists.count
|
account.owned_lists.count
|
||||||
end
|
end
|
||||||
|
|
|
@ -121,7 +121,7 @@ class User < ApplicationRecord
|
||||||
|
|
||||||
has_many :session_activations, dependent: :destroy
|
has_many :session_activations, dependent: :destroy
|
||||||
|
|
||||||
delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :delete_modal,
|
delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :unsubscribe_modal, :boost_modal, :delete_modal,
|
||||||
:reduce_motion, :system_font_ui, :noindex, :theme, :display_media, :hide_network,
|
:reduce_motion, :system_font_ui, :noindex, :theme, :display_media, :hide_network,
|
||||||
:expand_spoilers, :default_language, :aggregate_reblogs, :show_application,
|
:expand_spoilers, :default_language, :aggregate_reblogs, :show_application,
|
||||||
:advanced_layout, :use_blurhash, :use_pending_items, :trends, :crop_images,
|
:advanced_layout, :use_blurhash, :use_pending_items, :trends, :crop_images,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class AccountRelationshipsPresenter
|
class AccountRelationshipsPresenter
|
||||||
attr_reader :following, :followed_by, :blocking, :blocked_by,
|
attr_reader :following, :followed_by, :subscribing, :blocking, :blocked_by,
|
||||||
:muting, :requested, :domain_blocking,
|
:muting, :requested, :domain_blocking,
|
||||||
:endorsed, :account_note
|
:endorsed, :account_note
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ class AccountRelationshipsPresenter
|
||||||
|
|
||||||
@following = cached[:following].merge(Account.following_map(@uncached_account_ids, @current_account_id))
|
@following = cached[:following].merge(Account.following_map(@uncached_account_ids, @current_account_id))
|
||||||
@followed_by = cached[:followed_by].merge(Account.followed_by_map(@uncached_account_ids, @current_account_id))
|
@followed_by = cached[:followed_by].merge(Account.followed_by_map(@uncached_account_ids, @current_account_id))
|
||||||
|
@subscribing = cached[:subscribing].merge(Account.subscribing_map(@uncached_account_ids, @current_account_id))
|
||||||
@blocking = cached[:blocking].merge(Account.blocking_map(@uncached_account_ids, @current_account_id))
|
@blocking = cached[:blocking].merge(Account.blocking_map(@uncached_account_ids, @current_account_id))
|
||||||
@blocked_by = cached[:blocked_by].merge(Account.blocked_by_map(@uncached_account_ids, @current_account_id))
|
@blocked_by = cached[:blocked_by].merge(Account.blocked_by_map(@uncached_account_ids, @current_account_id))
|
||||||
@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))
|
||||||
|
@ -23,6 +24,7 @@ class AccountRelationshipsPresenter
|
||||||
|
|
||||||
@following.merge!(options[:following_map] || {})
|
@following.merge!(options[:following_map] || {})
|
||||||
@followed_by.merge!(options[:followed_by_map] || {})
|
@followed_by.merge!(options[:followed_by_map] || {})
|
||||||
|
@subscribing.merge!(options[:subscribing_map] || {})
|
||||||
@blocking.merge!(options[:blocking_map] || {})
|
@blocking.merge!(options[:blocking_map] || {})
|
||||||
@blocked_by.merge!(options[:blocked_by_map] || {})
|
@blocked_by.merge!(options[:blocked_by_map] || {})
|
||||||
@muting.merge!(options[:muting_map] || {})
|
@muting.merge!(options[:muting_map] || {})
|
||||||
|
@ -40,6 +42,7 @@ class AccountRelationshipsPresenter
|
||||||
@cached = {
|
@cached = {
|
||||||
following: {},
|
following: {},
|
||||||
followed_by: {},
|
followed_by: {},
|
||||||
|
subscribing: {},
|
||||||
blocking: {},
|
blocking: {},
|
||||||
blocked_by: {},
|
blocked_by: {},
|
||||||
muting: {},
|
muting: {},
|
||||||
|
@ -69,6 +72,7 @@ class AccountRelationshipsPresenter
|
||||||
maps_for_account = {
|
maps_for_account = {
|
||||||
following: { account_id => following[account_id] },
|
following: { account_id => following[account_id] },
|
||||||
followed_by: { account_id => followed_by[account_id] },
|
followed_by: { account_id => followed_by[account_id] },
|
||||||
|
subscribing: { account_id => subscribing[account_id] },
|
||||||
blocking: { account_id => blocking[account_id] },
|
blocking: { account_id => blocking[account_id] },
|
||||||
blocked_by: { account_id => blocked_by[account_id] },
|
blocked_by: { account_id => blocked_by[account_id] },
|
||||||
muting: { account_id => muting[account_id] },
|
muting: { account_id => muting[account_id] },
|
||||||
|
|
|
@ -28,6 +28,7 @@ class InitialStateSerializer < ActiveModel::Serializer
|
||||||
if object.current_account
|
if object.current_account
|
||||||
store[:me] = object.current_account.id.to_s
|
store[:me] = object.current_account.id.to_s
|
||||||
store[:unfollow_modal] = object.current_account.user.setting_unfollow_modal
|
store[:unfollow_modal] = object.current_account.user.setting_unfollow_modal
|
||||||
|
store[:unsubscribe_modal] = object.current_account.user.setting_unsubscribe_modal
|
||||||
store[:boost_modal] = object.current_account.user.setting_boost_modal
|
store[:boost_modal] = object.current_account.user.setting_boost_modal
|
||||||
store[:delete_modal] = object.current_account.user.setting_delete_modal
|
store[:delete_modal] = object.current_account.user.setting_delete_modal
|
||||||
store[:auto_play_gif] = object.current_account.user.setting_auto_play_gif
|
store[:auto_play_gif] = object.current_account.user.setting_auto_play_gif
|
||||||
|
|
|
@ -5,7 +5,7 @@ class REST::AccountSerializer < ActiveModel::Serializer
|
||||||
|
|
||||||
attributes :id, :username, :acct, :display_name, :locked, :bot, :discoverable, :group, :created_at,
|
attributes :id, :username, :acct, :display_name, :locked, :bot, :discoverable, :group, :created_at,
|
||||||
:note, :url, :avatar, :avatar_static, :header, :header_static,
|
:note, :url, :avatar, :avatar_static, :header, :header_static,
|
||||||
:followers_count, :following_count, :statuses_count, :last_status_at
|
:followers_count, :following_count, :subscribing_count, :statuses_count, :last_status_at
|
||||||
|
|
||||||
has_one :moved_to_account, key: :moved, serializer: REST::AccountSerializer, if: :moved_and_not_nested?
|
has_one :moved_to_account, key: :moved, serializer: REST::AccountSerializer, if: :moved_and_not_nested?
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class REST::RelationshipSerializer < ActiveModel::Serializer
|
class REST::RelationshipSerializer < ActiveModel::Serializer
|
||||||
attributes :id, :following, :showing_reblogs, :notifying, :followed_by,
|
attributes :id, :following, :showing_reblogs, :notifying, :followed_by, :subscribing,
|
||||||
:blocking, :blocked_by, :muting, :muting_notifications, :requested,
|
:blocking, :blocked_by, :muting, :muting_notifications, :requested,
|
||||||
:domain_blocking, :endorsed, :note
|
:domain_blocking, :endorsed, :note
|
||||||
|
|
||||||
|
@ -29,6 +29,10 @@ class REST::RelationshipSerializer < ActiveModel::Serializer
|
||||||
instance_options[:relationships].followed_by[object.id] || false
|
instance_options[:relationships].followed_by[object.id] || false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def subscribing
|
||||||
|
instance_options[:relationships].subscribing[object.id] ? true : false
|
||||||
|
end
|
||||||
|
|
||||||
def blocking
|
def blocking
|
||||||
instance_options[:relationships].blocking[object.id] || false
|
instance_options[:relationships].blocking[object.id] || false
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,7 +6,8 @@ class AccountSubscribeService < BaseService
|
||||||
# @param [String, Account] uri User URI to subscribe in the form of username@domain (or account record)
|
# @param [String, Account] uri User URI to subscribe in the form of username@domain (or account record)
|
||||||
def call(source_account, target_account)
|
def call(source_account, target_account)
|
||||||
begin
|
begin
|
||||||
target_account = ResolveAccountService.new.call(target_account, skip_webfinger: false)
|
target_account = ResolveAccountService.new.call(target_account, skip_webfinger: true)
|
||||||
|
target_account ||= ResolveAccountService.new.call(target_account, skip_webfinger: false)
|
||||||
rescue
|
rescue
|
||||||
target_account = nil
|
target_account = nil
|
||||||
end
|
end
|
||||||
|
@ -14,9 +15,7 @@ class AccountSubscribeService < BaseService
|
||||||
raise ActiveRecord::RecordNotFound if target_account.nil? || target_account.id == source_account.id || target_account.suspended?
|
raise ActiveRecord::RecordNotFound if target_account.nil? || target_account.id == source_account.id || target_account.suspended?
|
||||||
raise Mastodon::NotPermittedError if target_account.blocking?(source_account) || source_account.blocking?(target_account) || (!target_account.local? && target_account.ostatus?) || source_account.domain_blocking?(target_account.domain)
|
raise Mastodon::NotPermittedError if target_account.blocking?(source_account) || source_account.blocking?(target_account) || (!target_account.local? && target_account.ostatus?) || source_account.domain_blocking?(target_account.domain)
|
||||||
|
|
||||||
if source_account.following?(target_account)
|
if source_account.subscribing?(target_account)
|
||||||
return
|
|
||||||
elsif source_account.subscribing?(target_account)
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -234,6 +234,7 @@ class DeleteAccountService < BaseService
|
||||||
@account.statuses_count = 0
|
@account.statuses_count = 0
|
||||||
@account.followers_count = 0
|
@account.followers_count = 0
|
||||||
@account.following_count = 0
|
@account.following_count = 0
|
||||||
|
@account.subscribing_count = 0
|
||||||
@account.moved_to_account = nil
|
@account.moved_to_account = nil
|
||||||
@account.also_known_as = []
|
@account.also_known_as = []
|
||||||
@account.trust_level = :untrusted
|
@account.trust_level = :untrusted
|
||||||
|
|
|
@ -79,7 +79,6 @@ class FollowService < BaseService
|
||||||
def direct_follow!
|
def direct_follow!
|
||||||
follow = @source_account.follow!(@target_account, reblogs: @options[:reblogs], notify: @options[:notify], rate_limit: @options[:with_rate_limit], bypass_limit: @options[:bypass_limit])
|
follow = @source_account.follow!(@target_account, reblogs: @options[:reblogs], notify: @options[:notify], rate_limit: @options[:with_rate_limit], bypass_limit: @options[:bypass_limit])
|
||||||
|
|
||||||
UnsubscribeAccountService.new.call(@source_account, @target_account) if @source_account.subscribing?(@target_account)
|
|
||||||
LocalNotificationWorker.perform_async(@target_account.id, follow.id, follow.class.name, :follow)
|
LocalNotificationWorker.perform_async(@target_account.id, follow.id, follow.class.name, :follow)
|
||||||
MergeWorker.perform_async(@target_account.id, @source_account.id)
|
MergeWorker.perform_async(@target_account.id, @source_account.id)
|
||||||
|
|
||||||
|
|
|
@ -119,6 +119,7 @@ class SearchService < BaseService
|
||||||
blocked_by: Account.blocked_by_map(account_ids, account.id),
|
blocked_by: Account.blocked_by_map(account_ids, account.id),
|
||||||
muting: Account.muting_map(account_ids, account.id),
|
muting: Account.muting_map(account_ids, account.id),
|
||||||
following: Account.following_map(account_ids, account.id),
|
following: Account.following_map(account_ids, account.id),
|
||||||
|
subscribing: Account.subscribing_map(account_ids, account.id),
|
||||||
domain_blocking_by_domain: Account.domain_blocking_map_by_domain(domains, account.id),
|
domain_blocking_by_domain: Account.domain_blocking_map_by_domain(domains, account.id),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :setting_unfollow_modal, as: :boolean, wrapper: :with_label
|
= f.input :setting_unfollow_modal, as: :boolean, wrapper: :with_label
|
||||||
|
= f.input :setting_unsubscribe_modal, as: :boolean, wrapper: :with_label
|
||||||
= f.input :setting_boost_modal, as: :boolean, wrapper: :with_label
|
= f.input :setting_boost_modal, as: :boolean, wrapper: :with_label
|
||||||
= f.input :setting_delete_modal, as: :boolean, wrapper: :with_label
|
= f.input :setting_delete_modal, as: :boolean, wrapper: :with_label
|
||||||
|
|
||||||
|
|
|
@ -178,6 +178,7 @@ en:
|
||||||
setting_theme: Site theme
|
setting_theme: Site theme
|
||||||
setting_trends: Show today's trends
|
setting_trends: Show today's trends
|
||||||
setting_unfollow_modal: Show confirmation dialog before unfollowing someone
|
setting_unfollow_modal: Show confirmation dialog before unfollowing someone
|
||||||
|
setting_unsubscribe_modal: Show confirmation dialog before unsubscribing someone
|
||||||
setting_use_blurhash: Show colorful gradients for hidden media
|
setting_use_blurhash: Show colorful gradients for hidden media
|
||||||
setting_use_pending_items: Slow mode
|
setting_use_pending_items: Slow mode
|
||||||
severity: Severity
|
severity: Severity
|
||||||
|
|
|
@ -178,6 +178,7 @@ ja:
|
||||||
setting_theme: サイトテーマ
|
setting_theme: サイトテーマ
|
||||||
setting_trends: 本日のトレンドタグを表示する
|
setting_trends: 本日のトレンドタグを表示する
|
||||||
setting_unfollow_modal: フォローを解除する前に確認ダイアログを表示する
|
setting_unfollow_modal: フォローを解除する前に確認ダイアログを表示する
|
||||||
|
setting_unsubscribe_modal: 購読を解除する前に確認ダイアログを表示する
|
||||||
setting_use_blurhash: 非表示のメディアを色付きのぼかしで表示する
|
setting_use_blurhash: 非表示のメディアを色付きのぼかしで表示する
|
||||||
setting_use_pending_items: 手動更新モード
|
setting_use_pending_items: 手動更新モード
|
||||||
severity: 重大性
|
severity: 重大性
|
||||||
|
|
|
@ -451,6 +451,7 @@ Rails.application.routes.draw do
|
||||||
resource :search, only: :show, controller: :search
|
resource :search, only: :show, controller: :search
|
||||||
resource :lookup, only: :show, controller: :lookup
|
resource :lookup, only: :show, controller: :lookup
|
||||||
resources :relationships, only: :index
|
resources :relationships, only: :index
|
||||||
|
resources :subscribing, only: :index, controller: 'subscribing_accounts'
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :accounts, only: [:create, :show] do
|
resources :accounts, only: [:create, :show] do
|
||||||
|
@ -464,6 +465,8 @@ Rails.application.routes.draw do
|
||||||
member do
|
member do
|
||||||
post :follow
|
post :follow
|
||||||
post :unfollow
|
post :unfollow
|
||||||
|
post :subscribe
|
||||||
|
post :unsubscribe
|
||||||
post :block
|
post :block
|
||||||
post :unblock
|
post :unblock
|
||||||
post :mute
|
post :mute
|
||||||
|
@ -486,7 +489,6 @@ Rails.application.routes.draw do
|
||||||
resources :featured_tags, only: [:index, :create, :destroy]
|
resources :featured_tags, only: [:index, :create, :destroy]
|
||||||
resources :favourite_tags, only: [:index, :create, :show, :update, :destroy]
|
resources :favourite_tags, only: [:index, :create, :show, :update, :destroy]
|
||||||
resources :follow_tags, only: [:index, :create, :show, :update, :destroy]
|
resources :follow_tags, only: [:index, :create, :show, :update, :destroy]
|
||||||
resources :account_subscribes, only: [:index, :create, :show, :update, :destroy]
|
|
||||||
resources :domain_subscribes, only: [:index, :create, :show, :update, :destroy]
|
resources :domain_subscribes, only: [:index, :create, :show, :update, :destroy]
|
||||||
resources :keyword_subscribes, only: [:index, :create, :show, :update, :destroy]
|
resources :keyword_subscribes, only: [:index, :create, :show, :update, :destroy]
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ defaults: &defaults
|
||||||
default_sensitive: false
|
default_sensitive: false
|
||||||
hide_network: false
|
hide_network: false
|
||||||
unfollow_modal: false
|
unfollow_modal: false
|
||||||
|
unsubscribe_modal: false
|
||||||
boost_modal: false
|
boost_modal: false
|
||||||
delete_modal: true
|
delete_modal: true
|
||||||
auto_play_gif: false
|
auto_play_gif: false
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
class AddSubscribingCountToAccountStat < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
add_column :account_stats, :subscribing_count, :bigint, null: false, default: 0
|
||||||
|
end
|
||||||
|
end
|
|
@ -111,6 +111,7 @@ ActiveRecord::Schema.define(version: 2021_08_08_071221) do
|
||||||
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.datetime "last_status_at"
|
t.datetime "last_status_at"
|
||||||
|
t.bigint "subscribing_count", default: 0, null: false
|
||||||
t.index ["account_id"], name: "index_account_stats_on_account_id", unique: true
|
t.index ["account_id"], name: "index_account_stats_on_account_id", unique: true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -32,10 +32,11 @@ module Mastodon
|
||||||
case type
|
case type
|
||||||
when 'accounts'
|
when 'accounts'
|
||||||
processed, = parallelize_with_progress(Account.local.includes(:account_stat)) do |account|
|
processed, = parallelize_with_progress(Account.local.includes(:account_stat)) do |account|
|
||||||
account_stat = account.account_stat
|
account_stat = account.account_stat
|
||||||
account_stat.following_count = account.active_relationships.count
|
account_stat.following_count = account.active_relationships.count
|
||||||
account_stat.followers_count = account.passive_relationships.count
|
account_stat.followers_count = account.passive_relationships.count
|
||||||
account_stat.statuses_count = account.statuses.where.not(visibility: :direct).count
|
account_stat.subscribing_count = account.active_subscribes.count
|
||||||
|
account_stat.statuses_count = account.statuses.where.not(visibility: :direct).count
|
||||||
|
|
||||||
account_stat.save if account_stat.changed?
|
account_stat.save if account_stat.changed?
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,7 +6,7 @@ RSpec.describe Settings::ScopedSettings do
|
||||||
let(:object) { Fabricate(:user) }
|
let(:object) { Fabricate(:user) }
|
||||||
let(:scoped_setting) { described_class.new(object) }
|
let(:scoped_setting) { described_class.new(object) }
|
||||||
let(:val) { 'whatever' }
|
let(:val) { 'whatever' }
|
||||||
let(:methods) { %i(auto_play_gif default_sensitive unfollow_modal boost_modal delete_modal reduce_motion system_font_ui noindex theme) }
|
let(:methods) { %i(auto_play_gif default_sensitive unfollow_modal unsubscribe_modal boost_modal delete_modal reduce_motion system_font_ui noindex theme) }
|
||||||
|
|
||||||
describe '.initialize' do
|
describe '.initialize' do
|
||||||
it 'sets @object' do
|
it 'sets @object' do
|
||||||
|
|
|
@ -42,6 +42,13 @@ describe UserSettingsDecorator do
|
||||||
expect(user.settings['unfollow_modal']).to eq false
|
expect(user.settings['unfollow_modal']).to eq false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'updates the user settings value for unsubscribe modal' do
|
||||||
|
values = { 'setting_unsubscribe_modal' => '0' }
|
||||||
|
|
||||||
|
settings.update(values)
|
||||||
|
expect(user.settings['unsubscribe_modal']).to eq false
|
||||||
|
end
|
||||||
|
|
||||||
it 'updates the user settings value for boost modal' do
|
it 'updates the user settings value for boost modal' do
|
||||||
values = { 'setting_boost_modal' => '1' }
|
values = { 'setting_boost_modal' => '1' }
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue