バグ #1155
未完了world_settingsでボタンの無いダイアログが表示される。
説明
下の様なworld_settingを作ると、途中でボタンの無いダイアログが表示されます。
1. "user", "password"テキストボックスを持つダイアログが表示される。
2. "user", "password"テキストボックスを入力してOKボタンを押す。
3. OKとCancelボタン以外、ウィジェットが何もないダイアログが表示される。
4. OKボタンを押す。
5. 設定画面のアカウント一覧にworldが正しく登録される。
使い方を間違っているならばご教示いただけると助かります。
world_setting(:test, _("test")) { input(_("user"), :user) input(_("password"), :password) result = await_input world = Plugin::Test::World.new({ :icon => Plugin.filtering(:photo_filter, "https://www.wunderlist.com/site/images/favicon.ico", []).last.first, }.merge(result.to_h)) world }
再現手順
再現用プラグインhttps://github.com/moguno/testをcloneして、設定のアカウント情報からtestワールドのアカウント登録を行ってください。
toshi_a 初音 さんがほぼ7年前に更新
これは無意識に避けてたんですが、よく考えると悩ましい問題ですね。一言で言えばこれは仕様ですが、改善の余地はあると思います。
まだworld_settingsの使い方については公表していないので、以下に必要な仕様と、思いつく改善案を書きました。
dialogの仕様¶
world_settingsはdialog DSLをそのまま利用しているので、 await
や await_input
は、dialogの機能です。
dialog DSLの最後まで処理が終わったら、ダイアログボックスを破棄します。しかしdialogはユーザにボタンを押されるまで開いたままのほうが都合が良い場合がほとんどなので、内部的には最後にawait_inputを1回呼び出しています(暗黙のawait_input)。
1画面で終わるダイアログボックスの場合はこの仕様は好ましいと思います。一方ユーザと対話的にやり取りする場合、このチケットで指摘されたような、空白の画面が最後に表示されるという問題が起こります。
Twitterプラグインでは、最後にWorldを表示し、本当にそれを登録するかをユーザに選択させるようにしているため、このような問題が発生していません。このような最後の確認の是非はともかくとして、確認せずに登録完了とできないのは不便ですね。
思いつく改善案¶
互換性の問題に言及していますが、まだ出て間もないので、今なら変更してしまってもメリットのほうが大きいと思っています。
一度でもawaitを呼び出していたら暗黙のawait_inputをやめる¶
自動的に呼ばれるものも含めて、必ず1度はawaitかawait_inputは呼ばれるはずです。
したがって、dialogのライフサイクルの中で一度も呼ばれなかったときだけawait_inputを呼ぶようにすれば、今回のような問題は発生しなくなります。
一方、今までは必ず呼ばれていたわけですから、互換性がありません。
ウィジェットが一つもない場合、暗黙のawait_inputをやめる¶
ウィジェットが一つもない状態でボタンを押されるのを待っていても、ユーザから意味のある入力を受け取ることはできません。暗黙のawait_inputを呼ぶ前に、表示されているウィジェットを確認して、何もなければそのままダイアログを閉じます。
dialogを設計したときにはこれは不可能でしたが、mikutter 3.6ではForm DSLを応用して、設定画面の遅延レンダリングとかやっているので、頑張れば出来る気がします。
こちらであれば互換性を損ねることはないですが、実装が大掛かりになりそうです。
Satoshi Okuno さんがほぼ7年前に更新
すみません。長考してました。
dialog DSLとの兼ね合いなんですね。
ご提示の改善案では「一度でもawaitを呼び出していたら暗黙のawait_inputをやめる」のが素直かなと思います。
ただ、どちらも「特定条件を満たすとawait_inputが呼ばれなくなる」と言うところが、ハマった時にその法則に気づくまで時間がかかりそうで、悩ましいなと。
逆に、暗黙のawait_inputをユーザーがマニュアル制御できるのもいい気がしました。
dialog(last_await_input:false) { # falseだと暗黙のawait_inputが呼ばれない。デフォルトはtrue
}
暗黙のawait_inputを敢えて見せていくスタイル的な。