Scrapboxの開発 - React & Websocketで作るリアルタイムWiki
まじかよ
sugoi
つらい
がんばれがんばれ
すげぇ
楽しい
編集間違えたら ctrl-z
or command-z
で戻せます
がんばれ!!!!!
え、react
すごかった
!!ページ右側の「Start presentation」でスライドになります!!
@shokai
, Inc.
Scrapboxの開発
kininaru
2. 実装のテクニカルなこと
について話します
開発プロセス
完全リモートワーク、目覚ましをかけずに好きなだけ寝る派
適当に寝て適当に起きる
自分で色々な用途に使ってみる
用途が違うと、ほしい機能も違う
大学の研究室
家族で使う
同人サークル
ゲームの編成・装備・経過のログ取り
色々な機能が欲しくなってくる
でも、なんでも入れるとボタンだらけになってよくない(難しい・・)
techblog 兼 料理レシピ
として使う場合
公開機能が欲しくなる
公開・非公開プロジェクトを設定できるようにしよう
会社や大学の研究室
研究ノートを書いてるのに、発表のためにスライドを作り直すのは無駄
スライド作るのが面倒くさい、究極的には発表中に誰かにリアルタイム作成してもらいたい
ページ名が説明的に長くなりがち
長くなってもよい
はてブに上がってくるようなみたいなエモーショナルなタイトルをどんどんつけるべき
リンクは、入力補完でなんとかする
色々な人がいる
OS毎のショートカットキーの再現
Scrapboxの開発にScrapboxを使う
流れ
1. 好き放題、意見・要望・苦情を書いてもらう
3. Sprint
アレが抜けてない?とか指摘される
4. 実装前に軽く設計をScrapboxに書く
個別のタスク毎に1つずつページを作る
関連ページリストで一覧できる
やりたい事は100以上ある
個別ページ内で、同時並行して議論している
「やりたい事」の状態では優先度は付けられないし、無理につけない
家の引っ越しとかもこんな感じでやれる
巨大リストを作らない
巨大なToDoリストを見ると気が滅入る
山が高すぎると登る気にならない
ページのネットワークを作る
編集していると、実はこれ同じ機能を要求しているんじゃね?というissue群に気づいたりする
Sprint
やりたい事は全部scrapbox書いてある
ぶつかり稽古ガチ議論をする
リモートワークでも「どれからやっても良いよ」という雰囲気を作れる
たまに緊急で上から降ってくるタスクがあっても、妥当性を感じる
やらされてる感が無くなる
名案が思いついたら突然実装される
Scrapboxの実装
テクニカルな話
コア
WYSIWYGで画像やリンクも貼れるけど、編集ツールバーやボタンが無いエディタ
markdownより簡単なシンタックスで編集・文字装飾できる
を参考にして
で実装した
と自作の同期システムでうまいこと同時編集できる
Gitを参考にsubversionみたいなものを実装した
実装環境
エディタの実装
カーソルの縦棒はdiv
緑の範囲選択もdiv3つ
隠し <textarea />
IMEウィンドウを見せるためだけに存在する
カーソルの右側に浮いていて、ついてくる
が参考になった
y行目のx文字目の画面上の座標はどこか?
生テキスト こんにちは
を
1文字ずつ分割して
editor.html<div class='lines'>
<div class='line' id='L1'>
<span class='c-1'>こ</span>
<span class='c-2'>ん</span>
<span class='c-3'>に</span>
<span class='c-4'>ち</span>
<span class='c-5'>は</span>
</div>
</div>
jQueryで位置を取得する
let {left, top} = $('.lines #L1 .c-4').position()
クリックした位置から何行目の何文字目なのか?を求める
逆をやればいい
あとはカーソル移動やemacsキーバインド等を自前で実装すればok
と、よくインターネットに書いてある
jQueryはクロスブラウザで座標の扱いが完璧っぽい!
jQueryでDOM書き換えしなければok
render→ componentDidUpdate
からjQueryで座標計算→stateに入れる→再render
複数人で同時編集
こういう「コミット」をやりとりすればなんとかなるのでは!?
commit.json{
"id": hash,
"parent_id": parent_hash, // 1つ前への参照
"changes": [ 変更内容, 変更内容, 変更内容 ]
}
編集の命令は3種類だけ
1つの commit
の chanegs
配列に複数の編集が格納される
insert
隣の行のidを指定して新しい行を追加
javascript{
"_insert": positionId, // 文字を挿入する一つ下の位置にある行のId
"lines": {
"text": text,
"id": lineId // 新しく生成された行のid
}
}
新規に行を挿入する
最後に挿入するときは、特殊IDの _end
を指定する。
複数行を挿入するときは、insertを複数個作る
update
idを指定して更新
js{
"_update": lineId, // 変更する行のId
"lines": { "text": text }
}
行を変更する
delete
idを指定して削除
js{
"_delete": lineId, // 削除する行のId
lines: -1
}
行を削除する
コンフリクトしたら?
ユーザー側でmerge画面にするわけにはいかない
コンフリクト判定はサーバー側
clientはとりあえずpushする
pushがrejectされたらpull
git rebase
のような操作をする
気合マージ!!
mergeしよう → コンフリクト → pullしてみる
→ insertが参照していた行が削除されてる、どこにinsertしていいのかわからない!
→ commitをさかのぼって、「削除された行を参照したinsertをした行」を参照してinsertする