Mastodonを読む/フォロー時の処理その1(クライアント側)
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
[[Mastodonを読む]]
#contents
*はじめに [#c0102fb7]
新規トゥートについて見た際、送信したトゥートをフォロワー...
*フォロー処理トリガーの確認 [#ree518f7]
まずはユーザのフォローがどのコンポーネントで行われており...
**開発環境でのユーザの作り方 [#s2fee665]
でもその前に。開発環境ではデフォルトではadminユーザしかい...
運用環境のMastodonの場合はユーザ登録すると入力したメール...
一方、開発環境(Vagrant)ではletter_openerというgemが導入...
**フォローするユーザの発見 [#b16763c3]
では改めてフォローするためのユーザを作ったとして、まずは...
そもそものユーザをどうやって見つけるのかのところから。方...
-検索で探す
-ローカルタイムラインで見かける
などが考えられます。検索を追っかけていくとめんどくさそう...
**app/assets/javascripts/components/components/status.jsx...
タイムライン表示の時に確認したように、ホーム、ローカル、...
#code{{
render () {
let media = '';
const { status, ...other } = this.props;
if (status === null) {
return <div />;
}
if (status.get('reblog', null) !== null && typeof sta...
省略
}
if (status.get('media_attachments').size > 0 && !this...
省略
}
return (
<div className={this.props.muted ? 'status muted' :...
<div className='status__info'>
<div className='status__info-time'>
<a href={status.get('url')} className='status...
</div>
<a onClick={this.handleAccountClick.bind(this, ...
<div className='status__avatar'>
<Avatar src={status.getIn(['account', 'avat...
</div>
<DisplayName account={status.get('account')} />
</a>
</div>
<StatusContent status={status} onClick={this.hand...
{media}
<StatusActionBar {...this.props} />
</div>
);
}
}}
といいつつ長いですが、さらにピックアップするとアカウント...
&ref(status-account-click.png);
#code{{
<a onClick={this.handleAccountClick.bind(this, ...
<div className='status__avatar'>
<Avatar src={status.getIn(['account', 'avat...
</div>
<DisplayName account={status.get('account')} />
</a>
}}
というわけで、クリックするとhandleAccountClickメソッドが...
#code{{
handleAccountClick (id, e) {
if (e.button === 0) {
e.preventDefault();
this.context.router.push(`/accounts/${id}`);
}
}
}}
となっています。ふぅむ、アカウント部分をポイントすると「@...
routerというのは、React Routerのことでしょう。pushとある...
Mastodonクラスに戻って、「/accounts/id」に対応するクラス...
#code{{
<Route path='accounts/:accountId' component...
}}
とAccountTimelineクラスが使われていることがわかります。
**app/assets/javascripts/components/features/account_time...
というわけで、AccountTimelineに移動。enderメソッド
#code{{
render () {
const { statusIds, isLoading, hasMore, me } = this.pr...
if (!statusIds && isLoading) {
return (
<Column>
<LoadingIndicator />
</Column>
);
}
return (
<Column>
<ColumnBackButton />
<StatusList
prepend={<HeaderContainer accountId={this.props...
scrollKey='account_timeline'
statusIds={statusIds}
isLoading={isLoading}
hasMore={hasMore}
me={me}
onScrollToBottom={this.handleScrollToBottom}
/>
</Column>
);
}
}}
AccountTimelineは以下のように表示されます。
&ref(account-timeline.png);
今回興味があるのは上部のユーザ情報部分にあるフォローボタ...
***app/assets/javascripts/components/features/account_tim...
イベントハンドラが書いてあってコンポーネント自体の定義はH...
***app/assets/javascripts/components/features/account_tim...
Heeaderクラスのrenderメソッド。
#code{{
render () {
const { account, me } = this.props;
if (account === null) {
return <MissingIndicator />;
}
return (
<div className='account-timeline__header'>
<InnerHeader
account={account}
me={me}
onFollow={this.handleFollow}
/>
<ActionBar
account={account}
me={me}
onBlock={this.handleBlock}
onMention={this.handleMention}
onReport={this.handleReport}
onMute={this.handleMute}
/>
</div>
);
}
}}
ActionBarって下にあるハンバーガーメニューだろうので、Inne...
#code{{
import InnerHeader from '../../account/components/header';
}}
と、features/account/componentsにあるheader.jsxで定義され...
***app/assets/javascripts/components/features/account/com...
renderメソッド全部載せると長いので要点だけ、
#code{{
if (me !== account.get('id')) {
if (account.getIn(['relationship', 'requested'])) {
省略
} else if (!account.getIn(['relationship', 'blockin...
actionBtn = (
<div style={ { position: 'absolute', top: '10px...
<IconButton size={26} icon={account.getIn(['r...
active={account.getIn(['relationship', 'fol...
title={intl.formatMessage(account.getIn(['r...
onClick={this.props.onFollow} />
</div>
);
}
}
}}
ここがフォローボタンを出力しているコードです。で、クリッ...
***イベント発生時のフロー [#b6612372]
コンポーネントを構築する際は徐々に細かいパーツに描画処理...
クリックなどのイベントが発生すると、今度は逆にイベントが...
app/assets/javascripts/components/features/account/compon...
#code{{
<div style={ { position: 'absolute', top: '10px...
<IconButton size={26} icon={account.getIn(['r...
active={account.getIn(['relationship', 'fol...
title={intl.formatMessage(account.getIn(['r...
onClick={this.props.onFollow} />
</div>
}}
app/assets/javascripts/components/features/account_timeli...
#code{{
handleFollow () {
this.props.onFollow(this.props.account);
}
render () {
省略
return (
<div className='account-timeline__header'>
<InnerHeader
account={account}
me={me}
onFollow={this.handleFollow}
/>
省略
</div>
);
}
}}
app/assets/javascripts/components/features/account_timeli...
#code{{
const mapDispatchToProps = (dispatch, { intl }) => ({
onFollow (account) {
if (account.getIn(['relationship', 'following'])) {
dispatch(unfollowAccount(account.get('id')));
} else {
dispatch(followAccount(account.get('id')));
}
},
省略
});
export default injectIntl(connect(makeMapStateToProps, ma...
}}
と、ここまでさかのぼったところでdispatchが出てきました。...
**app/assets/javascripts/components/actions/accoounts.jsx...
followAccountが書いてありそうなもの、ということでactions...
#code{{
export function followAccount(id) {
return (dispatch, getState) => {
dispatch(followAccountRequest(id));
api(getState).post(`/api/v1/accounts/${id}/follow`).t...
dispatch(followAccountSuccess(response.data));
}).catch(error => {
dispatch(followAccountFail(error));
});
};
};
}}
ありました。というわけで、フォローボタンが押されるとサー...
けっこう長くなったので一旦ここで切ります。
*おわりに [#p910a8ab]
今回はユーザをフォローするにあたり、
-そもそもユーザ画面はどうやって表示されるのか
--トゥート(Status)からたどる場合、アカウント名をクリッ...
-フォローボタンは誰が表示しているのか、ボタンをクリックす...
を見てきました。だいぶ慣れてきましたが、コンポーネントの...
終了行:
[[Mastodonを読む]]
#contents
*はじめに [#c0102fb7]
新規トゥートについて見た際、送信したトゥートをフォロワー...
*フォロー処理トリガーの確認 [#ree518f7]
まずはユーザのフォローがどのコンポーネントで行われており...
**開発環境でのユーザの作り方 [#s2fee665]
でもその前に。開発環境ではデフォルトではadminユーザしかい...
運用環境のMastodonの場合はユーザ登録すると入力したメール...
一方、開発環境(Vagrant)ではletter_openerというgemが導入...
**フォローするユーザの発見 [#b16763c3]
では改めてフォローするためのユーザを作ったとして、まずは...
そもそものユーザをどうやって見つけるのかのところから。方...
-検索で探す
-ローカルタイムラインで見かける
などが考えられます。検索を追っかけていくとめんどくさそう...
**app/assets/javascripts/components/components/status.jsx...
タイムライン表示の時に確認したように、ホーム、ローカル、...
#code{{
render () {
let media = '';
const { status, ...other } = this.props;
if (status === null) {
return <div />;
}
if (status.get('reblog', null) !== null && typeof sta...
省略
}
if (status.get('media_attachments').size > 0 && !this...
省略
}
return (
<div className={this.props.muted ? 'status muted' :...
<div className='status__info'>
<div className='status__info-time'>
<a href={status.get('url')} className='status...
</div>
<a onClick={this.handleAccountClick.bind(this, ...
<div className='status__avatar'>
<Avatar src={status.getIn(['account', 'avat...
</div>
<DisplayName account={status.get('account')} />
</a>
</div>
<StatusContent status={status} onClick={this.hand...
{media}
<StatusActionBar {...this.props} />
</div>
);
}
}}
といいつつ長いですが、さらにピックアップするとアカウント...
&ref(status-account-click.png);
#code{{
<a onClick={this.handleAccountClick.bind(this, ...
<div className='status__avatar'>
<Avatar src={status.getIn(['account', 'avat...
</div>
<DisplayName account={status.get('account')} />
</a>
}}
というわけで、クリックするとhandleAccountClickメソッドが...
#code{{
handleAccountClick (id, e) {
if (e.button === 0) {
e.preventDefault();
this.context.router.push(`/accounts/${id}`);
}
}
}}
となっています。ふぅむ、アカウント部分をポイントすると「@...
routerというのは、React Routerのことでしょう。pushとある...
Mastodonクラスに戻って、「/accounts/id」に対応するクラス...
#code{{
<Route path='accounts/:accountId' component...
}}
とAccountTimelineクラスが使われていることがわかります。
**app/assets/javascripts/components/features/account_time...
というわけで、AccountTimelineに移動。enderメソッド
#code{{
render () {
const { statusIds, isLoading, hasMore, me } = this.pr...
if (!statusIds && isLoading) {
return (
<Column>
<LoadingIndicator />
</Column>
);
}
return (
<Column>
<ColumnBackButton />
<StatusList
prepend={<HeaderContainer accountId={this.props...
scrollKey='account_timeline'
statusIds={statusIds}
isLoading={isLoading}
hasMore={hasMore}
me={me}
onScrollToBottom={this.handleScrollToBottom}
/>
</Column>
);
}
}}
AccountTimelineは以下のように表示されます。
&ref(account-timeline.png);
今回興味があるのは上部のユーザ情報部分にあるフォローボタ...
***app/assets/javascripts/components/features/account_tim...
イベントハンドラが書いてあってコンポーネント自体の定義はH...
***app/assets/javascripts/components/features/account_tim...
Heeaderクラスのrenderメソッド。
#code{{
render () {
const { account, me } = this.props;
if (account === null) {
return <MissingIndicator />;
}
return (
<div className='account-timeline__header'>
<InnerHeader
account={account}
me={me}
onFollow={this.handleFollow}
/>
<ActionBar
account={account}
me={me}
onBlock={this.handleBlock}
onMention={this.handleMention}
onReport={this.handleReport}
onMute={this.handleMute}
/>
</div>
);
}
}}
ActionBarって下にあるハンバーガーメニューだろうので、Inne...
#code{{
import InnerHeader from '../../account/components/header';
}}
と、features/account/componentsにあるheader.jsxで定義され...
***app/assets/javascripts/components/features/account/com...
renderメソッド全部載せると長いので要点だけ、
#code{{
if (me !== account.get('id')) {
if (account.getIn(['relationship', 'requested'])) {
省略
} else if (!account.getIn(['relationship', 'blockin...
actionBtn = (
<div style={ { position: 'absolute', top: '10px...
<IconButton size={26} icon={account.getIn(['r...
active={account.getIn(['relationship', 'fol...
title={intl.formatMessage(account.getIn(['r...
onClick={this.props.onFollow} />
</div>
);
}
}
}}
ここがフォローボタンを出力しているコードです。で、クリッ...
***イベント発生時のフロー [#b6612372]
コンポーネントを構築する際は徐々に細かいパーツに描画処理...
クリックなどのイベントが発生すると、今度は逆にイベントが...
app/assets/javascripts/components/features/account/compon...
#code{{
<div style={ { position: 'absolute', top: '10px...
<IconButton size={26} icon={account.getIn(['r...
active={account.getIn(['relationship', 'fol...
title={intl.formatMessage(account.getIn(['r...
onClick={this.props.onFollow} />
</div>
}}
app/assets/javascripts/components/features/account_timeli...
#code{{
handleFollow () {
this.props.onFollow(this.props.account);
}
render () {
省略
return (
<div className='account-timeline__header'>
<InnerHeader
account={account}
me={me}
onFollow={this.handleFollow}
/>
省略
</div>
);
}
}}
app/assets/javascripts/components/features/account_timeli...
#code{{
const mapDispatchToProps = (dispatch, { intl }) => ({
onFollow (account) {
if (account.getIn(['relationship', 'following'])) {
dispatch(unfollowAccount(account.get('id')));
} else {
dispatch(followAccount(account.get('id')));
}
},
省略
});
export default injectIntl(connect(makeMapStateToProps, ma...
}}
と、ここまでさかのぼったところでdispatchが出てきました。...
**app/assets/javascripts/components/actions/accoounts.jsx...
followAccountが書いてありそうなもの、ということでactions...
#code{{
export function followAccount(id) {
return (dispatch, getState) => {
dispatch(followAccountRequest(id));
api(getState).post(`/api/v1/accounts/${id}/follow`).t...
dispatch(followAccountSuccess(response.data));
}).catch(error => {
dispatch(followAccountFail(error));
});
};
};
}}
ありました。というわけで、フォローボタンが押されるとサー...
けっこう長くなったので一旦ここで切ります。
*おわりに [#p910a8ab]
今回はユーザをフォローするにあたり、
-そもそもユーザ画面はどうやって表示されるのか
--トゥート(Status)からたどる場合、アカウント名をクリッ...
-フォローボタンは誰が表示しているのか、ボタンをクリックす...
を見てきました。だいぶ慣れてきましたが、コンポーネントの...
ページ名: