generated at
pin-diary
Next version: pin-diary-2
Latest version: takker99/pin-diarypin-diary-7
hr


ひとまず単発で実行できるものは書けた
ただSPAとしてページが書き換わるときにうまく再描画しないと正しく表示されない
pushStateをハンドリングできるイベントがなさそう...
/scrapboxlab/類似したタイトルのページを関連ページとして表示する window.pushState を書き換えて監視しています
ただ、それだとURLが変わるタイミングであってScrapboxのUIが書き変わるタイミングではないので、結局ページ内を監視することになりますが
対策
F5 を押す
タイトル監視
MutationObserver で監視
トップページにタイトル行ないから無理か
代わりに <title> を監視する
よさそう!
試しに実装してみた うまく動いてそう
ソート順を変えると崩れるから、そこも監視して再描画する必要がありそう
# dropdownMenuSortのinnerTextを監視する
ページ遷移時にDOMが消えてしまうので、監視がややこしくなる
監視の開始/終了のタイミングを測るのがややこしい
.page-list.clearfix .gridを監視する
どのページでもDOMが残る
scrapboxが大量にページカードを作るのにいちいち反応してpageがfreezeしてしまう。
一度空の日付ページが作られてしまうと、それがずっとPinされてしまうバグがある

/scrapboxlab/scrapbox.Layoutでトップページかどうかを判定
検索結果ページでも同じ list になってしまうっぽい
<title> の中身と併せて判定すればいけるかも
これはこれで便利だと思いますdnin
検索結果の先頭に日付ページが表示されるようになるのか。面白いな
done同じタブで別のprojectに移動すると、UserScriptが残ってしまう
直ってた
URL欄に別のprojectのURLを入力して飛んだとき
2020-10-23 23:39:03 直った
直って無さそう
scrapbox.Project.name で比較してるから全てのプロジェクトで動いてしまう
villagepump 以外のサイトにいったら作動させないようなflagを導入すればいいかな
const isVillagepump = true
if (location.pathname.slice(1, -1) !== scrapbox.Project.name) return で直接指定するだけでいいと思う
たしかに
flagだと対して変わらないかー
意図
なるべく判定処理を減らすようにしたかった
villagepump以外のscrapbox projectで handlePageChange を実行させないのは最初からわかっている
しかし現状は別projectでもタイトルが変更される度にEvent Handlerを呼び出して判定処理をするようになっている
いらない判定処理なので、最初から判定処理自体をしないようにしたかった
具体的には、villagepump以外のscrapbox projectに移動したタイミングで、 observer の監視を止めるようにする
しかし改めて考えてみると、そんな事する必要がないことに気づいた
タイトルは頻繁に変わるわけではないので、処理速度に影響を与えることはない
処理速度を向上させたいなら、もっと別の部分をtuningすべき
streamページからトップページへの遷移だとtitleが変わらずに再描画ができてない
titleが更新されたときにtitleの内容ではなくてlocation.hrefを比較するのがよさそう
実装してみた
なぜか期待通りに動かないと思ったらプロジェクト判定の条件式が逆だった
ページ遷移時の再描画が全く実行されてなかった
多分これでちゃんと動くようになった
<title> 以外を使ってページ遷移を監視する方法を探すかあ
雑にやるなら #app-container を監視すればいいけど、無駄にevent handlerが呼び出されてしまう。
程よい頻度で発行されるeventないかなあ
global汚染しちゃうから、moduleでscopeを区切ったほうが良さそう
でも後方互換性を保つのが難しいな
import /api/code/villagepump/pin-diary/user.js を使っている人に配慮しないといけない

user.js
const targetProject = 'villagepump'; const handlePageChange = callback => { callback() let path = null const handleObserve = (mutations) => { // 全部実行する必要はないので、1個だけ取り出す if (scrapbox.Project.name !== targetProject) return // URLが変わったときだけ実行する if (location.pathname !== path) callback() path = location.pathname } const observer1 = new MutationObserver(handleObserve); observer1.observe(document.querySelector('title'), { childList: true }) } const $ = (query, parent = document) => parent.querySelector(query) const $$ = query => [...document.querySelectorAll(query)] const zero = n => String(n).padStart(2, '0'); const timestamp = date => `${date.getFullYear()}/${zero(date.getMonth() + 1)}/${zero(date.getDate())}` const today = timestamp(new Date()); const path = `/${targetProject}/${encodeURIComponent(today)}` handlePageChange(() => { // トップページ以外では実行しない if (location.pathname.slice(1, -1) !== targetProject) return const pinCards = $$('.page-list-item.grid-style-item.pin') const [actualLastPin, lastPin] = pinCards.slice(pinCards.length - 2) console.log('lastPin: %o',lastPin)
↑これは何でしょうか?
最後にpinしたページを削除している?
最後にpinしたページを配列から取り出してるだけです
Array.prototype.pop()は要素削除も行ってしまいます
[...document.querySelectorAll(query)] で新たに生成した配列なのでDOMとは関係ないです
あ、そうでした。これコピーしたことになるんですね
user.js
// 日付ページを移動する const diary = $(`.page-list-item a[href="${path}"]`)?.parentNode console.log('The diary page card: %o',diary)

top pageに戻ったときにPinがおかしなことになる現象はこれで防げるはず
日付ページよりも更新日時が新しいページがあるときに発生する現象
user.old.js
if (lastPin === diary) { console.log('The diary page card is already pinned.'); return; }
防げなかった
top pageに戻るたびに、日付ページの位置を直したほうが確実だな
2020-12-05 19:27:12 pinしたページがが一つだけだと誤判定が起こってしまっていた
lastPin===undefined かつ diary===undefined だと最後のpinが日付ページだと誤判定してしまう
undefined判定を追加しておいた
user.js
// pinの最後が日付ページだったらつけ直す if (actualLastPin && (lastPin ?? actualLastPin) === diary) { console.log('The diary page card is already pinned.'); console.log('actualLastPin: %o',actualLastPin) actualLastPin?.after(lastPin); console.log('Finish pinning the today\'s diary page.') return; }
日付ページがなかったら、新しくカードを作る
user.js
if (diary) { diary.classList.add('pin'); // pinを作る const pinMark = document.createElement('div'); pinMark.classList.add('pin'); $('.hover', diary).after(pinMark) (lastPin ?? actualLastPin)?.after(diary) } else {

以下のコードだと、 lastPin のサムネイルが画像だった場合にバグる
div.description ではなく div.icon になるため
user.old.js
console.log('No diary page card found. Creating it....') const pin = lastPin.cloneNode(true) pin.style.opacity = 0.7 console.log('The new pinned card: %o',pin) $('a', pin).href = path $('.title', pin).textContent = today $('.description', pin).innerHTML = '' (lastPin ?? actualLastPin)?.after(pin)
下手にcloneせず、1からDOMを作ったほうが無難かも
user.js
console.log('No diary page card found. Creating it....') lastPin?.insertAdjacentHTML('afterend', ` <li class="page-list-item grid-style-item pin" style="opacity: 0.7;"> <a href="${path}" rel="route"> <div class="hover"></div> <div class="pin"></div> <div class="content"> <div class="header"> <div class="title">${today}</div> </div> <div class="description"></div> </div> </a> </li>`) } console.log('Finish pinning the today\'s diary page.') });


lastPinが空になる場合は何かおかしい状況なので、そもそも止めたほうがいいかもしれない
一つもPinがない状態とか
/villagepumpに限定すれば、ありえない状況ですけど……
別のprojectにも転用することをちょっと想定しました
その場合なら最初に追加しないといけなさそう


これでどうかな?
' を`に変えようとしたらインラインコードがそこで切れてしまうことに気づいた
\ `のようにエスケープできます
サンクスです!
よさそう
親要素ってどうやってcss selectorで取得するんだろう?
取得できないっぽい
まあjavascript HTMLElement.parentNode で取得できるから別ににいいか。


うまいこと .description の中身作れないかな
javascript
await fetch(`/api/pages/${path}`) .then(res => res.json()) .then(json => json.descriptions.join('\n'))
記法をうまく描画しないといけ無さそうだなぁ
/customize/scrapbox-parser.min.jsを使えば記法をparseして描画できます
でもそこまでする必要あるかなあ?
まず入れるだけ入れてみるか
いや当日のページが存在するなら、下に表示されているはずだから、単に移すだけでいいんじゃないか?
下に表示されている場合はそれでいけます
ただし、たくさんページを更新して当日ページが描画領域外に押し出されると、スクロールするまでDOMに入らなくなります。
document.querySelector('.grid').children.length を実行したら101が返ってきました
その場合を考慮すると、やはりAPIを叩くのが確実かと思います
100ページも更新されるのはそうそうないんじゃないか?
それもそうですね。じゃあ大丈夫かな
画面が小さいと描画領域外に押し出されてしまうことがある