スマホ操作時、トップを通ると選択範囲が動かない
要望に飛ばすべき?
要望飛ばしサンクス
2024-05-09直ったって!!
朗報
とりまこのページで動作確認しました。直ってそう
Scrapbox mobileでトップページを通るとreloadするまで範囲変更できないバグ
発生条件・原因不明
reloadで解決します
今回はこっち
文字列をダブルタップしてセレクトボタンを押したときに動かなくなることに気づくことがある
今度remote debugして挙動を調べてみます
2024-05-05調査
範囲変更できないケースで指を動かすと、次のエラーが大量に現れる
errorindex.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.jsclass 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定義は略した)
this.mobileLines
が null
になってしまっているということらしい
mobileLines
がコード中に登場するのは、上記コードにある2箇所のみ
とくに代入操作に限れば ie.default
の ref
しかない
breakpointを仕掛けて調べると、 this.mobileLines
には Lines | null
が代入されている
<div>
の onTouchMove
にlistenerが登録されていないように見えるが、stack traceから推測するに、Reactの内部処理で自動で touchmove
時に呼び出されるよう設定されているものと思われる
黒魔術の塊だったのですぐには解読できない
ie.default
を調べる
コードをたどると、 Lines
というclass componentが ie.default
だとわかる
Lines.jsclass 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"
を入れる記法が不明
こう書くと、例えば Lines
では this.refs.lines
で div.lines
を取得できるようになる
ごめんなさい、当方で調べられるのはここまでです
Lines
が代入されない条件が不明
選択範囲操作が有効な状態でも、 Lines
と null
が交互に高速に代入されている状況を観測した
re-renderしまくってる?
PCではトップページを経由しても選択範囲変更が正常に働くのに、mobileだと働かなくなる理由が不明
cf. PointerEvent
はPC/mobile双方で使われる共通プログラム
ここまでの情報をまとめて
さんに報告すれば、なにか進展があるかも……!
以降はtwitter民に報告任せた
helpのコミュニティでいいんじゃないかなぁ
一応流しておくかー
ありがたや
forum-jpに書いてくれた
さんもありがとう
なんかすごい。
お疲れ様です