【無料】Chatworkを便利にするChromeエクステンション作ったので公開!(ソースコード解説有り)
チャットツールのChatworkをちょっと便利にするChromeのエクステンションを作ったのでソースコード付きで公開します!(リファクタも説明します)
ショートカットでいくつかの操作ができるようになりキーボード作業での操作が快適になるはずです。
1.なぜ作ったのか
会社のメインコミュニケーションツールがChatworkなのですが、メンションをキーボードでできないとか、使いづらいのがむかついたのでこれはなんとかマシになるように改造してやろうと思ったからです。
(ソースを書きたかったのもある)
2.何ができるようになるのか
以下のショートカットが実行できます。
①「Cntr + Space」 = TOポップアップを表示
②「Cntr + Alt」 = 未読があるチャット表示(もう一回同じキーを押すとすべてのチャットに戻る)
③*「Cntr + Shift」 = 現在表示しているメッセージのうち自分のメッセージのみ表示する
④*「Cntr + q」 = 現在表示しているメッセージのうち自分のメッセージを非表示にする
*③④はもう一回同じキーを押すと元に戻る、またスクロールして再読み込みが走ると元に戻ります
【メリット】
いちいちマウスを使わなくてもよく使う操作ができるようになります
(特に①②がショートカットでできると、キーボードのみでの操作性が良くなります) *③④はおまけ機能です笑
3.インストール方法
1.必要なファイルを用意する(下記で説明)
2.chrome://extensions/ を開く
3.デベロッパーモードをONにする
4.パッケージ化されていない拡張機能を読み込む →1を指定
5.正常に取り込まれた後、chatworkを表示(再読み込み)
(非エンジニアの方で今すぐ使いたい場合はこちらからDLしてください)
4.必要なファイル
(1) manifest.json //extensionに必要な必須ファイル(書き方) 【説明あり】
(2) main.js //メイン処理用【説明あり】
(3) jquery.min.js //jquery公式からDL
(4) shotcut.js //shortcutを簡単にバインドできるplugin (公式HP) *日本語デモ
(5) icon.png //アイコン表示用 png推奨
5.ソースコード解説
4-(1),(2)のみ説明します。ほかはリンク先よりDLしてください。
画像は適当に用意してください。
(1) manifest.json
{
"manifest_version": 2,
"name": "Chatwork Custom",
"description": "for chatwork useful",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png"
},
"content_scripts": [
{
"matches": [ "https://www.chatwork.com/*" ],
"js": ["jquery.min.js","main.js","shortcut.js"]
}
]
}
"js"は読み込むファイル名と同じであること
(2) main.js *リファクタ後のソース
//後ほどリファクタ前のソースも載せるのでぜひ比較してみてください
window.onload = function() {
var $toBtn = $('#_to');
var $unreadChat = $('#_chatStatusTypeUnread');
var $allChat = $('#_chatStatusAll');
var hideMode = false;
var showUnread = false;
const CHATWORK_CLASS = {
TIMELINE_MESSAGE : '.timelineMessage',
TIMELINE_MESSAGE_USER_NAME : '.timelineMessage__userName',
}
_init();
/*---------------------------*/
function _init() {
_bindShortCutEvents();
}
/**
* Bind Event on shortcut.js {@see http://www.openjs.com/scripts/events/keyboard_shortcuts/index.php}
*/
function _bindShortCutEvents() {
// 未読のあるチャットの表示
shortcut.add("Ctrl+Alt",function() {
if (!showUnread) {
$unreadChat.click();
showUnread = true;
} else {
$allChat.click();
showUnread = false;
}
});
// TOポップアップ表示
shortcut.add("Ctrl+Space",function() {
$toBtn.click();
});
// 自分のメッセージを非表示
shortcut.add("Ctrl+q",function() {
_changeMessage(_getLoginUserName(), false);
});
// 自分のメッセージのみ表示
shortcut.add("Ctrl+Shift",function() {
_changeMessage(_getLoginUserName(), true);
});
}
/**
* @return {String} loginUserName
*/
function _getLoginUserName() {
return $('#_myStatusName').text().trim();
}
/**
* @param {String} targetName
* @param {boolean} isShowSelf
*/
function _changeMessage(targetName, isShowSelf) {
var prevUserName = '';
var messageList = $(CHATWORK_CLASS.TIMELINE_MESSAGE);
for (i=0; i < messageList.length; i++) {
var $targetMessage = $(messageList[i]);
var userName = _getUserName($targetMessage);
if (_isChangeTarget(isShowSelf, userName, targetName, prevUserName)) {
_changeDisplay($targetMessage);
}
prevUserName = userName;
}
if (hideMode) {
hideMode = false;
} else {
hideMode = true;
}
}
/**
* @param {Element} $message
* @return {String} userName
*/
function _getUserName($message) {
var $userName = $message.find(CHATWORK_CLASS.TIMELINE_MESSAGE_USER_NAME);
return $userName ? $userName.text() : '';
}
/**
* @param {Element} $target
*/
function _changeDisplay($target) {
if (!hideMode) {
$target.hide();
} else {
$target.show();
}
}
/**
* @param {boolean} isShowSelf
* @param {String} userName
* @param {String} targetName
* @param {String} prevUserName
*
* @return {boolean} isChangeTarget
*/
function _isChangeTarget(isShowSelf, userName, targetName, prevUserName) {
if (isShowSelf && userName !== targetName) {
return true;
} else if (userName === '' && prevUserName === targetName) { // 連続投稿メッセージ
return true;
} else if (!isShowSelf && userName === targetName) {
return true;
} else {
return false;
}
}
};
↓リファクタ前のソース(動作は変わらない。もちろんだけど)
window.onload = function() {
var currentUserName = $('#_myStatusName').children()[0].innerText;
var $toBtn = $('#_to');
var $unreadChat = $('#_chatStatusTypeUnread');
var $allChat = $('#_chatStatusAll');
var hideMode = false;
var prevUserName = '';
var showUnread = false;
// 未読のあるチャットの表示
shortcut.add("Ctrl+Alt",function() {
if (!showUnread) {
$unreadChat.click();
showUnread = true;
} else {
$allChat.click();
showUnread = false;
}
});
// TOポップアップ表示
shortcut.add("Ctrl+Space",function() {
$toBtn.click();
});
// 自分のメッセージを非表示
shortcut.add("Ctrl+q",function() {
_changeMessage(currentUserName, false);
});
// 自分のメッセージのみ表示
shortcut.add("Ctrl+Shift",function() {
_changeMessage(currentUserName, true);
});
// ダブルクリックしたユーザーのみ表示
/* $('.timelineMessage--border').on('dblclick', function(event) {
var targetElm = $(this).find('.timelineMessage__userName').children()[0];
if (!targetElm) {
return;
}
var targetUserName = targetElm.innerText;
_changeMessage(targetUserName);
}); */
function _changeMessage(targetName, isShowSelf) {
var messageList = $('.timelineMessage');
for(i=0; i < messageList.length; i++) {
var m = messageList[i];
var $userNameElm = $(m).find('.timelineMessage__userName');
var span = $userNameElm.children()[0];
if (!span && isShowSelf && prevUserName !== targetName) {
_chageDisplay($(m));
continue;
} else if (!span && !isShowSelf && prevUserName === targetName) {
_chageDisplay($(m));
continue;
} else if (!span) {
continue;
}
var userName = span.innerText;
if (_isChageTarget(isShowSelf, userName, targetName, prevUserName)) {
_chageDisplay($(m));
}
prevUserName = userName;
}
if (hideMode) {
hideMode = false;
} else {
hideMode = true;
}
prevUserName = '';
}
function _chageDisplay($target) {
if (!hideMode) {
$target.hide();
} else {
$target.show();
}
}
function _isChageTarget(isShowSelf, userName, targetName, prevUserName) {
if (isShowSelf && userName !== targetName) {
return true;
} else if (userName === '' && prevUserName === targetName) { // 連続投稿
return true;
} else if (!isShowSelf && userName === targetName) {
return true;
} else {
return false;
}
}
};
さて何が違うでしょうか?なんでこういう修正をしたと思いますか?
【やったこと】
① メソッド分割
② 不要な記述削除
③ 冗長な処理/重複処理をまとめる(メソッドによせる)
④ 誤字修正
⑤ jsdocの記載
⑥ jQueryメソッドの利用
⑦ 定数定義(一部のクラス名)
理由はすべて可読性のためです。(あと無駄なコードを減らすため)
行数でいけばリファクタ後の方が多いですが、メソッド一つ一つ見ると難しいことはしてないのが分かると思います。
可動性が良い・無駄なコードがないことはバグの発生が起こりにくくなります。
また、第三者が理解しやすい、追いやすい、修正しやすくなるメリットもあります。
これらはどの言語でも共通です。
たかがjsとか思ってはいけません。
phpもrubyもjavaもswiftも特性や構成・構文が違えどすべてに言えます。
キレイなソースコードを書くためには?
「たくさんレビューをしてもらう」
「いろんなソースを読み解く」(なるだけキレイなやつ)
「たくさん自分で書く」
上記の経験がありきです。
英語のハウツーやコツを本で読んだからと言っていきなり流暢な英語はしゃべれませんよね?それと一緒です。
最近ではソースをあまり書かない仕事なのでレビューがないですが、
以前はMRの際に1日に最大数十の指摘をもらえることもありました。
(まさにコメントの嵐笑)
一つずつ意味を調べたり、なぜそうするのかを聞きながら、しっかり理解をしていって徐々に指摘は減っていきました。
それでも、一人レビューワーが増えただけで、また数十といくこともあります。
反省しながらもとても勉強になることばかりなので、レビューをしてもらうって大事だな、と本当に思います。
ここに書いてるソースが超キレイだとか最高というつもりは全然ないです。
こういう書き方もあるとか色々あって正解はないんです。
こうやってWeb上に表示しているもの(DOM)はいかようにも操作できるので初心者の方はChrome Extensionを作ってみることをオススメします。
さらにcssも追加できるのでできることは結構あります。
無理にAPIとか使わなくてもいいんです。
という感じで自己満で使ったエクステンションの話終わります。
このくらいChatworkの標準機能としてあってほしいですけどね(笑)
Chatworkでこんなことできない?これ不便だなと思う点があればぜひ教えください。
できそうなものならチャレンジしてみて、またnoteにアップします。
なにかあればTwitterでDMくださいませ。
最後まで読んでいただきありがとうございましたm(_ _)m