Create a gist now

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Mastodon - Mobile Composer
// ==UserScript==
// @name Mastodon - Mobile Composer
// @namespace https://github.com/unarist/
// @version 0.9
// @description add composer to page bottom
// @author unarist
// @downloadURL https://gist.github.com/unarist/08f56c49986d3b1775fe88bc918cac50/raw/mastodon-mobile-composer.user.js
// @match https://*/web/*
// @grant GM_getValue
// @grant GM_deleteValue
// ==/UserScript==
/*
v0.9: 詰ボタン追加(絵文字前後のスペースをzwspにする)
v0.8: v2.3.0の浮いてる投稿ボタン(#6594)対応
v0.7でShift押下の挙動を潰しました。はい。
旧挙動が必要な方はGistのリビジョンから漁ってください。(設定を変更していたら再設定も必要)
公開範囲を変更・指定する代わりの方法はそのうち考えます。
*/
(function() {
'use strict';
const tag = (name, props = {}, children = '') => {
const e = Object.assign(document.createElement(name), props);
Object.assign(e.style, props.style);
if (children.forEach) children.forEach(c => e.appendChild(c));
else e.textContent = children;
return e;
};
// migration
if (GM_getValue('alwaysPublic') === undefined)
GM_deleteValue('alwaysPublic');
const stylesheet = document.head.appendChild(tag('style', {}, `
@media (min-width: 1025px) {
.mobile-composer { display: none }
}
.mobile-composer {
position: unset;
height: auto;
flex: 0 0 auto;
padding: 5px;
}
.mobile-composer .spoiler-input__input,
.mobile-composer .autosuggest-textarea__textarea {
margin-bottom: 5px;
font-size: 14px;
padding: 0.5em;
border-radius: 4px;
}
.mobile-composer .autosuggest-textarea__textarea {
min-height: unset;
height: 5em !important;
}
.mobile-composer__buttons {
display: flex;
justify-content: flex-end;
}
.mobile-composer__button {
padding: 0 1em;
height: 2em;
line-height: 2em;
margin-left: 5px;
}
`)).sheet;
const refs = {};
const button = (t, onclick) =>
tag('button', {
className: 'button mobile-composer__button',
onclick
}, t);
const target = document.querySelector('.ui');
const initialState = JSON.parse(document.querySelector('#initial-state').textContent);
const api = (method, url, body) =>
fetch(url, {
method,
headers: {
'Authorization': 'Bearer ' + initialState.meta.access_token,
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
});
const clear = () => {
[refs.cw, refs.body].forEach(e => e.value = '');
refs.body.focus();
};
const post = (visibility) => {
if (!refs.body.value) return;
refs.buttons.style.display = 'none';
api('post', '/api/v1/statuses', {
status: refs.body.value,
spoiler_text: refs.cw.value,
visibility: visibility
})
.then(resp => resp.ok ? clear() : Promise.reject(resp.statusText))
.catch(window.alert)
.then(() => refs.buttons.style.display = null);
};
const collapseEmojiSpaces = () => {
// Replace spaces around of emoji code to U+200B ZERO WIDTH SPACE.
// It still needs to be wrapped with U+0020 SPACE unless it's placed at start/end of the line.
// inspired by https://gist.github.com/pacochi/ae6714d1c71a8ef52a3d5a3407886e94
// testcase: ":aa: a\nfoo :aa: :bb: bar\n:aa: :bar foo: :bar\nb :aa:"
refs.body.value =
refs.body.value
.replace(/: :/g, ':\x00\x00:')
.replace(/(?:(^|\n)|[ \u200b\x00])(:[\w\-]{2,}:)(?:($|\n)|[ \u200b\x00])/g, '$1\u200b$2\u200b$3')
.replace(/(:\x00+:)|(\u200b?)\x00(\u200b?)/g, (x,a,b,c) => a ? ': :' : (b || '') + (c || ''))
.replace(/\u200b{2,}/g, '\u200b')
.replace(/\u200b*(^|$|\n)\u200b*/g, '$1');
};
const getVisibility = (alternative = false) => 'public';
refs.root = tag('div', { className: 'drawer__inner mobile-composer' }, [
refs.cw = tag('input', {
type: 'text',
placeholder: 'CW',
className: 'spoiler-input__input'
}),
refs.body = tag('textarea', {
className: 'autosuggest-textarea__textarea',
onkeydown: e => void(e.keyCode == 13 && e.ctrlKey && post(getVisibility(e.shiftKey))),
oninput: e => void(refs.body.style.backgroundColor = (e.target.value.length > 500 ? 'pink' : null))
}),
refs.buttons = tag('div', { className: 'mobile-composer__buttons' }, [
button('', collapseEmojiSpaces),
button('×', clear),
button('DM', post.bind(null, 'direct')),
button('非公開', post.bind(null, 'private')),
button('未収載', post.bind(null, 'unlisted')),
button('公開', post.bind(null, 'public'))
])
]);
target.appendChild(refs.root);
// set offset for compose button introduced at v2.3.0rc1 (#6594)
stylesheet.insertRule(`.floating-action-button { margin-bottom: ${refs.root.clientHeight}px; }`, stylesheet.cssRules.length);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment