mikutterをいじりたおす 第5回

ついに画像が投稿できるようになります。第1回はこちら

画像投稿実装(後半)

前半はこちら

投稿ボックスの動きを追う

第1回で、core/mui/gtk_postbox.rbの中のpost_itが投稿ボタンをおした時の挙動を定義しているのだろう、と語った。実際に、以下のように実装されていて、どうやらそれで間違いない。

service.postは最終的に別スレッドでstatuses/updateを呼び出すだけなので、画像があったらここをif文でわけてあげればいいだろう、と思ったわけだ。つまり、returnの直後にif文をおいてそのまま実装した。するとなんということだろう、画像が追加されない。@picturesを出力してみると配列が空。一体どういうことなのか。

これはbefore_postにヒントがありそうだ。before_postを見てみよう。

なんということだ、新しいPostBoxを作るのだ。最初にreturn false if delegateとあって、falseが返るパターンはここしかない。すなわち、post_itの最初のunlessが動いて投稿できないのはこのパターンだけになる。ということはdelegateのなかで新しいPostBoxが定義されているのだろう。

delegateの定義を見ると、確かに新しいPostBoxを作っている。というわけで、この中でoptions[:pictures]に目的の配列を渡そう。以下の一行を適当に追加すればよい。

これで新しいインスタンスにpicturesの配列が渡せる。次に、initializeの中で、:picturesが定義されていた場合にのみ@picturesにこれを代入するようにしよう。といっても以下のように書けばよい。

さらに@picturesにもともと配列が入っていたらボタンが追加されるようにしよう。これは@pictureboxを作るときに行うのがよいと思うので、widget_picturebox関数の中で行う。

以下のように呼びだそう。add_pictureは第2引数があれば、そこのハッシュに代入してボタンを作り、@picturesにはそれを追加しない、という定義にすればよさそうだ。

add_pictureはこんな感じ。

これで新しいPostBoxに画像の配列が渡せて、さらにそれのボタンが追加されるようになった。

新しいPostBoxに画像の配列を渡してしまったらもとのPostBoxからは画像を削除しないといけない。そもそも投稿欄の文字も全部削除している。これはどこでやっているかというと、実は第1回で登場したpost_set_default_textなのだ。これのif文の1つめの条件分岐でこの処理をしている。というわけで、ここに元のPostBoxの画像を全部消す処理を追加しておく。

clear_picturesは当然いま考えた関数なので、これも定義しよう。やらなければいけないことは、@picturesを空にすることと、ボタンを削除することだ。こんな感じになるだろうか。

さて、ながながとデバッグをほったらかしてしまったので、post_itの中で@picturesが空でなかったら投稿しないでその@picturesを出力する処理をしよう。あくまでデバッグ用だ。unless @pictures.empty?; p @pictures; return endとでもしておこう。

投稿ボタンを押すと、投稿はされないが新しい投稿欄に画像が移動し、@picturesの配列が出力されたことと思う。ちゃんと@picturesにはファイル名が入っているだろう。

投稿する

さて、下準備は完了した。いよいよ投稿できる。今回はめんどくさいのでmikutterが築いてきた文化を破壊しつつquery!を直接叩く。脳がないのでThreadも作る。ごめんな

実験では投稿時もquery!を叩いていたのが、リプライ先が指定できないなどの問題があった。これを解消するためにlib/mikutwitter/api_shortcuts.rbをいじる。これは「in_reply_to_status」で全ファイル検索するとわかる。ここのupdateにservice.postを叩くとき通る。で、ここのオプションにmedia_idsを指定した時にちゃんと送信先にmedia_idsが送信されるようにしないといけない。

こうすることで、オプションにmedia_idsを指定するとそれが送られるようになる。

次はpost_with_pictures関数の実装。実験では雑実装をしたが、Deferredとかいうものがあるのをやっとしったので、前回紹介したservice.rbに含まれているdefine_postalを参考に以下のように実装した。

ThreadにはDeferred機能がついていて、trapだのnextだの使えるので、mikutterコンソールで遊んでみるとよい。遊べば最初のブロック→nextで指定したブロック→……と進むことがわかるはず。途中で例外が発生すれば(上ではraiseで発生させている)最初のtrapブロックにうつってそこまでのnextは実行されない。遊べることも能力のうち。

などとmikutterコンソールで入力すると標準出力には何が出るか、考えながらやってみると理解に易いだろう。

さて、次はpost_with_picturesをpost_itから呼び出す。と言ってもこれは簡単。むしろ簡単にするためにpost_with_picturesをややこしくしたといっても過言ではない。以下のように、service.postと同じようにpost_with_picturesをすればよいのだ。

さあ、これで画像投稿実装はほぼ完成。実際にmikutterを起動して、その快適さを確かめて欲しい。

その他の細かい実装

そこそこ大事だけれど細かい実装をしておく。重要ではないのでサラッとコードを載せて説明するだけ。

投稿をキャンセルした時の挙動

投稿中にキャンセルすると文字列がもとの投稿欄に返ってくる。これはcancel_post関数のなかで処理している。もとのPostBoxのbufferに今の投稿欄のbufferの文字列を入れている場所があるので、その次に以下のようにでも書いておけばよい。

投稿中に画像の追加・削除を禁止する

ボタンや投稿欄のsensitive(編集・実行可能か)はrefresh_buttonsで定義されている。ここに画像の各ボタンの可否を書いておけばよい。実験での実装では以下の様な関数を用意して、これを引数なしでrefresh_buttonsから呼び出すことで実装した。

なおrefresh_buttonsはif文で大きく2つのブロックにわかれているが、elseの方に書く。理由は忘れた。

画像追加時や削除時にappend_picture_buttonsを呼び出すことで、ボタンの可否をさらにリアルタイムに制御することもできる。また、ドラッグ・アンド・ドロップの実装の際にposting?がnilまたはfalseでないときにドラッグ・アンド・ドロップを拒否するようにすることも必要だ。

なおドラッグ・アンド・ドロップは、データを受け取る際に無視してもよいが、drag-motionシグナルのときにfalseを返すのがマナーらしい。

まあどうせ多少ガバガバ実装をしておいても、add_pictureは4個以上のときにはじくので問題ないが。

残り文字数を画像追加時に反映する

画像を追加すると25文字分奪われるので、この分残り文字数から引いておきたい。残り文字数をカウントする関数はremain_charcountなので、@picturesがempty?でないときに-25するようにしておく。

また残り文字数は@remain.set_text(remain_charcount.to_s)でセットする。手動でセットする。なんでや。

次回予告

次回は動画を投稿します。


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です