NISHIO Hirokazu[Translate]
Regroupの共同編集
Regroupで共同編集ツールがどうやって実現されてるのかの解説

ts
ReactDOM.render(<Router />, document.getElementById('root'));

ts
const Router = () => { return ( <HashRouter> <Route path="/:id" component={WhenHashSpecified} /> <Route exact path="/"> ... </Route> </HashRouter> ); }

ts
function WhenHashSpecified(obj: any) { return ( <App urlParam={obj.match.params.id} /> ); }

ts
const App = (props: { urlParam: string }) => { useUrlParam(props.urlParam) ...

ts
export const useUrlParam = (urlParam: string) => { useEffect(() => { // resolve map name // subscribe changes on server ... }, [urlParam]); const items = getGlobal().items; useEffect(() => { // save to server if items are changed }, [items]); };

// save to server if items are changed
マウスのドラッグで付箋の位置が動いた時に、各マウスムーブごとにリクエストが飛ぶともっさりするので100msごとにした
今は「そもそもマウスムーブごとにReact的状態の更新をするのも良くない」と考え直した
ts
if (toSave) { // do nothing } else { toSave = true; setTimeout(() => { toSave = false; saveToServer() }, 100) }

// resolve map name
URLパラメータを元に、参照すべきマップの名前を解決する。
最初に作ったときはマップを見るリンクで編集できて、その後で「編集できないけど閲覧できる」GoogleDocみたいなのを実現したくてこうなった。閲覧リンクから編集リンクを予想できない仕組み。
エラー処理は雑、キーが見つからない場合は白紙のマップにファールバックする
ここが「インターネットに接続してなかったらローカルのデータを表示」にしたい
ts
if (urlParam.match(/key=([^&]*)/)) { const key = RegExp.$1; db.collection("key_to_map").doc(key).get().then((doc: any) => { if (doc.exists) { console.log("map exists") const toConnect = true; const { isReadOnly, mapname } = doc.data() setGlobal({ toConnect: toConnect, isReadOnly: isReadOnly, mapname: mapname }) setUpReadSubscription(toConnect, isReadOnly, mapname); } else { // doc.data() will be undefined in this case console.log("No such document!"); } }).catch((error: any) => { console.log("Error getting document:", error); }); } else { // handle special case for development }

// subscribe changes on server
基本はこう
ts
let unsubscribe = db.collection("map").doc(mapname).onSnapshot((doc: any) => { loadFromServer(doc.data()); });
書き込み可能な時には「サーバのデータを読む前に手元で変更して上書きしないように」という意図で、まず読み込んでからsubscribeしてるが、そもそも読み込みが完了するまで編集不能にすべき

マップの新規作成
初期状態をサーバに書き込んで新規タブで開いてる
ネットワーク接続がない時にも新規作成したい
しかしaddのcatchはすぐには呼ばれない、黙って内部でリトライされてしまうため
Firestoreでオフライン判定単体ではできない件も関係してそう
ts
const createNewMap = () => { saveToNewMap(INITIAL_GLOBAL_STATE).then((docRef: any) => { window.open(`/#/key=${docRef.id}`) }) }
ts
export const saveToNewMap = (state: TYPE_GLOBAL_STATE) => { const doc = convertStateToFirestore(state); return db.collection("map").add(doc) .then((docRef: any) => { return db.collection("key_to_map").add( { mapname: docRef.id } ) }) .catch((error: any) => { alert(`Error: ${error}`) }); }


"Engineer's way of creating knowledge" the English version of my book is now available on [Engineer's way of creating knowledge]

(C)NISHIO Hirokazu / Converted from [Scrapbox] at [Edit]