generated at
YAPC2016Gyazzスライド
ページ右側の Start presentation でスライドになる

ReactでGyazzというWikiを自作して運用している話
@shokai shokai
Nota, Inc.

http://gyazo.comを運営している会社
スクリーンショット共有サービス
アニメGIFでも撮影できる
プルリクにも便利

Gyazo Tシャツ & シール
私に質問や感想を言うとあげます
トーク後でも可
15枚ぐらいある


Gyazzという名のWiKi
2人で作っている rakusai shokai

トーク概要より
WYSIWYGで画像やリンクも貼れるけど、編集ツールバーやボタンが無いエディタ
markdownより簡単なシンタックスで編集できる
Atomを参考にしてReactで実装した
contenteditableは使ってない
Socket.IOと自作の同期システムでうまいこと同時編集できる
Gitを参考にsubversionみたいなものを実装した

社内で運用してて、3ヶ月で900ページくらい作られている
このスライドもGyazzから生成している
「五行の送り火」を「大文字焼き」って書いたら勝手に修正されてた

ワイキキ空港のターミナル連絡バスのこと
みんなで書けるwebサイト

WiKiのつらみ
メジャーなWikiの方式

独特のシンタックスを手で書く方式
覚えるのつらい
さっき見ていた場所をちょっと直したいだけなのに、編集モードに入ると全体が生テキストになってる・・

WYSIWYGエディタ方式
見たまま編集できる
ツールバー、メニューがたくさん
一度作った綺麗な見た目の段落と、同じスタイルの段落をもう一個作るのが難しい
文章書いてる時間より装飾整えてる時間の方が長い・・ (shokai個人の見解です)

Gyazzとは?
増井俊之 が10年ぐらい前に作った
ここ数年、いろんな人がGyazzクローンを作っている rakusaishokai
アウトラインエディタのようなWiKi
編集・閲覧のモード切り替えが無い
カーソルのある行だけがWiKi Syntaxむき出しになる
かんたんなSyntax
がんばって書いたり覚えたりしなくても、適当に見やすく表示する
ページ間リンクを貼っておけば関連検索で発見できる
階層は無い、Web的
アイディアを書いておくと、忘れた頃に関連リンクに現れてピンときたりする
複数人で同時編集できる

ほぼ 角カッコで囲うだけ
外部リンク
[URL]
内部リンク
[ページタイトル]
#ページタイトル ハッシュタグ風
タイトル付き外部リンク
[URL タイトル]
[タイトル URL] 逆順でもいい
画像埋め込み
[画像のURL]
リンク付き画像
[画像のURL リンク先URL]
[リンク先URL 画像のURL] 逆順でもいい
アイコン記法
[ページタイトル.icon]
shokai
アイコンタワー記法
[ページタイトル.icon*数字]
shokaishokaishokaishokaishokaishokaishokaishokaishokaishokaishokaishokaishokaishokaishokaishokaishokaishokaishokaishokai
太字
[[あいうえお]] 角カッコ2つ
あいうえお
インラインコード
console.log("hello") バッククオートで囲う
コードブロック記法
test.js
function () { alert('hello') }
code:言語名 もしくは code:ファイル名 の下のブロックに色がつく
CLI記法
$ npm install jquery -save
% git init
行頭がコマンドっぽい行に色がつく
(デモ)

(デモ:ページを作ってみる)
Reactのページを書く
hashタグ
関連ページ
外部リンク
画像うめこみ、リンクつける
CLI記法
codeblock記法
アイコン記法
asadf

アイコン記法で投票

カーソル行がsyntaxむき出しになるの重要
「これどう書いてるのかな?」がすぐ見れる
HTMLのソース見て勉強したのを思い出す


実装の話

実装環境
EventEmitter式Store
全面的にasync-await

エディタの実装
カーソルの縦棒はdiv
緑の範囲選択もdiv3つ
隠し <textarea />
IMEウィンドウを見せるためだけに存在する
カーソルの右側に浮いていて、ついてくる
Atomが参考になった

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

ReactとjQueryは相性悪い?
よくインターネットに書いてある
jQueryでDOM書き換えしなければok
jQuery クロスブラウザで座標の扱いが完璧っぽい

ReactとjQueryでflux

viewはstore (stateとprops)からのみ作られる
viewが他のviewを書き換えない
他のviewの位置を使って描画するviewはどうするの?

他のview componentの位置で位置補正するcomponent
storeが変更される
render()
componentDidUpdate が発火
ここでjQueryでDOMを見て座標取得
storeを更新、もしくは自分のstateを更新する
this.setState({x: 36, y: 250}) // 座標をセット
再renderされる
Fluxの円環の理を2回回すイメージ


複数人で同時編集

同時編集
実用Gitを読む
こういう「コミット」をやりとりすればなんとかなるのでは!?
commit.json
{ "id": hash, "parent_id": parent_hash, // 1つ前への参照 "changes": [ 変更内容, 変更内容, 変更内容 ] }


編集の命令は3つだけ

insert
隣の行のidを指定して新しい行を追加
js
{ _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する

Slack通知
差分だけ通知する
commitの中のinsert/update系をmergeして並べ替える

undo操作
自分がやった操作のみをundoしたい
同時編集中の他人の操作はundoしたくない

revertコミットを作って貯めておく
pushしてコンフリクトしなかったcommit
insert
同じ行idをdelete
update
元のテキストにupdate
delete
元の位置にinsert
ctrl-Z で逆順に適用する

おわり・まとめ
WYSIWYGで画像やリンクも貼れるけど、編集ツールバーやボタンが無いエディタ
Socket.IOと自作の同期システムでうまいこと同時編集できる

We are hiring
五山の送り火で有名な大文字山が見える
フルリモート可


YAPC2016Gyazzスライド