Sponsored
MemoNest というメモアプリを公開しました。
件名と値を登録しておいて、値をタップするとそのままコピーできます。
データは使っている端末の中にだけ保存されます。
アカウント登録はありませんし、入力したメモをサーバーへ送ることもありません。
無料で使えます。
普通のメモアプリだと、コピーが面倒だった
作ったきっかけは、ほしい物リストの URL です。
家族から「あれ送って」と言われるたびに、ブラウザの履歴から探したり、自分宛に送っておいたメールを検索したりしていました。
ほかにも、似たようなものがあります。
駐車場の区画番号。 契約者番号の下4桁。 読み返したい漫画の巻数とページ。 半年に一度くらいしか使わない社内システムの ID。
パスワード管理アプリへ入れるほど大事なものではない。
でも、覚えておくほどでもないし、忘れると困ります。
じゃあ普通のメモアプリへ書けばいいかというと、今度はコピーするときが面倒でした。
メモを開いて、目的の場所を探して、長押しして、選択範囲を調整してコピーする。
スマホだと、この選択範囲の調整が結構面倒です。
僕が欲しかったのは、単に「この値をタップしたらコピーされる」だけでした。
なので作りました。
できること
データは2階層になっています。
たとえば「家のこと」というメモを作り、その中に「ほしい物リスト」というグループを作る。
あとは、その中へ件名と値を追加していきます。
- 値をタップするとコピーされます。URL を登録しても、タップでリンクは開きません
- メモ単位で PIN ロックをかけられます
- JSON でエクスポート、インポートできます
- ホーム画面へ追加しておけば、オフラインでも使えます
- 検索、並び替え、アイコン、ダークテーマにも対応しています
URL をタップしても開かず、コピーするだけなのは意図した仕様です。
URL でも数字でも文字列でも、値をタップしたらコピー。
毎回同じ動きにしたかったので、ここは最初から変えていません。
サーバーを持たない構成にした
メモのデータは IndexedDB へ保存しています。
端末間の同期はありません。
アプリ自体は Cloudflare Pages で配信しています。静的ファイルを置いているだけなので、バックエンドもありません。
この構成にした理由は単純で、利用者のメモを自分のサーバーで預かりたくなかったからです。
そもそもサーバーにデータがなければ、こちらから漏れるメモもありません。
運用するものも減りますし、費用もほとんどかかりません。
最近、自分で作るものを静的サイトへ寄せているのも同じ理由です。
一方で、不便なところもあります。
端末を変えてもデータは自動では移りません。
ブラウザのデータを消せば、MemoNest のデータも消えます。
そのため、設定画面にはエクスポート機能を置いています。
しばらくバックアップを取っていない場合は、画面上にも案内を出すようにしました。
同期までやろうとすると、結局どこかにサーバーが必要になります。
今回はそこまでやらず、必要な人には自分でバックアップを取ってもらう形にしています。
引っかかったところ
コピー処理に await を挟むと iOS でコケる
クリップボードへの書き込みは、利用者がタップした流れの中で実行しないとブラウザに弾かれることがあります。
MemoNest でも、コピー処理の直前に非同期処理を挟んだところ、iOS Safari でコピーできなくなりました。
画面上ではコピーに成功したように見えるのに、貼り付けてみると何も入っていない。
これが少し面倒でした。
そのため、copyText() は同期関数のままにしています。
ただ、コードだけを見ると「ここは async にしてもよさそう」に見えます。
数か月後の自分なら、たぶん普通に await を追加します。
そしてまた iPhone だけ壊す気がしたので、ここは回帰テストも入れてあります。
ロック中のメモに平文を持たせない
PIN でロックしたメモは AES-256-GCM で暗号化しています。
暗号鍵は PBKDF2 で PIN から生成します。
暗号処理そのものより時間がかかったのは、データの型をどうするかでした。
ロック中のレコードには、平文のフィールド自体を持たせていません。
locked を使った判別可能ユニオンにしているため、ロックされた状態のデータから平文を読もうとすると TypeScript のビルドで止まります。
「ロック中はこのフィールドを読まないようにする」という運用にはしたくありませんでした。
その手のルールは、そのうち忘れるので。
間違ったコードを書いた時点でビルドが落ちるようにしています。
取り込んだ JSON の URL を信用しない
MemoNest は JSON ファイルからデータをインポートできます。
アプリの外からデータが入ってくるのは、基本的にここだけです。
たとえば、値として javascript: から始まる文字列を仕込んだ JSON を読み込ませて、その値をリンクとして開けてしまうと、スクリプトを実行できる可能性があります。
Sponsored
そのため、URL として扱う文字列はスキームを許可リストで制限しています。
自分用の小さなアプリでも、ファイルを読み込めるようにした時点で外部入力になります。
インポート機能を付けるときは、このあたりも一応見ておいたほうがよさそうです。
広告について
MemoNest は無料で使えますが、画面には広告を表示しています。
広告を入れたことで、メモの内容を外部へ送るようにはしていません。
通信先は CSP で制限していますし、アプリ側にもメモをサーバーへ送信する処理はありません。
広告ブロックについては、できれば解除して使ってもらえると助かります。
これから
ひとまず公開しましたが、僕自身もまだ使いながら細かいところを直しています。
不具合や「ここが使いづらい」などあれば、アプリ内の問い合わせフォームから送ってください。
開発自体は、今回もかなり AI を使っています。
そのあたりは以前の記事にも書きました。
おわりに
やっていることはかなり単純です。
件名と値を登録して、必要になったらタップしてコピーする。
ほぼそれだけのアプリです。
ただ、自分の場合はこういう「たまに使う短い文字列」が思った以上に多くて、作ってから毎日のように使っています。
同じようなものをメモアプリや自分宛のメールから毎回探している人は、よければ使ってみてください。