整理した
This commit is contained in:
33
src/client/app/common/scripts/check-for-update.ts
Normal file
33
src/client/app/common/scripts/check-for-update.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import MiOS from '../mios';
|
||||
import { version as current } from '../../config';
|
||||
|
||||
export default async function(mios: MiOS, force = false, silent = false) {
|
||||
const meta = await mios.getMeta(force);
|
||||
const newer = meta.version;
|
||||
|
||||
if (newer != current) {
|
||||
localStorage.setItem('should-refresh', 'true');
|
||||
localStorage.setItem('v', newer);
|
||||
|
||||
// Clear cache (serive worker)
|
||||
try {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
navigator.serviceWorker.controller.postMessage('clear');
|
||||
}
|
||||
|
||||
navigator.serviceWorker.getRegistrations().then(registrations => {
|
||||
registrations.forEach(registration => registration.unregister());
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
if (!silent) {
|
||||
alert('%i18n:common.update-available%'.replace('{newer}', newer).replace('{current}', current));
|
||||
}
|
||||
|
||||
return newer;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
67
src/client/app/common/scripts/compose-notification.ts
Normal file
67
src/client/app/common/scripts/compose-notification.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import getPostSummary from '../../../../common/get-post-summary';
|
||||
import getReactionEmoji from '../../../../common/get-reaction-emoji';
|
||||
|
||||
type Notification = {
|
||||
title: string;
|
||||
body: string;
|
||||
icon: string;
|
||||
onclick?: any;
|
||||
};
|
||||
|
||||
// TODO: i18n
|
||||
|
||||
export default function(type, data): Notification {
|
||||
switch (type) {
|
||||
case 'drive_file_created':
|
||||
return {
|
||||
title: 'ファイルがアップロードされました',
|
||||
body: data.name,
|
||||
icon: data.url + '?thumbnail&size=64'
|
||||
};
|
||||
|
||||
case 'mention':
|
||||
return {
|
||||
title: `${data.user.name}さんから:`,
|
||||
body: getPostSummary(data),
|
||||
icon: data.user.avatarUrl + '?thumbnail&size=64'
|
||||
};
|
||||
|
||||
case 'reply':
|
||||
return {
|
||||
title: `${data.user.name}さんから返信:`,
|
||||
body: getPostSummary(data),
|
||||
icon: data.user.avatarUrl + '?thumbnail&size=64'
|
||||
};
|
||||
|
||||
case 'quote':
|
||||
return {
|
||||
title: `${data.user.name}さんが引用:`,
|
||||
body: getPostSummary(data),
|
||||
icon: data.user.avatarUrl + '?thumbnail&size=64'
|
||||
};
|
||||
|
||||
case 'reaction':
|
||||
return {
|
||||
title: `${data.user.name}: ${getReactionEmoji(data.reaction)}:`,
|
||||
body: getPostSummary(data.post),
|
||||
icon: data.user.avatarUrl + '?thumbnail&size=64'
|
||||
};
|
||||
|
||||
case 'unread_messaging_message':
|
||||
return {
|
||||
title: `${data.user.name}さんからメッセージ:`,
|
||||
body: data.text, // TODO: getMessagingMessageSummary(data),
|
||||
icon: data.user.avatarUrl + '?thumbnail&size=64'
|
||||
};
|
||||
|
||||
case 'othello_invited':
|
||||
return {
|
||||
title: '対局への招待があります',
|
||||
body: `${data.parent.name}さんから`,
|
||||
icon: data.parent.avatarUrl + '?thumbnail&size=64'
|
||||
};
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
8
src/client/app/common/scripts/contains.ts
Normal file
8
src/client/app/common/scripts/contains.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export default (parent, child) => {
|
||||
let node = child.parentNode;
|
||||
while (node) {
|
||||
if (node == parent) return true;
|
||||
node = node.parentNode;
|
||||
}
|
||||
return false;
|
||||
};
|
13
src/client/app/common/scripts/copy-to-clipboard.ts
Normal file
13
src/client/app/common/scripts/copy-to-clipboard.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Clipboardに値をコピー(TODO: 文字列以外も対応)
|
||||
*/
|
||||
export default val => {
|
||||
const form = document.createElement('textarea');
|
||||
form.textContent = val;
|
||||
document.body.appendChild(form);
|
||||
form.select();
|
||||
const result = document.execCommand('copy');
|
||||
document.body.removeChild(form);
|
||||
|
||||
return result;
|
||||
};
|
13
src/client/app/common/scripts/date-stringify.ts
Normal file
13
src/client/app/common/scripts/date-stringify.ts
Normal file
@ -0,0 +1,13 @@
|
||||
export default date => {
|
||||
if (typeof date == 'string') date = new Date(date);
|
||||
return (
|
||||
date.getFullYear() + '年' +
|
||||
(date.getMonth() + 1) + '月' +
|
||||
date.getDate() + '日' +
|
||||
' ' +
|
||||
date.getHours() + '時' +
|
||||
date.getMinutes() + '分' +
|
||||
' ' +
|
||||
`(${['日', '月', '火', '水', '木', '金', '土'][date.getDay()]})`
|
||||
);
|
||||
};
|
21
src/client/app/common/scripts/fuck-ad-block.ts
Normal file
21
src/client/app/common/scripts/fuck-ad-block.ts
Normal file
@ -0,0 +1,21 @@
|
||||
require('fuckadblock');
|
||||
|
||||
declare const fuckAdBlock: any;
|
||||
|
||||
export default (os) => {
|
||||
function adBlockDetected() {
|
||||
os.apis.dialog({
|
||||
title: '%fa:exclamation-triangle%広告ブロッカーを無効にしてください',
|
||||
text: '<strong>Misskeyは広告を掲載していません</strong>が、広告をブロックする機能が有効だと一部の機能が利用できなかったり、不具合が発生する場合があります。',
|
||||
actins: [{
|
||||
text: 'OK'
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
if (fuckAdBlock === undefined) {
|
||||
adBlockDetected();
|
||||
} else {
|
||||
fuckAdBlock.onDetected(adBlockDetected);
|
||||
}
|
||||
};
|
2
src/client/app/common/scripts/gcd.ts
Normal file
2
src/client/app/common/scripts/gcd.ts
Normal file
@ -0,0 +1,2 @@
|
||||
const gcd = (a, b) => !b ? a : gcd(b, a % b);
|
||||
export default gcd;
|
5
src/client/app/common/scripts/get-kao.ts
Normal file
5
src/client/app/common/scripts/get-kao.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export default () => [
|
||||
'(=^・・^=)',
|
||||
'v(‘ω’)v',
|
||||
'🐡( \'-\' 🐡 )フグパンチ!!!!'
|
||||
][Math.floor(Math.random() * 3)];
|
11
src/client/app/common/scripts/get-median.ts
Normal file
11
src/client/app/common/scripts/get-median.ts
Normal file
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* 中央値を求めます
|
||||
* @param samples サンプル
|
||||
*/
|
||||
export default function(samples) {
|
||||
if (!samples.length) return 0;
|
||||
const numbers = samples.slice(0).sort((a, b) => a - b);
|
||||
const middle = Math.floor(numbers.length / 2);
|
||||
const isEven = numbers.length % 2 === 0;
|
||||
return isEven ? (numbers[middle] + numbers[middle - 1]) / 2 : numbers[middle];
|
||||
}
|
21
src/client/app/common/scripts/loading.ts
Normal file
21
src/client/app/common/scripts/loading.ts
Normal file
@ -0,0 +1,21 @@
|
||||
const NProgress = require('nprogress');
|
||||
NProgress.configure({
|
||||
trickleSpeed: 500,
|
||||
showSpinner: false
|
||||
});
|
||||
|
||||
const root = document.getElementsByTagName('html')[0];
|
||||
|
||||
export default {
|
||||
start: () => {
|
||||
root.classList.add('progress');
|
||||
NProgress.start();
|
||||
},
|
||||
done: () => {
|
||||
root.classList.remove('progress');
|
||||
NProgress.done();
|
||||
},
|
||||
set: val => {
|
||||
NProgress.set(val);
|
||||
}
|
||||
};
|
53
src/client/app/common/scripts/parse-search-query.ts
Normal file
53
src/client/app/common/scripts/parse-search-query.ts
Normal file
@ -0,0 +1,53 @@
|
||||
export default function(qs: string) {
|
||||
const q = {
|
||||
text: ''
|
||||
};
|
||||
|
||||
qs.split(' ').forEach(x => {
|
||||
if (/^([a-z_]+?):(.+?)$/.test(x)) {
|
||||
const [key, value] = x.split(':');
|
||||
switch (key) {
|
||||
case 'user':
|
||||
q['includeUserUsernames'] = value.split(',');
|
||||
break;
|
||||
case 'exclude_user':
|
||||
q['excludeUserUsernames'] = value.split(',');
|
||||
break;
|
||||
case 'follow':
|
||||
q['following'] = value == 'null' ? null : value == 'true';
|
||||
break;
|
||||
case 'reply':
|
||||
q['reply'] = value == 'null' ? null : value == 'true';
|
||||
break;
|
||||
case 'repost':
|
||||
q['repost'] = value == 'null' ? null : value == 'true';
|
||||
break;
|
||||
case 'media':
|
||||
q['media'] = value == 'null' ? null : value == 'true';
|
||||
break;
|
||||
case 'poll':
|
||||
q['poll'] = value == 'null' ? null : value == 'true';
|
||||
break;
|
||||
case 'until':
|
||||
case 'since':
|
||||
// YYYY-MM-DD
|
||||
if (/^[0-9]+\-[0-9]+\-[0-9]+$/) {
|
||||
const [yyyy, mm, dd] = value.split('-');
|
||||
q[`${key}_date`] = (new Date(parseInt(yyyy, 10), parseInt(mm, 10) - 1, parseInt(dd, 10))).getTime();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
q[key] = value;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
q.text += x + ' ';
|
||||
}
|
||||
});
|
||||
|
||||
if (q.text) {
|
||||
q.text = q.text.trim();
|
||||
}
|
||||
|
||||
return q;
|
||||
}
|
13
src/client/app/common/scripts/streaming/channel.ts
Normal file
13
src/client/app/common/scripts/streaming/channel.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import Stream from './stream';
|
||||
import MiOS from '../../mios';
|
||||
|
||||
/**
|
||||
* Channel stream connection
|
||||
*/
|
||||
export default class Connection extends Stream {
|
||||
constructor(os: MiOS, channelId) {
|
||||
super(os, 'channel', {
|
||||
channel: channelId
|
||||
});
|
||||
}
|
||||
}
|
34
src/client/app/common/scripts/streaming/drive.ts
Normal file
34
src/client/app/common/scripts/streaming/drive.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import Stream from './stream';
|
||||
import StreamManager from './stream-manager';
|
||||
import MiOS from '../../mios';
|
||||
|
||||
/**
|
||||
* Drive stream connection
|
||||
*/
|
||||
export class DriveStream extends Stream {
|
||||
constructor(os: MiOS, me) {
|
||||
super(os, 'drive', {
|
||||
i: me.account.token
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class DriveStreamManager extends StreamManager<DriveStream> {
|
||||
private me;
|
||||
private os: MiOS;
|
||||
|
||||
constructor(os: MiOS, me) {
|
||||
super();
|
||||
|
||||
this.me = me;
|
||||
this.os = os;
|
||||
}
|
||||
|
||||
public getConnection() {
|
||||
if (this.connection == null) {
|
||||
this.connection = new DriveStream(this.os, this.me);
|
||||
}
|
||||
|
||||
return this.connection;
|
||||
}
|
||||
}
|
57
src/client/app/common/scripts/streaming/home.ts
Normal file
57
src/client/app/common/scripts/streaming/home.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import * as merge from 'object-assign-deep';
|
||||
|
||||
import Stream from './stream';
|
||||
import StreamManager from './stream-manager';
|
||||
import MiOS from '../../mios';
|
||||
|
||||
/**
|
||||
* Home stream connection
|
||||
*/
|
||||
export class HomeStream extends Stream {
|
||||
constructor(os: MiOS, me) {
|
||||
super(os, '', {
|
||||
i: me.account.token
|
||||
});
|
||||
|
||||
// 最終利用日時を更新するため定期的にaliveメッセージを送信
|
||||
setInterval(() => {
|
||||
this.send({ type: 'alive' });
|
||||
me.account.lastUsedAt = new Date();
|
||||
}, 1000 * 60);
|
||||
|
||||
// 自分の情報が更新されたとき
|
||||
this.on('i_updated', i => {
|
||||
if (os.debug) {
|
||||
console.log('I updated:', i);
|
||||
}
|
||||
merge(me, i);
|
||||
});
|
||||
|
||||
// トークンが再生成されたとき
|
||||
// このままではAPIが利用できないので強制的にサインアウトさせる
|
||||
this.on('my_token_regenerated', () => {
|
||||
alert('%i18n:common.my-token-regenerated%');
|
||||
os.signout();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class HomeStreamManager extends StreamManager<HomeStream> {
|
||||
private me;
|
||||
private os: MiOS;
|
||||
|
||||
constructor(os: MiOS, me) {
|
||||
super();
|
||||
|
||||
this.me = me;
|
||||
this.os = os;
|
||||
}
|
||||
|
||||
public getConnection() {
|
||||
if (this.connection == null) {
|
||||
this.connection = new HomeStream(this.os, this.me);
|
||||
}
|
||||
|
||||
return this.connection;
|
||||
}
|
||||
}
|
34
src/client/app/common/scripts/streaming/messaging-index.ts
Normal file
34
src/client/app/common/scripts/streaming/messaging-index.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import Stream from './stream';
|
||||
import StreamManager from './stream-manager';
|
||||
import MiOS from '../../mios';
|
||||
|
||||
/**
|
||||
* Messaging index stream connection
|
||||
*/
|
||||
export class MessagingIndexStream extends Stream {
|
||||
constructor(os: MiOS, me) {
|
||||
super(os, 'messaging-index', {
|
||||
i: me.account.token
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class MessagingIndexStreamManager extends StreamManager<MessagingIndexStream> {
|
||||
private me;
|
||||
private os: MiOS;
|
||||
|
||||
constructor(os: MiOS, me) {
|
||||
super();
|
||||
|
||||
this.me = me;
|
||||
this.os = os;
|
||||
}
|
||||
|
||||
public getConnection() {
|
||||
if (this.connection == null) {
|
||||
this.connection = new MessagingIndexStream(this.os, this.me);
|
||||
}
|
||||
|
||||
return this.connection;
|
||||
}
|
||||
}
|
20
src/client/app/common/scripts/streaming/messaging.ts
Normal file
20
src/client/app/common/scripts/streaming/messaging.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import Stream from './stream';
|
||||
import MiOS from '../../mios';
|
||||
|
||||
/**
|
||||
* Messaging stream connection
|
||||
*/
|
||||
export class MessagingStream extends Stream {
|
||||
constructor(os: MiOS, me, otherparty) {
|
||||
super(os, 'messaging', {
|
||||
i: me.account.token,
|
||||
otherparty
|
||||
});
|
||||
|
||||
(this as any).on('_connected_', () => {
|
||||
this.send({
|
||||
i: me.account.token
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
11
src/client/app/common/scripts/streaming/othello-game.ts
Normal file
11
src/client/app/common/scripts/streaming/othello-game.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import Stream from './stream';
|
||||
import MiOS from '../../mios';
|
||||
|
||||
export class OthelloGameStream extends Stream {
|
||||
constructor(os: MiOS, me, game) {
|
||||
super(os, 'othello-game', {
|
||||
i: me ? me.account.token : null,
|
||||
game: game.id
|
||||
});
|
||||
}
|
||||
}
|
31
src/client/app/common/scripts/streaming/othello.ts
Normal file
31
src/client/app/common/scripts/streaming/othello.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import StreamManager from './stream-manager';
|
||||
import Stream from './stream';
|
||||
import MiOS from '../../mios';
|
||||
|
||||
export class OthelloStream extends Stream {
|
||||
constructor(os: MiOS, me) {
|
||||
super(os, 'othello', {
|
||||
i: me.account.token
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class OthelloStreamManager extends StreamManager<OthelloStream> {
|
||||
private me;
|
||||
private os: MiOS;
|
||||
|
||||
constructor(os: MiOS, me) {
|
||||
super();
|
||||
|
||||
this.me = me;
|
||||
this.os = os;
|
||||
}
|
||||
|
||||
public getConnection() {
|
||||
if (this.connection == null) {
|
||||
this.connection = new OthelloStream(this.os, this.me);
|
||||
}
|
||||
|
||||
return this.connection;
|
||||
}
|
||||
}
|
30
src/client/app/common/scripts/streaming/requests.ts
Normal file
30
src/client/app/common/scripts/streaming/requests.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import Stream from './stream';
|
||||
import StreamManager from './stream-manager';
|
||||
import MiOS from '../../mios';
|
||||
|
||||
/**
|
||||
* Requests stream connection
|
||||
*/
|
||||
export class RequestsStream extends Stream {
|
||||
constructor(os: MiOS) {
|
||||
super(os, 'requests');
|
||||
}
|
||||
}
|
||||
|
||||
export class RequestsStreamManager extends StreamManager<RequestsStream> {
|
||||
private os: MiOS;
|
||||
|
||||
constructor(os: MiOS) {
|
||||
super();
|
||||
|
||||
this.os = os;
|
||||
}
|
||||
|
||||
public getConnection() {
|
||||
if (this.connection == null) {
|
||||
this.connection = new RequestsStream(this.os);
|
||||
}
|
||||
|
||||
return this.connection;
|
||||
}
|
||||
}
|
30
src/client/app/common/scripts/streaming/server.ts
Normal file
30
src/client/app/common/scripts/streaming/server.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import Stream from './stream';
|
||||
import StreamManager from './stream-manager';
|
||||
import MiOS from '../../mios';
|
||||
|
||||
/**
|
||||
* Server stream connection
|
||||
*/
|
||||
export class ServerStream extends Stream {
|
||||
constructor(os: MiOS) {
|
||||
super(os, 'server');
|
||||
}
|
||||
}
|
||||
|
||||
export class ServerStreamManager extends StreamManager<ServerStream> {
|
||||
private os: MiOS;
|
||||
|
||||
constructor(os: MiOS) {
|
||||
super();
|
||||
|
||||
this.os = os;
|
||||
}
|
||||
|
||||
public getConnection() {
|
||||
if (this.connection == null) {
|
||||
this.connection = new ServerStream(this.os);
|
||||
}
|
||||
|
||||
return this.connection;
|
||||
}
|
||||
}
|
108
src/client/app/common/scripts/streaming/stream-manager.ts
Normal file
108
src/client/app/common/scripts/streaming/stream-manager.ts
Normal file
@ -0,0 +1,108 @@
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import * as uuid from 'uuid';
|
||||
import Connection from './stream';
|
||||
|
||||
/**
|
||||
* ストリーム接続を管理するクラス
|
||||
* 複数の場所から同じストリームを利用する際、接続をまとめたりする
|
||||
*/
|
||||
export default abstract class StreamManager<T extends Connection> extends EventEmitter {
|
||||
private _connection: T = null;
|
||||
|
||||
private disposeTimerId: any;
|
||||
|
||||
/**
|
||||
* コネクションを必要としているユーザー
|
||||
*/
|
||||
private users = [];
|
||||
|
||||
protected set connection(connection: T) {
|
||||
this._connection = connection;
|
||||
|
||||
if (this._connection == null) {
|
||||
this.emit('disconnected');
|
||||
} else {
|
||||
this.emit('connected', this._connection);
|
||||
|
||||
this._connection.on('_connected_', () => {
|
||||
this.emit('_connected_');
|
||||
});
|
||||
|
||||
this._connection.on('_disconnected_', () => {
|
||||
this.emit('_disconnected_');
|
||||
});
|
||||
|
||||
this._connection.user = 'Managed';
|
||||
}
|
||||
}
|
||||
|
||||
protected get connection() {
|
||||
return this._connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* コネクションを持っているか否か
|
||||
*/
|
||||
public get hasConnection() {
|
||||
return this._connection != null;
|
||||
}
|
||||
|
||||
public get state(): string {
|
||||
if (!this.hasConnection) return 'no-connection';
|
||||
return this._connection.state;
|
||||
}
|
||||
|
||||
/**
|
||||
* コネクションを要求します
|
||||
*/
|
||||
public abstract getConnection(): T;
|
||||
|
||||
/**
|
||||
* 現在接続しているコネクションを取得します
|
||||
*/
|
||||
public borrow() {
|
||||
return this._connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* コネクションを要求するためのユーザーIDを発行します
|
||||
*/
|
||||
public use() {
|
||||
// タイマー解除
|
||||
if (this.disposeTimerId) {
|
||||
clearTimeout(this.disposeTimerId);
|
||||
this.disposeTimerId = null;
|
||||
}
|
||||
|
||||
// ユーザーID生成
|
||||
const userId = uuid();
|
||||
|
||||
this.users.push(userId);
|
||||
|
||||
this._connection.user = `Managed (${ this.users.length })`;
|
||||
|
||||
return userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* コネクションを利用し終わってもう必要ないことを通知します
|
||||
* @param userId use で発行したユーザーID
|
||||
*/
|
||||
public dispose(userId) {
|
||||
this.users = this.users.filter(id => id != userId);
|
||||
|
||||
this._connection.user = `Managed (${ this.users.length })`;
|
||||
|
||||
// 誰もコネクションの利用者がいなくなったら
|
||||
if (this.users.length == 0) {
|
||||
// また直ぐに再利用される可能性があるので、一定時間待ち、
|
||||
// 新たな利用者が現れなければコネクションを切断する
|
||||
this.disposeTimerId = setTimeout(() => {
|
||||
this.disposeTimerId = null;
|
||||
|
||||
this.connection.close();
|
||||
this.connection = null;
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
}
|
137
src/client/app/common/scripts/streaming/stream.ts
Normal file
137
src/client/app/common/scripts/streaming/stream.ts
Normal file
@ -0,0 +1,137 @@
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import * as uuid from 'uuid';
|
||||
import * as ReconnectingWebsocket from 'reconnecting-websocket';
|
||||
import { wsUrl } from '../../../config';
|
||||
import MiOS from '../../mios';
|
||||
|
||||
/**
|
||||
* Misskey stream connection
|
||||
*/
|
||||
export default class Connection extends EventEmitter {
|
||||
public state: string;
|
||||
private buffer: any[];
|
||||
public socket: ReconnectingWebsocket;
|
||||
public name: string;
|
||||
public connectedAt: Date;
|
||||
public user: string = null;
|
||||
public in: number = 0;
|
||||
public out: number = 0;
|
||||
public inout: Array<{
|
||||
type: 'in' | 'out',
|
||||
at: Date,
|
||||
data: string
|
||||
}> = [];
|
||||
public id: string;
|
||||
public isSuspended = false;
|
||||
private os: MiOS;
|
||||
|
||||
constructor(os: MiOS, endpoint, params?) {
|
||||
super();
|
||||
|
||||
//#region BIND
|
||||
this.onOpen = this.onOpen.bind(this);
|
||||
this.onClose = this.onClose.bind(this);
|
||||
this.onMessage = this.onMessage.bind(this);
|
||||
this.send = this.send.bind(this);
|
||||
this.close = this.close.bind(this);
|
||||
//#endregion
|
||||
|
||||
this.id = uuid();
|
||||
this.os = os;
|
||||
this.name = endpoint;
|
||||
this.state = 'initializing';
|
||||
this.buffer = [];
|
||||
|
||||
const query = params
|
||||
? Object.keys(params)
|
||||
.map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
|
||||
.join('&')
|
||||
: null;
|
||||
|
||||
this.socket = new ReconnectingWebsocket(`${wsUrl}/${endpoint}${query ? '?' + query : ''}`);
|
||||
this.socket.addEventListener('open', this.onOpen);
|
||||
this.socket.addEventListener('close', this.onClose);
|
||||
this.socket.addEventListener('message', this.onMessage);
|
||||
|
||||
// Register this connection for debugging
|
||||
this.os.registerStreamConnection(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback of when open connection
|
||||
*/
|
||||
private onOpen() {
|
||||
this.state = 'connected';
|
||||
this.emit('_connected_');
|
||||
|
||||
this.connectedAt = new Date();
|
||||
|
||||
// バッファーを処理
|
||||
const _buffer = [].concat(this.buffer); // Shallow copy
|
||||
this.buffer = []; // Clear buffer
|
||||
_buffer.forEach(data => {
|
||||
this.send(data); // Resend each buffered messages
|
||||
|
||||
if (this.os.debug) {
|
||||
this.out++;
|
||||
this.inout.push({ type: 'out', at: new Date(), data });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback of when close connection
|
||||
*/
|
||||
private onClose() {
|
||||
this.state = 'reconnecting';
|
||||
this.emit('_disconnected_');
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback of when received a message from connection
|
||||
*/
|
||||
private onMessage(message) {
|
||||
if (this.isSuspended) return;
|
||||
|
||||
if (this.os.debug) {
|
||||
this.in++;
|
||||
this.inout.push({ type: 'in', at: new Date(), data: message.data });
|
||||
}
|
||||
|
||||
try {
|
||||
const msg = JSON.parse(message.data);
|
||||
if (msg.type) this.emit(msg.type, msg.body);
|
||||
} catch (e) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to connection
|
||||
*/
|
||||
public send(data) {
|
||||
if (this.isSuspended) return;
|
||||
|
||||
// まだ接続が確立されていなかったらバッファリングして次に接続した時に送信する
|
||||
if (this.state != 'connected') {
|
||||
this.buffer.push(data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.os.debug) {
|
||||
this.out++;
|
||||
this.inout.push({ type: 'out', at: new Date(), data });
|
||||
}
|
||||
|
||||
this.socket.send(JSON.stringify(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Close this connection
|
||||
*/
|
||||
public close() {
|
||||
this.os.unregisterStreamConnection(this);
|
||||
this.socket.removeEventListener('open', this.onOpen);
|
||||
this.socket.removeEventListener('message', this.onMessage);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user