Add relationship action buttons to WebUI status

This commit is contained in:
noellabo 2020-01-11 20:24:22 +09:00
parent 3bcbbb7463
commit 3e31123c2a
14 changed files with 219 additions and 43 deletions

View file

@ -56,6 +56,9 @@ class Settings::PreferencesController < Settings::BaseController
:setting_use_pending_items,
:setting_trends,
:setting_crop_images,
:setting_show_follow_button_on_timeline,
:setting_show_subscribe_button_on_timeline,
:setting_show_target,
notification_emails: %i(follow follow_request reblog favourite mention digest report pending_account trending_tag),
interactions: %i(must_be_follower must_be_following must_be_following_dm)
)

View file

@ -0,0 +1,73 @@
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import IconButton from './icon_button';
import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { me, show_follow_button_on_timeline, show_subscribe_button_on_timeline } from '../initial_state';
const messages = defineMessages({
follow: { id: 'account.follow', defaultMessage: 'Follow' },
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' },
});
export default @injectIntl
class AccountActionBar extends ImmutablePureComponent {
static propTypes = {
account: ImmutablePropTypes.map.isRequired,
onFollow: PropTypes.func.isRequired,
onSubscribe: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
updateOnProps = [
'account',
]
handleFollow = () => {
this.props.onFollow(this.props.account);
}
handleSubscribe = () => {
this.props.onSubscribe(this.props.account);
}
render () {
const { account, intl } = this.props;
if (!account || (!show_follow_button_on_timeline && !show_subscribe_button_on_timeline)) {
return <div />;
}
let buttons, following_buttons, subscribing_buttons;
if (account.get('id') !== me && account.get('relationship', null) !== null) {
const following = account.getIn(['relationship', 'following']);
const subscribing = account.getIn(['relationship', 'subscribing']);
const requested = account.getIn(['relationship', 'requested']);
if (show_subscribe_button_on_timeline && (!account.get('moved') || subscribing)) {
subscribing_buttons = <IconButton icon='rss-square' title={intl.formatMessage(subscribing ? messages.unsubscribe : messages.subscribe)} onClick={this.handleSubscribe} active={subscribing} />;
}
if (show_follow_button_on_timeline && (!account.get('moved') || following)) {
if (requested) {
following_buttons = <IconButton disabled icon='hourglass' title={intl.formatMessage(messages.requested)} />;
} else {
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>
}
return (
<div className='account__action-bar'>
{buttons}
</div>
);
}
}

View file

@ -9,6 +9,7 @@ import RelativeTimestamp from './relative_timestamp';
import DisplayName from './display_name';
import StatusContent from './status_content';
import StatusActionBar from './status_action_bar';
import AccountActionBar from './account_action_bar';
import AttachmentList from './attachment_list';
import Card from '../features/status/components/card';
import { injectIntl, defineMessages, FormattedMessage } from 'react-intl';
@ -112,6 +113,8 @@ class Status extends ImmutablePureComponent {
onToggleHidden: PropTypes.func,
onToggleCollapsed: PropTypes.func,
onQuoteToggleHidden: PropTypes.func,
onFollow: PropTypes.func.isRequired,
onSubscribe: PropTypes.func.isRequired,
muted: PropTypes.bool,
hidden: PropTypes.bool,
unread: PropTypes.bool,
@ -346,6 +349,14 @@ class Status extends ImmutablePureComponent {
this.node = c;
}
handleFollow = () => {
this.props.onFollow(this._properStatus().get('account'));
}
handleSubscribe = () => {
this.props.onSubscribe(this._properStatus().get('account'));
}
render () {
let media = null;
let statusAvatar, prepend, rebloggedByText;
@ -658,6 +669,7 @@ class Status extends ImmutablePureComponent {
{prepend}
<div className={classNames('status', `status-${status.get('visibility')}`, { 'status-reply': !!status.get('in_reply_to_id'), muted: this.props.muted })} data-id={status.get('id')}>
<AccountActionBar account={status.get('account')} {...other} />
<div className='status__expand' onClick={this.handleExpandClick} role='presentation' />
<div className='status__info'>
<a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener noreferrer'>

View file

@ -29,6 +29,10 @@ import {
revealQuote,
} from '../actions/statuses';
import {
followAccount,
unfollowAccount,
subscribeAccount,
unsubscribeAccount,
unmuteAccount,
unblockAccount,
} from '../actions/accounts';
@ -36,6 +40,7 @@ import {
blockDomain,
unblockDomain,
} from '../actions/domain_blocks';
import { initMuteModal } from '../actions/mutes';
import { initBlockModal } from '../actions/blocks';
import { initBoostModal } from '../actions/boosts';
@ -43,7 +48,7 @@ import { initReport } from '../actions/reports';
import { openModal } from '../actions/modal';
import { deployPictureInPicture } from '../actions/picture_in_picture';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { boostModal, deleteModal } from '../initial_state';
import { boostModal, deleteModal, unfollowModal, unsubscribeModal } from '../initial_state';
import { showAlertForError } from '../actions/alerts';
const messages = defineMessages({
@ -56,6 +61,8 @@ const messages = defineMessages({
quoteConfirm: { id: 'confirmations.quote.confirm', defaultMessage: 'Quote' },
quoteMessage: { id: 'confirmations.quote.message', defaultMessage: 'Quoting now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' },
unsubscribeConfirm: { id: 'confirmations.unsubscribe.confirm', defaultMessage: 'Unsubscribe' },
});
const makeMapStateToProps = () => {
@ -244,6 +251,37 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
}
},
onFollow (account) {
if (account.getIn(['relationship', 'following']) || account.getIn(['relationship', 'requested'])) {
if (unfollowModal) {
dispatch(openModal('CONFIRM', {
message: <FormattedMessage id='confirmations.unfollow.message' defaultMessage='Are you sure you want to unfollow {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
confirm: intl.formatMessage(messages.unfollowConfirm),
onConfirm: () => dispatch(unfollowAccount(account.get('id'))),
}));
} else {
dispatch(unfollowAccount(account.get('id')));
}
} else {
dispatch(followAccount(account.get('id')));
}
},
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')));
}
},
});
export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Status));

View file

@ -28,5 +28,8 @@ export const showTrends = getMeta('trends');
export const title = getMeta('title');
export const cropImages = getMeta('crop_images');
export const disableSwiping = getMeta('disable_swiping');
export const show_follow_button_on_timeline = getMeta('show_follow_button_on_timeline');
export const show_subscribe_button_on_timeline = getMeta('show_subscribe_button_on_timeline');
export const show_target = getMeta('show_target');
export default initialState;

View file

@ -15,31 +15,34 @@ class UserSettingsDecorator
private
def process_update
user.settings['notification_emails'] = merged_notification_emails if change?('notification_emails')
user.settings['interactions'] = merged_interactions if change?('interactions')
user.settings['default_privacy'] = default_privacy_preference if change?('setting_default_privacy')
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['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['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['display_media'] = display_media_preference if change?('setting_display_media')
user.settings['expand_spoilers'] = expand_spoilers_preference if change?('setting_expand_spoilers')
user.settings['reduce_motion'] = reduce_motion_preference if change?('setting_reduce_motion')
user.settings['disable_swiping'] = disable_swiping_preference if change?('setting_disable_swiping')
user.settings['system_font_ui'] = system_font_ui_preference if change?('setting_system_font_ui')
user.settings['noindex'] = noindex_preference if change?('setting_noindex')
user.settings['theme'] = theme_preference if change?('setting_theme')
user.settings['hide_network'] = hide_network_preference if change?('setting_hide_network')
user.settings['aggregate_reblogs'] = aggregate_reblogs_preference if change?('setting_aggregate_reblogs')
user.settings['show_application'] = show_application_preference if change?('setting_show_application')
user.settings['advanced_layout'] = advanced_layout_preference if change?('setting_advanced_layout')
user.settings['use_blurhash'] = use_blurhash_preference if change?('setting_use_blurhash')
user.settings['use_pending_items'] = use_pending_items_preference if change?('setting_use_pending_items')
user.settings['trends'] = trends_preference if change?('setting_trends')
user.settings['crop_images'] = crop_images_preference if change?('setting_crop_images')
user.settings['notification_emails'] = merged_notification_emails if change?('notification_emails')
user.settings['interactions'] = merged_interactions if change?('interactions')
user.settings['default_privacy'] = default_privacy_preference if change?('setting_default_privacy')
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['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['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['display_media'] = display_media_preference if change?('setting_display_media')
user.settings['expand_spoilers'] = expand_spoilers_preference if change?('setting_expand_spoilers')
user.settings['reduce_motion'] = reduce_motion_preference if change?('setting_reduce_motion')
user.settings['disable_swiping'] = disable_swiping_preference if change?('setting_disable_swiping')
user.settings['system_font_ui'] = system_font_ui_preference if change?('setting_system_font_ui')
user.settings['noindex'] = noindex_preference if change?('setting_noindex')
user.settings['theme'] = theme_preference if change?('setting_theme')
user.settings['hide_network'] = hide_network_preference if change?('setting_hide_network')
user.settings['aggregate_reblogs'] = aggregate_reblogs_preference if change?('setting_aggregate_reblogs')
user.settings['show_application'] = show_application_preference if change?('setting_show_application')
user.settings['advanced_layout'] = advanced_layout_preference if change?('setting_advanced_layout')
user.settings['use_blurhash'] = use_blurhash_preference if change?('setting_use_blurhash')
user.settings['use_pending_items'] = use_pending_items_preference if change?('setting_use_pending_items')
user.settings['trends'] = trends_preference if change?('setting_trends')
user.settings['crop_images'] = crop_images_preference if change?('setting_crop_images')
user.settings['show_follow_button_on_timeline'] = show_follow_button_on_timeline_preference if change?('setting_show_follow_button_on_timeline')
user.settings['show_subscribe_button_on_timeline'] = show_subscribe_button_on_timeline_preference if change?('setting_show_subscribe_button_on_timeline')
user.settings['show_target'] = show_target_preference if change?('setting_show_target')
end
def merged_notification_emails
@ -142,6 +145,18 @@ class UserSettingsDecorator
boolean_cast_setting 'setting_crop_images'
end
def show_follow_button_on_timeline_preference
boolean_cast_setting 'setting_show_follow_button_on_timeline'
end
def show_subscribe_button_on_timeline_preference
boolean_cast_setting 'setting_show_subscribe_button_on_timeline'
end
def show_target_preference
boolean_cast_setting 'setting_show_target'
end
def boolean_cast_setting(key)
ActiveModel::Type::Boolean.new.cast(settings[key])
end

View file

@ -126,6 +126,7 @@ class User < ApplicationRecord
:expand_spoilers, :default_language, :aggregate_reblogs, :show_application,
:advanced_layout, :use_blurhash, :use_pending_items, :trends, :crop_images,
:disable_swiping,
:show_follow_button_on_timeline, :show_subscribe_button_on_timeline, :show_target,
to: :settings, prefix: :setting, allow_nil: false
attr_reader :invite_code, :sign_in_token_attempt

View file

@ -26,23 +26,26 @@ class InitialStateSerializer < ActiveModel::Serializer
}
if object.current_account
store[:me] = object.current_account.id.to_s
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[:delete_modal] = object.current_account.user.setting_delete_modal
store[:auto_play_gif] = object.current_account.user.setting_auto_play_gif
store[:display_media] = object.current_account.user.setting_display_media
store[:expand_spoilers] = object.current_account.user.setting_expand_spoilers
store[:reduce_motion] = object.current_account.user.setting_reduce_motion
store[:disable_swiping] = object.current_account.user.setting_disable_swiping
store[:advanced_layout] = object.current_account.user.setting_advanced_layout
store[:use_blurhash] = object.current_account.user.setting_use_blurhash
store[:use_pending_items] = object.current_account.user.setting_use_pending_items
store[:is_staff] = object.current_account.user.staff?
store[:trends] = Setting.trends && object.current_account.user.setting_trends
store[:crop_images] = object.current_account.user.setting_crop_images
else
store[:me] = object.current_account.id.to_s
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[:delete_modal] = object.current_account.user.setting_delete_modal
store[:auto_play_gif] = object.current_account.user.setting_auto_play_gif
store[:display_media] = object.current_account.user.setting_display_media
store[:expand_spoilers] = object.current_account.user.setting_expand_spoilers
store[:reduce_motion] = object.current_account.user.setting_reduce_motion
store[:disable_swiping] = object.current_account.user.setting_disable_swiping
store[:advanced_layout] = object.current_account.user.setting_advanced_layout
store[:use_blurhash] = object.current_account.user.setting_use_blurhash
store[:use_pending_items] = object.current_account.user.setting_use_pending_items
store[:is_staff] = object.current_account.user.staff?
store[:trends] = Setting.trends && object.current_account.user.setting_trends
store[:crop_images] = object.current_account.user.setting_crop_images
store[:show_follow_button_on_timeline] = object.current_account.user.setting_show_follow_button_on_timeline
store[:show_subscribe_button_on_timeline] = object.current_account.user.setting_show_subscribe_button_on_timeline
store[:show_target] = object.current_account.user.setting_show_target
else
store[:auto_play_gif] = Setting.auto_play_gif
store[:display_media] = Setting.display_media
store[:reduce_motion] = Setting.reduce_motion

View file

@ -31,6 +31,17 @@
.fields-group
= f.input :setting_show_application, as: :boolean, wrapper: :with_label, recommended: true
%h4= t 'preferences.fedibird_features'
.fields-group
= f.input :setting_show_follow_button_on_timeline, as: :boolean, wrapper: :with_label
.fields-group
= f.input :setting_show_subscribe_button_on_timeline, as: :boolean, wrapper: :with_label
-# .fields-group
-# = f.input :setting_show_target, as: :boolean, wrapper: :with_label
%h4= t 'preferences.public_timelines'
.fields-group

View file

@ -1217,6 +1217,7 @@ en:
too_few_options: must have more than one item
too_many_options: can't contain more than %{max} items
preferences:
fedibird_features: Fedibird features
other: Other
posting_defaults: Posting defaults
public_timelines: Public timelines

View file

@ -1162,6 +1162,7 @@ ja:
too_few_options: は複数必要です
too_many_options: は%{max}個までです
preferences:
fedibird_features: Fedibirdの機能
other: その他
posting_defaults: デフォルトの投稿設定
public_timelines: 公開タイムライン

View file

@ -54,6 +54,9 @@ en:
setting_hide_network: Who you follow and who follows you will be hidden on your profile
setting_noindex: Affects your public profile and post pages
setting_show_application: The application you use to post will be displayed in the detailed view of your posts
setting_show_follow_button_on_timeline: You can easily check the follow status and build a follow list quickly
setting_show_subscribe_button_on_timeline: You can easily check the status of your subscriptions and quickly build a subscription list
setting_show_target: Enable the function to switch between posting target and follow / subscribe target
setting_use_blurhash: Gradients are based on the colors of the hidden visuals but obfuscate any details
setting_use_pending_items: Hide timeline updates behind a click instead of automatically scrolling the feed
username: Your username will be unique on %{domain}
@ -177,6 +180,9 @@ en:
setting_noindex: Opt-out of search engine indexing
setting_reduce_motion: Reduce motion in animations
setting_show_application: Disclose application used to send posts
setting_show_follow_button_on_timeline: Show follow button on timeline
setting_show_subscribe_button_on_timeline: Show subscribe button on timeline
setting_show_target: Enable targeting features
setting_system_font_ui: Use system's default font
setting_theme: Site theme
setting_trends: Show today's trends

View file

@ -54,6 +54,9 @@ ja:
setting_hide_network: フォローとフォロワーの情報がプロフィールページで見られないようにします
setting_noindex: 公開プロフィールおよび各投稿ページに影響します
setting_show_application: 投稿するのに使用したアプリが投稿の詳細ビューに表示されるようになります
setting_show_follow_button_on_timeline: フォロー状態を確認し易くなり、素早くフォローリストを構築できます
setting_show_subscribe_button_on_timeline: 購読状態を確認し易くなり、素早く購読リストを構築できます
setting_show_target: 投稿対象と、フォロー・購読の対象を切り替える機能を有効にします
setting_use_blurhash: ぼかしはメディアの色を元に生成されますが、細部は見えにくくなっています
setting_use_pending_items: 新着があってもタイムラインを自動的にスクロールしないようにします
username: あなたのユーザー名は %{domain} の中で重複していない必要があります
@ -177,6 +180,9 @@ ja:
setting_noindex: 検索エンジンによるインデックスを拒否する
setting_reduce_motion: アニメーションの動きを減らす
setting_show_application: 送信したアプリを開示する
setting_show_follow_button_on_timeline: タイムライン上にフォローボタンを表示する
setting_show_subscribe_button_on_timeline: タイムライン上に購読ボタンを表示する
setting_show_target: ターゲット機能を有効にする
setting_system_font_ui: システムのデフォルトフォントを使う
setting_theme: サイトテーマ
setting_trends: 本日のトレンドタグを表示する

View file

@ -39,6 +39,9 @@ defaults: &defaults
trends: true
trendable_by_default: false
crop_images: true
show_follow_button_on_timeline: false
show_subscribe_button_on_timeline: false
show_target: false
notification_emails:
follow: false
reblog: false