Skip to content

Instantly share code, notes, and snippets.

@totoraj930
Last active June 19, 2021 16:47
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save totoraj930/d1394dadb51d75666a76829f61e7280c to your computer and use it in GitHub Desktop.
Save totoraj930/d1394dadb51d75666a76829f61e7280c to your computer and use it in GitHub Desktop.
TweetDeckをスマホで使いやすくするスクリプト

使い方

  • bookmarklet.txtを全文コピーしてブックマークに追加します。
    • ここからが一番コピーしやすいと思います。
  • スマホからTweetDeckを開いてログインした状態でブックマークレットを実行します。

以上です。

実行方法がわからない場合は「スマホ ブックマークレット 実行方法」などで調べてください。

Androidの方へ

このスクリプトが自動で実行されるTweetDeck用ブラウザもあるので試してみてね

https://twitter.com/totoraj930/status/1031919574059376641

仕様など

スクリプトはこのGistから読み込まれるので更新不要です。

アクセス過多で怒られたので自分のサーバーから読み込むようにしました。

変なものは組み込んでませんが心配であれば読み込んでいるtj-deck.jsを確認してください。

かなり無茶な実装方法なので不安定だったり不具合がでたりするかもしれません。

Chrome以外で動作確認していないのでどうなってもしりませーーーーん!!!!!

追記: Gistとサーバーのスクリプト両方更新するの面倒くさいのでGistは更新しないことにしました。

Minify前のスクリプトもサーバーに置いておくのでそちらでソースコード確認してください。

javascript:function tjLoadScript(a){var b=new XMLHttpRequest;b.open("GET",a+"?v="+Date.now(),!0),b.responseType="text";var c=eval;b.addEventListener("load",function(){return 200!==b.status&&304!==b.status?void alert("script\u306E\u53D6\u5F97\u306B\u5931\u6557\u3057\u307E\u3057\u305F"):void c(b.responseText)}),b.send()}tjLoadScript("https://p.eriri.net/tj-deck/tj-deck.js");
// bookmarklet.txtのMinify前
function tjLoadScript(url) {
var req = new XMLHttpRequest();
req.open("GET", url + "?v=" + Date.now(), true);
req.responseType = "text";
req.addEventListener("load", function (event) {
if (req.status !== 200 && req.status !== 304) {
console.error("scriptの取得に失敗しました");
alert("scriptの取得に失敗しました");
return;
}
eval(req.responseText);
});
req.send();
}
tjLoadScript("https://p.eriri.net/tj-deck/tj-deck.js");
// このファイルは更新しません
// 最新のスクリプトは下記をご確認ください。
// https://p.eriri.net/tj-deck/main.js
// https://p.eriri.net/tj-deck/tj-deck.js
class TJScrollTask {
constructor($target, x, duration) {
this.$t = $target;
this.x = x;
this.d = duration;
this.sl = $target.scrollLeft;
this.sTime = Date.now();
this.ended = false;
this._bindAnim = this._anim.bind(this);
requestAnimationFrame(this._bindAnim);
}
stop() {
if (this.ended) return;
this.ended = true;
cancelAnimationFrame(this._bindAnim);
}
_anim() {
if (this.ended) return;
var t = (Date.now()-this.sTime)/this.d,
b = this.sl,
c = this.x - this.sl,
d = 1;
if (t > 1 && !this.ended) {
this.stop();
t = 1;
}
this.$t.scrollLeft = this._easeOut(t, b, c, d);
if (t < 1) requestAnimationFrame(this._bindAnim);
}
_easeOut(t, b, c, d) {
t /= d;
t = t-1;
return c*(t*t*t + 1) + b;
}
}
class TJTweetDeck {
constructor() {
this.$wrap = document.querySelector("#container");
}
back() {
// モーダルが表示中なら消して終了
var $mdlDismiss = document.querySelector(".mdl-dismiss");
if ($mdlDismiss) {
$mdlDismiss.click();
return;
}
// ツイートパネルが表示中なら消して終了
if (this.isShownDrawer()) {
this.hideDrawer();
return;
}
// カラムに戻るボタンがあれば押して終了
var $clm = this.getClosestColumn(this.$wrap.scrollLeft);
var $backToHome = $clm.querySelector(".js-column-back");
if ($backToHome) {
$backToHome.click();
return;
}
}
// 何か表示中ならtrue
isShownItem() {
return !!document.querySelector(".mdl-dismiss") || this.isShownDrawer();
}
// ドロワーが表示中ならtrue
isShownDrawer() {
return !!document.querySelector(".hide-detail-view-inline");
}
// ドロワーを非表示にする
hideDrawer() {
var $btn = document.querySelector(".js-hide-drawer");
if ($btn) $btn.click();
}
// ドロワーを表示する
showDrawer() {
var $btn = document.querySelector(".js-show-drawer");
if ($btn) $btn.click();
}
// 戻るボタンを管理する
manageBack() {
history.pushState(null, null, "");
window.addEventListener("popstate", function (event) {
this.back();
history.pushState(null, null, "");
}.bind(this));
}
// 横スクロールを管理する
manageScroll() {
var sPos;
var sTime = Date.now();
var prevPos;
var $prevClm;
var flag = null;// -1:開始前, 0:縦方向, 1:横方向
var scrollTask;
window.addEventListener("touchstart", function (event) {
if (event.touches.length > 1 || this.isShownItem()) return;
sPos = this._getPosObj(event);
prevPos = sPos;
flag = -1;
sTime = Date.now();
$prevClm = this.getClosestColumn(this.$wrap.scrollLeft);
}.bind(this));
window.addEventListener("touchmove", function (event) {
if (!flag) return;
if (flag < 0) {
var pos = this._getPosObj(event);
if (Math.abs(pos.x - sPos.x) < Math.abs(pos.y - sPos.y)) {
flag = 0;
return;
} else {
flag = 1;
}
}
if (flag == 1) {
if (scrollTask) scrollTask.stop();
var pos = this._getPosObj(event);
this.$wrap.scrollLeft += prevPos.x - pos.x;
prevPos = pos;
}
}.bind(this));
window.addEventListener("touchend", function (event) {
if (flag < 1) return;
flag = null;
var time = Date.now(),
pos = prevPos,
distance = sPos.x - pos.x;
var $targetClm;
// スワイプ時
if (Math.abs(distance) / (time-sTime) >= 0.5) {
if (distance > 0) {
$targetClm = $prevClm.nextElementSibling;
} else {
$targetClm = $prevClm.previousElementSibling;
}
}
else {
$targetClm = this.getClosestColumn(this.$wrap.scrollLeft);
}
if ($targetClm) {
if (scrollTask) scrollTask.stop();
scrollTask = new TJScrollTask(this.$wrap, $targetClm.offsetLeft, 500);
}
}.bind(this));
}
getClosestColumn(sL) {
var clms = document.querySelectorAll("section.column");
for (var i=0; i < clms.length; i++) {
var distance = Math.abs(sL - clms[i].offsetLeft);
if (distance <= clms[i].offsetWidth/2) {
return clms[i];
}
}
return clms[0];
}
_getPosObj(event) {
return {
x: event.touches[0].pageX,
y: event.touches[0].pageY
}
}
hideMenu() {
document.body.classList.add("tj_hide_menu");
}
showMenu() {
document.body.classList.remove("tj_hide_menu");
}
addTweetBtn() {
var $btn = document.createElement("button");
$btn.classList.add("tj_tweet_btn", "Button", "Button--primary", "tweet-button");
$btn.innerHTML = `<i class="Icon icon-compose"></i>`;
$btn.addEventListener("click", this.showDrawer);
// document.body.insertBefore($btn, document.body.firstChild);
document.querySelector(".js-app-content").appendChild($btn);
}
addStyle() {
var $head = document.querySelector("head"),
$style = document.createElement("style");
$style.rel = "stylesheet";
$style.type = "text/css";
$style.innerHTML =
`
body.tj_hide_menu {
position: fixed;
width: calc(100% + 50px);
height: 100%;
margin-left: -50px;
}
.tj_tweet_btn {
position: fixed!important;
width: 50px!important;
height: 50px!important;
bottom: 1em!important;
right: 1em!important;
padding: 0;
background-color: #1da1f2;
color: #fff;
border-radius: 36px;
font-size: 16px;
line-height: 1em;
text-align: center;
box-shadow: 1px 1px 5px rgba(0, 0, 0, .5);
z-index: 200;
}
.hide-detail-view-inline .tj_tweet_btn {
display: none;
}
.tj_tweet_btn .icon-compose {
display: inline-block;
margin-top: 0;
margin-left: 2px;
font-size: 20px;
}
.application {
z-index: auto;
}
/* カラムの余白をなくす */
.app-columns {
padding: 0!important;
}
/* カラムを幅いっぱいに表示 */
.column {
width: ${document.body.clientWidth}px!important;
max-width: 600px!important;
margin: 0!important;
}
/* カラムの設定をabsoluteに */
.js-column-options-container {
position: absolute!important;
width: 100%;
}
/* サイドパネルを表示したときにメインを動かなくする */
.application > .app-content {
margin-right: 0!important;
transform: translateX(0px)!important;
}
/* メインエリアのスクロールを禁止 */
#container {
overflow: hidden!important;
}
/* サイドパネルを幅いっぱいに表示 */
.js-drawer {
width: ${document.body.clientWidth}px!important;
max-width: 600px!important;
left: -${document.body.clientWidth}px!important;
}
.hide-detail-view-inline .js-drawer {/* 表示中 */
width: ${document.body.clientWidth}px!important;
max-width: 600px!important;
left: 0!important;
z-index: 201!important;
}
.hide-detail-view-inline .js-drawer:after {
display: none!important;
}
/* ツイートボタンを大きく */
.js-docked-compose .js-send-button {
width: 100px!important;
text-align: center;
}
/* サイドパネルのフッターを消す */
.js-docked-compose > footer {
display: none!important;
}
.js-docked-compose .compose-content {
bottom: 0!important;
}
/* サイドパネルのヘッダーを消す */
.js-compose-header {
position: absolute!important;
right: 20px!important;
border: 0!important;
}
header.js-compose-header div.compose-title {
display: none!important;
}
.js-account-selector-grid-toggle {
margin-right: 50px!important;
}
/* モーダルの位置調整 */
.overlay:before,
.ovl-plain:before,
.ovl:before {
display: none!important;
}
/* モーダルのメディア表示調整 */
.js-modal-panel .js-embeditem {/* 画面いっぱいに表示 */
height: 100%!important;
top: 0!important;
bottom: 0!important;
}
.js-modal-panel .js-embeditem iframe {
max-width: 100%!important;
max-height: 100%!important;
}
.js-modal-panel .js-med-tweet {/* ツイートを非表示 */
display: none!important;
}
`;
$head.appendChild($style);
}
}
var tj_tweetdeck = new TJTweetDeck();
tj_tweetdeck.addStyle();
tj_tweetdeck.manageScroll();
tj_tweetdeck.manageBack();
tj_tweetdeck.hideMenu();
tj_tweetdeck.addTweetBtn();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment