:don: - TLジャンプしようとしてたやつ
by
unarist
2018-04-14 [2018/04/14 15:56:50]
動くといいね。masterではもう動かないよ。
@@ -9,8 +9,8 @@
// BUG: TIMELINE_REFRESH_SUCCESS always *concat* to cached timeline...
-((
- store = (() => {
+(() => {
+ const store = (() => {
// >= v16: to descendant
let current_node = document.querySelector('#mastodon')._reactRootContainer.current.child;
while ('function' === typeof current_node.type) {
@@ -18,19 +18,83 @@
if (store) return store;
current_node = current_node.child;
}
- })(),
- now = new Date(),
- now_array = [0, now.getFullYear(), now.getMonth() + 1, now.getDate(), now.getHours(), now.getMinutes()],
- query = String(prompt('Enter max_id:\n [[yyyy/]mm/dd ]hh:mm or status_id\nLeave empty to load latest timeline.')),
- query_date = query && query.match(/(?:(?:(\d{4})\D+)?(\d+)\D+(\d+)\D+)?(\d+):(\d+)/).map((e, i) => e || now_array[i]),
- max_id = query_date ? new Date(query_date[1], query_date[2] - 1, query_date[3], query_date[4], query_date[5], 0).getTime() * 2 ** 16 : query
-) =>
- fetch(`/api/v1/timelines/public?local=true${ max_id ? '&max_id=' + max_id : ''}`)
+ })();
+
+ const target = (() => {
+ // path match
+ const path_segments = location.pathname.split('/').slice(1);
+ if (path_segments[0] === 'accounts') {
+ const [, id, type] = path_segments;
+ const build = (label = '', append_key = '', append_path = '') =>
+ ({ label: label || `account timeline`, key: `accounts:${id}${append_key}`, path: `accounts/${id}${append_path}` });
+ switch (type) {
+ case 'media': return build('account media timeline', ':media', '?only_media=true');
+ case 'with_replies': return build('account timeline (w/ replies)', ':with_replies', '?exclude_replies=false');
+ default: return build('account timeline', '', 'exclude_replies=true'};
+ }
+ } else if (path_segments[0] === 'timelines') {
+ const [, type, id] = path_segments;
+ switch (type) {
+ case 'local': return { label: 'local', key: 'community', path: 'timelines/public?local=true'};
+ case 'public': return { label: 'federated', key: 'public', path: 'timelines/public'};
+ case 'tag': return { label: `#${id}`, key: `hashtag:${id}`, path: 'timelines/tag/${id}'};
+ }
+ }
+ // use pinned columns
+ if (document.querySelector('.fa-users.column-header__icon')) {
+ return { label: 'local', key: 'community', path: 'timelines/public?local=true'};
+ }
+ })();
+
+ if (!target) {
+ alert('No supported timeline detected.\nThis script finds:\n- account/local timeline on right-most column\n- pinned local timeline');
+ return;
+ }
+
+ const query = prompt(`Load statuses to ${target.label} timeline.\n\nEnter <max_id> | [[yyyy/]mm/dd ]hh:mm\nor leave empty to load latest timeline.`);
+ if (query === null) return;
+
+ const parseDateQuery = s => {
+ const now_array = ((d = new Date()) => [0, d.getFullYear(), d.getMonth() + 1, d.getDate(), d.getHours(), d.getMinutes()])();
+ const match = query.match(/(?:(?:(\d{4})\D+)?(\d+)\D+(\d+)\D+)?(\d+):(\d+)/).map((e, i) => e || now_array[i]);
+ return match && new Date(query_date[1], query_date[2] - 1, query_date[3], query_date[4], query_date[5], 0).getTime() * 2 ** 16;
+ }
+ const parseMaxIdQuery = s => (s.match(/^\d+$/) || {})[0];
+
+ const max_id = parseDateQuery(query) || parseMaxIdQuery(query);
+
+ if (!max_id) {
+ alert('Invalid query: ' + query);
+ return;
+ }
+
+
+ fetch(`/api/v1/${target.path}${target.path.includes('?') ? '&' : '?'}limit=40` + (max_id ? `&max_id=${max_id}` : ''))
.then(x => x.json())
- .then(x => store.dispatch({
- type: 'TIMELINE_REFRESH_SUCCESS',
- timeline: 'community',
- statuses: x,
- partial: false
- }))
-)();
+ .then(statuses => {
+ // TIMELINE_UPDATE trims timeline if the timeline has 40 statuses already
+ const scrollTop = store.getState().getIn(['timelines', target.key, 'top']);
+ store.dispatch({
+ type: 'TIMELINE_SCROLL_TOP',
+ timeline: target.key,
+ top: true
+ });
+ store.dispatch({
+ type: 'TIMELINE_REFRESH_SUCCESS',
+ timeline: target.key,
+ statuses: statuses.slice(1),
+ partial: false
+ });
+ store.dispatch({
+ type: 'TIMELINE_UPDATE',
+ timeline: target.key,
+ status: statuses[0],
+ references: []
+ });
+ store.dispatch({
+ type: 'TIMELINE_SCROLL_TOP',
+ timeline: target.key,
+ top: scrollTop
+ });
+ });
+})();
/*
* @title bookmarklet
* @description my bookmarklet
* @include http://*
* @license MIT License
* @require
* @private
*/
// BUG: TIMELINE_REFRESH_SUCCESS always *concat* to cached timeline...
(() => {
const store = (() => {
// >= v16: to descendant
let current_node = document.querySelector('#mastodon')._reactRootContainer.current.child;
while ('function' === typeof current_node.type) {
const store = current_node.memoizedProps.store;
if (store) return store;
current_node = current_node.child;
}
})();
const target = (() => {
// path match
const path_segments = location.pathname.split('/').slice(1);
if (path_segments[0] === 'accounts') {
const [, id, type] = path_segments;
const build = (label = '', append_key = '', append_path = '') =>
({ label: label || `account timeline`, key: `accounts:${id}${append_key}`, path: `accounts/${id}${append_path}` });
switch (type) {
case 'media': return build('account media timeline', ':media', '?only_media=true');
case 'with_replies': return build('account timeline (w/ replies)', ':with_replies', '?exclude_replies=false');
default: return build('account timeline', '', 'exclude_replies=true'};
}
} else if (path_segments[0] === 'timelines') {
const [, type, id] = path_segments;
switch (type) {
case 'local': return { label: 'local', key: 'community', path: 'timelines/public?local=true'};
case 'public': return { label: 'federated', key: 'public', path: 'timelines/public'};
case 'tag': return { label: `#${id}`, key: `hashtag:${id}`, path: 'timelines/tag/${id}'};
}
}
// use pinned columns
if (document.querySelector('.fa-users.column-header__icon')) {
return { label: 'local', key: 'community', path: 'timelines/public?local=true'};
}
})();
if (!target) {
alert('No supported timeline detected.\nThis script finds:\n- account/local timeline on right-most column\n- pinned local timeline');
return;
}
const query = prompt(`Load statuses to ${target.label} timeline.\n\nEnter <max_id> | [[yyyy/]mm/dd ]hh:mm\nor leave empty to load latest timeline.`);
if (query === null) return;
const parseDateQuery = s => {
const now_array = ((d = new Date()) => [0, d.getFullYear(), d.getMonth() + 1, d.getDate(), d.getHours(), d.getMinutes()])();
const match = query.match(/(?:(?:(\d{4})\D+)?(\d+)\D+(\d+)\D+)?(\d+):(\d+)/).map((e, i) => e || now_array[i]);
return match && new Date(query_date[1], query_date[2] - 1, query_date[3], query_date[4], query_date[5], 0).getTime() * 2 ** 16;
}
const parseMaxIdQuery = s => (s.match(/^\d+$/) || {})[0];
const max_id = parseDateQuery(query) || parseMaxIdQuery(query);
if (!max_id) {
alert('Invalid query: ' + query);
return;
}
fetch(`/api/v1/${target.path}${target.path.includes('?') ? '&' : '?'}limit=40` + (max_id ? `&max_id=${max_id}` : ''))
.then(x => x.json())
.then(statuses => {
// TIMELINE_UPDATE trims timeline if the timeline has 40 statuses already
const scrollTop = store.getState().getIn(['timelines', target.key, 'top']);
store.dispatch({
type: 'TIMELINE_SCROLL_TOP',
timeline: target.key,
top: true
});
store.dispatch({
type: 'TIMELINE_REFRESH_SUCCESS',
timeline: target.key,
statuses: statuses.slice(1),
partial: false
});
store.dispatch({
type: 'TIMELINE_UPDATE',
timeline: target.key,
status: statuses[0],
references: []
});
store.dispatch({
type: 'TIMELINE_SCROLL_TOP',
timeline: target.key,
top: scrollTop
});
});
})();
- Permalink
- このページへの個別リンクです。
- RAW
- 書かれたコードへの直接のリンクです。
- Packed
- 文字列が圧縮された書かれたコードへのリンクです。
- Userscript
- Greasemonkey 等で利用する場合の .user.js へのリンクです。
- Loader
- @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
- Metadata
- コード中にコメントで @xxx と書かれたメタデータの JSON です。