そういえば、書く場所へは共有できなかった。Qiita・Zenn・note・Medium・アメブロへの共有ボタンを足しました
作成: / 公開: / 内容更新:
執筆:tamito0201 / 掲載・運営:プロマリ
記事の下の共有欄に、X や LINE はあるのに、読んだ人が自分の記事で紹介する Qiita・Zenn・note・Medium・アメブロへの入口がありませんでした。note は公式の共有入口へ、入口の無い4つは「題名と URL をコピーして投稿画面を開く」新しい動きで足しました。4.2.0 では、貼った先でそのまま記事のカードになるよう、共有先ごとの書き方をそろ…
入口の無い4つには、新しい動きを足した
ここからが、今回の本題です。Qiita・Zenn・Medium・アメブロには、外のページを共有する共有の入口がありません。note のように入口の URL へ記事の情報を付けて開く、というやり方が使えないのです。では、この4つの共有先のボタンは、押したら何をすればよいのでしょうか。
このプラグインがこれまで持っていた動きは、3種類でした。入口の URL を開く open、記事の URL をコピーする copy、スマホなどの端末に備わった共有の画面を呼び出す native です。入口の無い4つは、どれにもうまく当てはまりません。そこで、4つの案を並べて比べました。
1つ目の案は、投稿画面を開くだけにすることです。open の入口の代わりに、投稿画面の URL を書いておけば、コードを1行も変えずに足せます。けれど、開いた投稿画面は空っぽで、記事の題名も URL もありません。読者は元のタブへ戻って URL をコピーし、また投稿画面へ戻って貼ることになります。入口を置いた意味がほとんどありません。
2つ目の案は、URL をコピーするだけにすることです。窓を開かないので、ブラウザに止められる心配もありません。けれど、それなら Qiita のボタンも Zenn のボタンも、すでにある「URLをコピー」のボタンと同じ動きになります。4つのボタンを並べる意味がなくなってしまいます。
3つ目の案は、投稿画面の URL に、題名や本文を付けて開くことです。GitHub の Issue のように、URL の後ろに ?title=… と書けば、題名が入った画面を開けるサービスもあります。けれど、4.1.0 を作ったときに確かめた範囲では、4つのサービスは、どれもそうした項目を読みませんでした。読まれない項目を付けて開くと、見た目にはちゃんと動いているのに、画面には何も入っていない、という失敗になります。エラーが出ないぶん、いちばん気づきにくい失敗です。実はこのあと、アメブロの投稿画面だけは、ログインした状態で確かめ直すと、題名と本文の項目を読むことが分かりました。その話は、この先の「コピーする文」の節でします。
そして4つ目の案が、今回選んだ「題名と URL をコピーしてから、投稿画面を開く」です。ボタンを1回押すと、記事の題名と URL がクリップボードに入り、同時にそのサービスの投稿画面が新しいタブで開きます。読者は、開いた画面に貼り付けるだけで、紹介の記事を書き始められます。この動きに、compose(書く、組み立てる)という名前を付けました。
操作の種類を増やすのは、共有先を1つ足すのとは重さの違う判断です。共有先は「データのカード」を足すだけで済みますが、操作の種類はプログラムの動きそのものを変えるからです。本編の連載で残した ADR-0002 にも、「クリックで何をするかの種類を増やす必要が出たら、別の判断として扱う」と書いていました。そこで今回の判断は、新しいADR(ADR-0003)として残しています。上の4つの案と、捨てた理由も、そこに書きました。
compose を使う共有先のカードは、note よりもさらに短くなります。Qiita の例を見てみましょう。
# Qiita's draft editor. Qiita has no entry point for sharing an external page, so the title and URL are copied first.
# Icon: simple-icons 16.33.0 (qiita.svg), CC0 1.0. The logo remains a trademark of its owner.
key = 'qiita'
label = 'Qiita'
brand_color = '#55C500'
action = 'compose'
endpoint = 'https://qiita.com/drafts/new'action が ‘compose’ になり、endpoint には共有の入口ではなく、投稿画面の URL を書きます。そして、[params] の表がありません。compose は、投稿画面の URL に何も付けないからです。付けても読まれないことは、3つ目の案のところで確かめたとおりです。4.1.0 では、Zenn・Medium・アメブロのカードも同じ形で、それぞれ8行でした(アイコンの行を含む)。4.2.0 で、この下に下書きの形を書く2行が加わり、アメブロは open へ移りました。それも、この先の節で見ます。
1回押すと、2つのことが同時に始まる
それでは、compose のボタンを押したとき、画面の裏で何が起きるのかを見ていきます。
押した瞬間に始まることは2つです。1つ目は、クリップボードへの書き込みです。書き込む中身は、4.1.0 では、記事の題名と、改行と、記事の URL の3つを並べた文でした。4.2.0 からは、共有先ごとに「そのサービスで記事のカードになる書き方」の文を書き込みます。URL には、ほかの共有先と同じようにUTMが付き、utm_source には qiita のような共有先の名前が入ります。2つ目は、投稿画面を新しいタブで開くことです。そして、2つのことが始まったあと、コピーがうまくいったかどうかを、画面の中の小さな知らせで伝えます。
書き込む文を決めているのは、ドメイン層(判断や決まりごとを置く層)の共有先の値オブジェクト ShareDestination にある、composeDraft という小さな関数です。2ページ目で見た、共有 URL を組み立てる shareUrl のすぐ隣に置いています。
composeDraft(request: ShareRequest): string {
return this.action === ShareAction.Compose ? this.#render(request) : '';
}
#render(request: ShareRequest): string {
return this.#draft
? DraftTemplate.render(this.#draft, LinkCardPolicy.bound(request))
: DraftTemplate.render(DraftTemplate.COMPOSE_DEFAULT, request);
}compose 以外の共有先なら空の文を返し、compose なら下書きを作って返します。下書きの形は、共有先のカードに書いたひな形(draft)で決まり、書いていなければ 4.1.0 と同じ「題名+改行+URL」です。ひな形へ記事の値を差し込むのは DraftTemplate、題名や説明を上限の長さで切るのは LinkCardPolicy という、ドメイン層の小さな2つのサービスが受け持ちます。共有 URL の組み立てと同じく、共有先が自分の操作に合わせて答えるので、呼ぶ側は操作の種類で分岐せずに済みます。とても短い関数ですが、「何をコピーするか」という決まりを、画面やブラウザの都合から切り離して1か所に置いているのがポイントです。4.2.0 で文の形をがらりと変えたときも、この関数を呼ぶ側は1行も直していません。ブラウザがなくても試験できるので、題名が空のときに下書きの先頭へ空行が残らないことも、試験で確かめています。
実際にクリップボードへ書いて、タブを開くのは、ユースケース(利用者の操作を受けて、手順を進める層)の HandleShareClickUseCase です。compose の部分は、次の数行です。
case 'compose': {
preventDefault();
// Popup blockers honour window.open only in the click's synchronous turn, so both effects start
// before the first await. The clipboard write starts first; a synchronous throw becomes a rejection
// so the editor still opens. A failed copy is reported, not retried with a prompt: the user is
// already looking at the new tab (ADR-0003).
const writing = new Promise<void>((resolve) => { resolve(this.#gateways.clipboard.write(button.draft)); });
this.#gateways.newTab.open(button.href);
return writing.then(() => 'composed' as const, () => 'compose-copy-failed' as const);
}上から読んでいきます。2行目の preventDefault は、ボタンを押したときのブラウザのふつうの動きを止める指示です。3〜6行目のコメントには、なぜこの順番なのかを英語で書いています。7行目でクリップボードへの書き込みを始め、8行目で投稿画面のタブを開きます。9行目は、書き込みが終わったら、成功なら ‘composed’、失敗なら ‘compose-copy-failed’ という結果を返す、という意味です。この結果を見て、画面に出す知らせの文言が決まります。
ここで注目してほしいのは、7行目と8行目のあいだに、待つための書き方(await)が1つも無いことです。クリップボードへの書き込みは時間のかかる処理なので、ふつうなら「書き込みが終わるのを待ってから、次へ進む」と書きたくなります。けれど、ここではあえて待たずに、書き込みを始めたらすぐにタブを開いています。次の節で、その理由を見ていきます。
もう1つ、ユースケースの中にブラウザの機能の名前(navigator.clipboard や window.open)が出てこないことにも注目してください。this.#gateways.clipboard と this.#gateways.newTab は、ドメイン層に置いた「約束(インターフェース)」で、ブラウザの本物の機能は、別の層(infrastructure)がその約束に合わせて実装しています。今回、新しいタブを開くための約束 NewTabGateway を足し、infrastructure の BrowserNewTab が window.open で実装しました。小窓を開く既存の約束とは分けています。その理由は5ページ目の見直しの節でお話しします。この分け方のおかげで、試験では本物のブラウザの代わりに、呼ばれた順番を記録するだけの代役を渡して、「コピー→タブ」の順に呼ばれることを確かめられます。本編の連載2.5「DIP――ブラウザの機能を、ポートの向こうへ置く」で読んだ作りが、ここでもそのまま効いています。
コピーする文は、貼る先のカードの書き方に合わせる
4.1.0 の compose がコピーした文は、「題名+改行+URL」という、とても素朴な形でした。技術ブログの共有なら、Markdown(文字に記号を添えて見出しやリンクを表す書き方)のリンクの形、[題名](URL) にしたほうが気が利いているのでは、と思うかもしれません。実は、その案も考えました。
| 案 | 貼った結果(Markdown で書く場所) | 貼った結果(見たまま書く場所) |
|---|---|---|
| [題名](URL) | 題名がリンクになる | 記号の [ ] ( ) がそのまま残る |
| 題名+改行+URL | 1行目が文、2行目が URL(URL の行はカードになることもある) | 1行目が文、2行目が URL(多くはリンクになる) |
| URL だけ | URL の1行だけ | URL の1行だけ |
4つのサービスのうち、Qiita と Zenn は、Markdown で書く場所です。一方、Medium とアメブロは、画面に見えているとおりに書いていく場所(見たまま書くエディタ)が基本です。Markdown のリンクの形でコピーすると、Qiita と Zenn ではきれいなリンクになりますが、Medium とアメブロでは、[ や ( の記号が、そのまま文の中に残ってしまいます。
4つとも同じ1つの文をコピーする作りなので、どの書く場所に貼っても崩れない形を選ぶ必要がありました。題名と URL を別の行に分けるだけなら、Markdown の場所でも見たまま書く場所でも、そのまま読める文になります。URL を独立した行にしておけば、URL だけの行をカードやリンクに変えてくれる機能にも拾ってもらえます。題名が空のときに URL だけにするのも、同じ考え方です。
共有先ごとにコピーする文の形を変える、という案もありました。4.1.0 では、カードの項目が増えるのを避けて、ADR-0003 の見直す条件に「題名と URL 以外も貼りたいという要望が出たら」と書き、必要になったときに考えることにしていました。ところが、その「必要になったとき」は、公開したその日のうちに来ました。
公開したあとで、ログインした Chrome で5つのボタンを実際に押し、開いた投稿画面に貼ってみたのです。すると Qiita でも Medium でも、貼った2行はただの文字とリンクのままで、記事の題名や画像の付いたカードにはなりませんでした。アメブロは、白紙の投稿画面が開くだけです。「どこに貼っても崩れない」ことと、「貼った先で記事が引き立つ」ことは、別のことだったわけです。そこで、それぞれのサービスで、記事をカードにする書き方を1つずつ確かめ直しました。
旅行用の変換プラグを思い浮かべてください。家電のプラグは1つでも、国ごとにコンセントの形が違うので、差し込み口に合わせたアダプタを挟みます。記事の題名や URL も同じで、中身は1つでも、カードになる「差し込み口の形」はサービスごとに違いました。
| 共有先 | 動き | 下書きの形 | 貼った・開いた結果 |
|---|---|---|---|
| Qiita | compose | 題名・空行・URL・空行 | プレビューで、URL の行が記事のカードになる。4.1.0 の形は空行が足りず、カードにならない |
| Zenn | compose | 題名・空行・@[card](URL) | プレビューで、記事のカードになる |
| Medium | compose | URL だけ | 空の行に貼って Enter を押すと、記事のカードになる。題名と URL の2行を貼ると1つの段落にまとめられ、Enter を押してもカードにならない |
| アメブロ | open(4.1.0 は compose) | カードの HTML を本文として送る | 題名とカードが入った状態で、投稿画面が開く |
| note | open(変更なし) | 公式の入口に URL を渡す | 記事のカードが入った「noteで書く」の画面が開く |
表の中で、アメブロだけは動きそのものが変わっています。アメブロの投稿画面(srventryinsertinput.do)は、URL に entry_title と entry_text という項目を付けて開くと、題名と本文をあらかじめ入れた状態で開くことが分かったのです。1ページ目で「入口なし」としたとおり、これは外のサイト向けに公開された共有の入口ではありません。けれど、読まれる項目があるなら、コピーして貼ってもらうより、開いた時点で本文にカードが入っているほうが、読者の手間が1つ減ります。そこでアメブロは compose をやめ、note と同じ open に移しました。本文には、アメブロのエディタが記事のカードとして表示する HTML(ogpCard という形)を入れて送ります。公開された仕様ではないので、確かめた日付ごと記録に残しています。
Medium は、少し事情が違います。Medium の公式ヘルプには、新しい行に URL を貼って Enter を押すと埋め込みになる、と書いてあります。ところが「題名+改行+URL」を貼ると、Medium は2行を1つの段落にまとめてしまい、URL が独立した行になりません。そのため、Enter を押してもカードになりませんでした。そこで Medium だけは URL だけをコピーし、コピーの知らせに「貼り付けたあと Enter を押すと、記事のカードになります」と添えることにしました。Medium には記事を取り込む機能(インポート)もありますが、公式ヘルプはこれを、取り込む権利を持つ内容に限っています。他人の記事を紹介する今回の用途には当たらないので、使っていません。
共有先ごとの書き方は、コードではなく、共有先のカードに書きます。4.2.0 の Qiita のカードは、次のようになりました。
key = 'qiita'
label = 'Qiita'
brand_color = '#55C500'
action = 'compose'
endpoint = 'https://qiita.com/drafts/new'
draft = "{title}\n\n{url}\n"
draft_format = 'text'draft がひな形で、{title} や {url} のところに記事の値が入ります。使える差し込み口は {title}・{url}・{description}・{image}・{host} の5つだけで、それ以外を書くと生成器が止めます。draft_format は、値をそのまま入れる text か、HTML として安全な形に直して入れる html かの区別です。アメブロのカードは html なので、題名や説明に < や & が入っていても、カードの形は崩れません。この判断は ADR-0004 に残しました。カードになる書き方は、共有先ごとのカードに宣言する。2ページ目で見た「共有先はデータ」という作りを、下書きの形にまで広げたわけです。
アメブロには、もう1つ気をつけることがありました。下書きを URL に入れて送るので、URL が長くなるのです。日本語の1文字は URL の中では9文字になり、題名はアメブロの題名の欄とカードの両方に入ります。長すぎる URL を送ると、アメブロは 400(リクエストが正しくない)を返し、投稿画面そのものが開きません。そこで、通る長さを二分探索で測ってみると、面白いことが分かりました。
| 状態 | アメブロへ送るクッキー | 通った URL | 400 になった URL |
|---|---|---|---|
| ログインしていない | ほぼ無し | 6,000 文字まで(ログイン画面へ転送) | 8,000 文字 |
| ログイン済み | 約1,800 文字 | 5,105 文字まで | 5,140 文字 |
ログインすると、通る URL が1,000文字以上短くなりました。上限は URL だけでなく、クッキーまで合わせたリクエスト全体の大きさで決まっていたのです。クッキーの量は読者ごとに違い、ページからは測れません。そこで、下書きを送る URL を 3,500 文字以内に収めることにしました。ログイン済みで通った 5,105 文字との差は約1,600文字あるので、今回の計測よりクッキーが1,600文字ほど多い読者まで通る見込みです。超えそうなときは、まず説明を縮めて外し、次に画像を外し、最後に題名を縮めます。この順番を受け持つのが、さきほどの LinkCardPolicy です。手元の試験ページで、この記事の題名・説明・画像を使って確かめたときは 3,038 文字で、カードの中身はどれも削られずに入りました。
窓を開けるのは、クリックの切符が有効なうちだけ
なぜ、書き込みの完了を待たずにタブを開くのか。答えは、ブラウザに備わったポップアップブロッカーです。
ページが勝手に新しい窓やタブを開けると、広告の窓が次々に開くような迷惑なサイトを作れてしまいます。そこでブラウザは、読者がクリックなどの操作をした、その直後にだけ、窓を開くことを許します。仕様では、この「操作の直後」をユーザーの操作の直後と呼んでいます。この記事では、クリックのたびに発行される「有効期限つきの切符」にたとえます。
切符が有効なのは、クリックを受けて動き始めた処理の、最初のひと続きのあいだです。そこで await を使って、クリップボードへの書き込みが終わるのを待つと、待っているあいだに処理がいったん途切れます。書き込みが終わって処理が再開したときには、ブラウザから見ると「クリックの直後」ではなくなっていることがあり、そこで窓を開こうとすると、ポップアップブロッカーに止められてしまうのです。
だから compose では、クリップボードへの書き込みと、タブを開く処理の両方を、最初の await より前に呼び始めるようにしました。書き込みは「始めた」だけで、終わりを待っていません。終わったかどうかは、タブを開いたあとで、結果として受け取ります。こうすれば、タブを開く瞬間は、必ずクリックの切符が有効なうちになります。
書き込みを先に始めているのにも、小さな理由があります。クリップボードへの書き込みも、ブラウザによっては「操作の直後」かどうかや、ページに焦点があるかどうかを見ます。タブを先に開くと、焦点が新しいタブへ移ってしまい、書き込みが断られやすくなります。そこで、書き込みを先に始め、そのすぐあとにタブを開く順番にしました。ただ、これでも書き込みが終わる前に焦点が移ることはありえます。その場合にどうなるかは、次の節で扱います。
コードの7行目で、書き込みを new Promise で包んでいるのにも意味があります。クリップボードの機能がそもそも使えない環境では、書き込みを呼んだ瞬間に例外(エラー)が出ることがあります。そのまま例外が飛ぶと、8行目のタブを開く処理まで届かずに、処理が止まってしまいます。Promise で包むと、その場の例外も「あとで受け取る失敗」に変わるので、書き込みがどんな形で失敗しても、8行目は必ず実行されます。
コピーに失敗しても、投稿画面は開く
それでは、クリップボードへの書き込みに失敗したら、どうなるのでしょうか。
結論から言うと、成功しても失敗しても、投稿画面は開きます。違うのは、画面の中に出す知らせの文言だけです。成功なら「記事のリンクをコピーしました。投稿画面に貼り付けてください」(4.1.0 では「タイトルとURLを…」。Medium では、このあとに Enter の案内が付きます)、失敗なら「コピーできませんでした。投稿画面にタイトルとURLを入力してください」と出します。
失敗の知らせを、成功のふりをして隠さないことは大事です。もし失敗しても「コピーしました」と出してしまったら、読者は投稿画面で貼り付けて、何も入らないことに戸惑います。失敗したなら、失敗したと正直に伝えれば、読者は元のタブから自分でコピーするなど、次の手を打てます。
一方で、「URLをコピー」のボタン(copy)では、コピーに失敗したとき、URL を入れた小さな入力の窓(prompt)を出して、読者が自分でコピーできるようにしています。compose では、この窓を出しません。compose で失敗が分かるころには、読者の目はもう新しいタブの投稿画面にあります。元のページに窓を出しても、読者からは見えないのです。見えない窓で読者を待たせるより、知らせの文言だけにしておくほうが親切だと判断しました。
知らせの文言は、サイトの設定で変えられます。設定の messages という表に、composed(成功)と compose_failed(失敗)の2つの項目を足しました。どちらも書かなければ、上の既定の文言が使われます。
composed = "記事のリンクをコピーしました。投稿画面に貼り付けてください" # Optional. After copying for qiita / zenn / medium; a destination's compose_hint follows it.
compose_failed = "コピーできませんでした。投稿画面にタイトルとURLを入力してください" # Optional. When that copy fails; the editor still opens.既定の文言を持たせたのは、これまでの設定ファイルを1文字も直さずに、新しい版へ上げられるようにするためです。項目を必須にすると、新しい版に上げた瞬間、すべてのサイトの設定が検査で止まってしまいます。今回の変更は「足すだけ」の変更なので、版の番号も、互換性を保った機能の追加を示す 4.1.0 にしました。
ここまでで、compose で分からないことも2つあります。1つは、新しいタブが本当に開けたかどうかです。投稿画面はnoopenerを付けて開くので、開いた窓の手がかりが元のページに返ってきません。開いた先のページが、元のページを書き換えるのを防ぐための指定ですが、その代わりに、開けたかどうかも分からないのです。もう1つは、実際のブラウザでどれくらいの割合でコピーが断られるかです。ログインした Chrome で押したときは、どれもコピーに成功しましたが、1回ずつ確かめただけで、割合を測ったわけではありません。Safari では、まだ確かめていません。どちらも ADR-0003 の「悪い結果」と「見直す条件」に書いてあります。
compose は、リンクではなくボタンとして描く
compose のボタンは、画面の上では、X や note のボタンと同じ丸い見た目をしています。けれど、HTML の中では、別の部品として描いています。X や note・アメブロは <a>(リンク)、compose の3つは <button>(ボタン)です。
const CONTROL: Readonly<Record<ShareAction, { readonly followsLink: boolean; readonly linkable: boolean }>> = {
[ShareAction.Open]: { followsLink: true, linkable: true },
[ShareAction.Copy]: { followsLink: false, linkable: true },
[ShareAction.Native]: { followsLink: false, linkable: true },
[ShareAction.Compose]: { followsLink: false, linkable: false },
};どちらで描くかを決めているのは、表示の層ではなく、ドメイン層のこの表です。followsLink は「押すことが、そのままリンクをたどることか」(open だけが真)、linkable は「リンクとして描いてよいか」(compose だけが偽)を表します。compose をリンクとして描くと、手順を通らずにリンクだけがたどられたとき、コピーせずに投稿画面が開いてしまいます。だから compose だけは、リンクとして描いてはいけないのです。
この表は、操作の種類(ShareAction)のすべての値について書くことを、TypeScript の型(Record)が求めています。いつか操作を1つ足したら、この表に書き足すまでコンパイルが通りません。表示の層は、手順の層が表から取り出して渡す2つの値を読むだけです。円い表示なら、followsLink が真ならリンク、偽ならボタン、と描き分けます。
なぜ分けるのでしょうか。リンクとボタンは、見た目を同じにできても、意味が違うからです。リンクは「どこかへ移動する」もので、行き先の URL を持っています。ボタンは「何かをする」もので、押したときの動きを持っています。open の共有先は、入口の URL へ移動するのが仕事なので、リンクがふさわしい。一方、compose は、移動する前にコピーという「動き」をするのが仕事なので、ボタンがふさわしいのです。
この違いは、画面を見ない人にとって、とくに大事です。画面の読み上げソフトは、リンクを「リンク」、ボタンを「ボタン」と読み上げます。compose をリンクとして描くと、読み上げを聞いた人は「Qiita へ移動するのかな」と思って押し、思いがけずクリップボードが書き換わることになります。ボタンとして描けば、「Qiita、ボタン」と読み上げられ、押すと何かの動きが起きることが伝わります。キーボードで操作する人にとっても、リンクは Enter で、ボタンは Enter と Space で押せる、という違いがあります。
見た目は同じ丸いボタンのままにしているので、目で見て使う人にとっては、X も Qiita も同じように押せます。見た目は共有欄としてそろえ、中身の意味は動きに合わせて分ける。この部品では、既存の copy や native でも同じ考え方を取っていて、今回の compose もそれに合わせました。
順番は、代役を使った試験で確かめる
「コピーを始めてから、待たずにタブを開く」という順番は、目で見ても確かめにくいものです。人がボタンを押すと、コピーもタブも一瞬で終わってしまうからです。そこで、この順番を試験で確かめています。試験では、本物のクリップボードやブラウザの代わりに、呼ばれたことを記録するだけの代役を渡します。
試験の名前は、日本語で「何を確かめるか」がそのまま読めるように書いています。compose について足した試験のうち、2つを見てみましょう。
it('書き込みが成功したら composed を返し、操作を一度だけ通知する', async () => { /* … */ });
it('書き込みが拒否されても・同期で投げても投稿画面は開き、compose-copy-failed を返す(prompt は出さない)', async () => { /* … */ });1つ目は、うまくいったときの試験です。クリップボードの代役が書き込みを受け付けたら、結果が composed になること。そして、「共有ボタンが押された」という合図が、1回だけ出ることを確かめます。合図が2回出ると、5ページ目で見る計測で、押された回数が2倍に数えられてしまいます。
2つ目は、失敗したときの試験です。クリップボードの代役に、書き込みを断らせたり、呼ばれた瞬間に例外を投げさせたりします。どちらの場合も、タブを開く代役が呼ばれていること、結果が compose-copy-failed になること、そして copy のときのような入力の窓(prompt)を出していないことを確かめます。「失敗しても投稿画面は開く」という約束を、失敗の種類ごとに確かめているわけです。
既存の copy の試験には、「書き込みの完了を待ってから結果を返す(完了前に成功を名乗らない)」というものもあります。compose も同じで、タブは待たずに開きますが、結果(composed か、compose-copy-failed か)は、書き込みが終わってから返します。待たないのは窓を開くところだけで、成功を名乗るのは、成功が分かってから。この2つを分けて考えるのが、compose の作りの要でした。
この順番の作り方について、判断を書き残しておきます。コピーとタブは、最初の await より前に、この順で呼び始める。ブラウザの制約と、読者の手間の両方を満たす、ちょうど間の置き方です。
貼り付けると、記事のカードになる
最後に、読者の側から見た compose の結果を見ておきましょう。投稿画面に貼り付けると、何が入るのでしょうか。
Qiita の投稿画面に貼り付けると、1行目に記事の題名、1行あけて記事の URL が入ります。プレビューを見ると、URL の行は、記事の題名・説明・画像の付いたカードになっています。あとは、その下に自分の言葉で書き足すだけです。「この記事を参考に試してみたら、ここでつまずいた」「自分の環境ではこうだった」。紹介の記事の書き始めとして、ちょうどよい形だと思います。
4.1.0 のこの節では、「カードになるかどうかは、各サービスのエディタの機能と設定しだいで、この記事では確かめていません」と書いていました。4.2.0 では、そこを確かめています。ログインした Chrome で4つのボタンを押し、Qiita と Zenn は貼ったあとのプレビューで、Medium は貼って Enter を押したあとで、アメブロは開いた時点で、記事がカードとして入ることを見ました。確かめるために作った下書きは、あとで削除しています。
こうして、入口の無い4つにも、1回押せば書き始められる道ができました。次のページでは、増えたボタンを共有欄のどこに、どう並べたかを見ていきます。
考えてみる:compose を小窓(ポップアップ)で開かず、いつも新しいタブにしたのはなぜ?
これまでの open の共有先は、設定しだいで、小さな窓(600×500 程度)で開いていました。X の投稿画面のように、短い文を書いてすぐ閉じる画面なら、小窓が便利です。けれど compose で開くのは、記事を書くためのエディタです。小窓では狭すぎますし、ログインしていなければ、その狭い窓でログインの画面を通ることになります。同じタブで開く案もありましたが、それだと元の記事が画面から消えて、読み返しながら書くことができません。そのうえ、コピーの結果を知らせるページも消えてしまいます。そこで compose だけは、設定に関わらず、いつも新しいタブで開くことにしました。代わりに、小窓の設定が compose には効かないことを、設定の説明に書いています。















このページ(3ページ目)の感想・質問・設計へのコメント