generated at
スマホ操作時、トップを通ると選択範囲が動かない
要望に飛ばすべき?bsahd
要望飛ばしサンクスtakker
2024-05-09直ったって!!takker
朗報cFQ2f7LRuLYP
とりまこのページで動作確認しました。直ってそうtakker

from @基素
Scrapbox mobileでトップページを通るとreloadするまで範囲変更できないバグ
発生条件・原因不明
Scrapboxのトップページを通ると発生するっぽいですねbsahd
reloadで解決します
今回はこっち基素
文字列をダブルタップしてセレクトボタンを押したときに動かなくなることに気づくことがある
今度remote debugして挙動を調べてみますtakker
2024-05-05調査
範囲変更できないケースで指を動かすと、次のエラーが大量に現れる
おそらくtouchmoveもしくはpointermoveが発火するたびに発生している
error
index.js:2 Uncaught TypeError: Cannot read properties of null (reading 'lines') at PointerEvent.getCursorPoint (index.js:2:557141) at PointerEvent.nearestCursorPosition (index.js:2:557268) at PointerEvent.onTouchMove (index.js:2:560751) at HTMLDivElement.wrapper (index.js:2:2429884) at HTMLDivElement.dispatch (index.js:2:2040313) at be.handle (index.js:2:2038121) at HTMLDivElement.sentryWrapped (index.js:2:113900)
PointerEvent を調べる
PointerEvent.js
class PointerEvent extends j.Component { constructor(i); getLines() { return this.mobileLines } getCursorPoint(i) { var v = (0, W.default)(this.getLines().lines()).offset(); return (0, pe.calcCursorPoint)(i, { offset: v }) } nearestCursorPosition(i); focusAndSelectAll(i); setSelectionRange(i); componentDidMount(); componentDidUpdate(); componentWillUnmount(); resetSwipeCursor(i); reset(); onTouchStart(i); onTouchMove(i); onTouchMoveLines(i); onTouchEnd(i); onMouseDown(i); onMouseMove(i); onMouseUp(i); onClick(i); onDoubleMouseDown(i); onTrippleMouseDown(i); onResize(); render() { var i = this.props.children; return j.default.createElement("div", { onMouseDown: this.onMouseDown, onMouseMove: this.onMouseMove, onMouseUp: this.onMouseUp, onClick: this.onClick }, j.default.createElement(ie.default, _extends({ ref: i=>{ this.mobileLines = i } }, i.props)), j.default.createElement(ae.default, null)) } }
(一部のmethods定義は略した)
class componentのようだ
this.mobileLines null になってしまっているということらしい
mobileLines がコード中に登場するのは、上記コードにある2箇所のみ
とくに代入操作に限れば ie.default ref しかない
callback Refと推測される
breakpointを仕掛けて調べると、 this.mobileLines には Lines | null が代入されている
<div> onTouchMove にlistenerが登録されていないように見えるが、stack traceから推測するに、Reactの内部処理で自動で touchmove 時に呼び出されるよう設定されているものと思われる
黒魔術の塊だったのですぐには解読できない
ie.default を調べる
コードをたどると、 Lines というclass componentが ie.default だとわかる
Lines.js
class Lines extends j.Component { static get propTypes(); shouldComponentUpdate(i); lines() { return this.refs.lines } render() { for (var i = (0, ee.default)(this.props.lines, { cursorLine: this.props.cursorVisible && this.props.cursorPosition.line }), v = i[this.props.cursorPosition.line] && i[this.props.cursorPosition.line].id, _ = [], W = 0; W < i.length; W++) { var Y, le, ce = i[W], de = !ce.codeBlock && (0, ie.isEmptyLine)(ce) || !X.default.PageHistory.isEnable ? !X.default.Page.lastAccessed || ce.updated > X.default.Page.lastAccessed : !!X.default.Page.prevSnapshotCreated && ce.updated > X.default.Page.prevSnapshotCreated; if (null !== (Y = ce.codeBlock) && void 0 !== Y && Y.end && ["mermaid", "mmd"].includes(null === (le = ce.codeBlock) || void 0 === le ? void 0 : le.lang)) { for (var fe = [], pe = W; pe > 0; pe--) { var be = i[pe]; if (!be.codeBlock || be.codeBlock.start) break; fe.unshift(be.text), be.codeBlock.hasCursor || (be.unread && (de = be.unread), be.updated > ce.updated && (ce.updated = be.updated)) } ce.codeBlock.mermaid = fe.join("\n") } var ye = !(!ce.codeBlock && (0, ie.isEmptyLine)(ce) || !X.default.PageHistory.isEnable) && (!!X.default.Page.nextSnapshotLineIds && !X.default.Page.nextSnapshotLineIds.includes(ce.id)) , _e = ce.updated > X.default.Page.loaded , we = this.props.cursorVisible && v === ce.id , xe = this.props.permalinkLine === ce.id; _.push(j.default.createElement(Z.default, { key: ce.id, id: ce.id, userId: ce.userId, email: ce.email, updated: ce.updated, unread: de, updatedAfterLoad: _e, willDeleteNext: ye, isCursorLine: we, titleLine: 0 === W, permalinkLine: xe, cli: ce.cli, helpfeel: ce.helpfeel, codeBlock: ce.codeBlock, tableBlock: ce.tableBlock, numberList: ce.numberList, formulaLine: ce.formulaLine, quoteLine: ce.quoteLine, section: ce.section, numberOfImages: ce.numberOfImages, isSnapshot: this.props.isSnapshot, displayStyle: this.props.displayStyle, rtl: !ce.codeBlock && !ce.tableBlock && (0, ae.isRTLText)(ce.text), enableTranslation: this.props.enableTranslation }, ce.text)) } return j.default.createElement("div", { className: "lines", ref: "lines" }, _) } }
(一部のmethods定義は略した)
ref に文字列 "lines" を入れる記法が不明takker
どうやら古いrefの書き方らしい
こう書くと、例えば Lines では this.refs.lines div.lines を取得できるようになる
ごめんなさい、当方で調べられるのはここまでですtakker
Lines が代入されない条件が不明
選択範囲操作が有効な状態でも、 Lines null が交互に高速に代入されている状況を観測した
re-renderしまくってる?
PCではトップページを経由しても選択範囲変更が正常に働くのに、mobileだと働かなくなる理由が不明
cf. PointerEvent はPC/mobile双方で使われる共通プログラム
ここまでの情報をまとめてshokaiさんに報告すれば、なにか進展があるかも……!
以降はtwitter民に報告任せた
helpのコミュニティでいいんじゃないかなぁ基素
一応流しておくかー基素
ありがたやtakker
forum-jpに書いてくれたbsahdさんもありがとう
なんかすごい。
お疲れ様です基素yuyuko