V10 (#2826)
* wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * Update CHANGELOG.md * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * Update CHANGELOG.md * Update CHANGELOG.md * wip * Update CHANGELOG.md * wip * wip * wip * wip
This commit is contained in:
@ -13,21 +13,21 @@ type Notification = {
|
||||
|
||||
export default function(type, data): Notification {
|
||||
switch (type) {
|
||||
case 'drive_file_created':
|
||||
case 'driveFileCreated':
|
||||
return {
|
||||
title: '%i18n:common.notification.file-uploaded%',
|
||||
body: data.name,
|
||||
icon: data.url
|
||||
};
|
||||
|
||||
case 'unread_messaging_message':
|
||||
case 'unreadMessagingMessage':
|
||||
return {
|
||||
title: '%i18n:common.notification.message-from%'.split("{}")[0] + `${getUserName(data.user)}` + '%i18n:common.notification.message-from%'.split("{}")[1] ,
|
||||
body: data.text, // TODO: getMessagingMessageSummary(data),
|
||||
icon: data.user.avatarUrl
|
||||
};
|
||||
|
||||
case 'reversi_invited':
|
||||
case 'reversiInvited':
|
||||
return {
|
||||
title: '%i18n:common.notification.reversi-invited%',
|
||||
body: '%i18n:common.notification.reversi-invited-by%'.split("{}")[0] + `${getUserName(data.parent)}` + '%i18n:common.notification.reversi-invited-by%'.split("{}")[1],
|
||||
|
105
src/client/app/common/scripts/note-subscriber.ts
Normal file
105
src/client/app/common/scripts/note-subscriber.ts
Normal file
@ -0,0 +1,105 @@
|
||||
import Vue from 'vue';
|
||||
|
||||
export default prop => ({
|
||||
data() {
|
||||
return {
|
||||
connection: null
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
$_ns_note_(): any {
|
||||
return this[prop];
|
||||
},
|
||||
|
||||
$_ns_isRenote(): boolean {
|
||||
return (this.$_ns_note_.renote &&
|
||||
this.$_ns_note_.text == null &&
|
||||
this.$_ns_note_.fileIds.length == 0 &&
|
||||
this.$_ns_note_.poll == null);
|
||||
},
|
||||
|
||||
$_ns_target(): any {
|
||||
return this._ns_isRenote ? this.$_ns_note_.renote : this.$_ns_note_;
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection = (this as any).os.stream;
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.capture(true);
|
||||
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection.on('_connected_', this.onStreamConnected);
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.decapture(true);
|
||||
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection.off('_connected_', this.onStreamConnected);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
capture(withHandler = false) {
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
const data = {
|
||||
id: this.$_ns_target.id
|
||||
} as any;
|
||||
|
||||
if (
|
||||
(this.$_ns_target.visibleUserIds || []).includes(this.$store.state.i.id) ||
|
||||
(this.$_ns_target.mentions || []).includes(this.$store.state.i.id)
|
||||
) {
|
||||
data.read = true;
|
||||
}
|
||||
|
||||
this.connection.send('sn', data);
|
||||
if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated);
|
||||
}
|
||||
},
|
||||
|
||||
decapture(withHandler = false) {
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection.send('un', {
|
||||
id: this.$_ns_target.id
|
||||
});
|
||||
if (withHandler) this.connection.off('noteUpdated', this.onStreamNoteUpdated);
|
||||
}
|
||||
},
|
||||
|
||||
onStreamConnected() {
|
||||
this.capture();
|
||||
},
|
||||
|
||||
onStreamNoteUpdated(data) {
|
||||
const { type, id, body } = data;
|
||||
|
||||
if (id !== this.$_ns_target.id) return;
|
||||
|
||||
switch (type) {
|
||||
case 'reacted': {
|
||||
const reaction = body.reaction;
|
||||
if (this.$_ns_target.reactionCounts == null) Vue.set(this.$_ns_target, 'reactionCounts', {});
|
||||
this.$_ns_target.reactionCounts[reaction] = (this.$_ns_target.reactionCounts[reaction] || 0) + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'pollVoted': {
|
||||
if (body.userId == this.$store.state.i.id) return;
|
||||
const choice = body.choice;
|
||||
this.$_ns_target.poll.choices.find(c => c.id === choice).votes++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.$emit(`update:${prop}`, this.$_ns_note_);
|
||||
},
|
||||
}
|
||||
});
|
318
src/client/app/common/scripts/stream.ts
Normal file
318
src/client/app/common/scripts/stream.ts
Normal file
@ -0,0 +1,318 @@
|
||||
import autobind from 'autobind-decorator';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import * as ReconnectingWebsocket from 'reconnecting-websocket';
|
||||
import { wsUrl } from '../../config';
|
||||
import MiOS from '../../mios';
|
||||
|
||||
/**
|
||||
* Misskey stream connection
|
||||
*/
|
||||
export default class Stream extends EventEmitter {
|
||||
private stream: ReconnectingWebsocket;
|
||||
private state: string;
|
||||
private buffer: any[];
|
||||
private sharedConnections: SharedConnection[] = [];
|
||||
private nonSharedConnections: NonSharedConnection[] = [];
|
||||
|
||||
constructor(os: MiOS) {
|
||||
super();
|
||||
|
||||
this.state = 'initializing';
|
||||
this.buffer = [];
|
||||
|
||||
const user = os.store.state.i;
|
||||
|
||||
this.stream = new ReconnectingWebsocket(wsUrl + (user ? `?i=${user.token}` : ''));
|
||||
this.stream.addEventListener('open', this.onOpen);
|
||||
this.stream.addEventListener('close', this.onClose);
|
||||
this.stream.addEventListener('message', this.onMessage);
|
||||
|
||||
if (user) {
|
||||
const main = this.useSharedConnection('main');
|
||||
|
||||
// 自分の情報が更新されたとき
|
||||
main.on('meUpdated', i => {
|
||||
os.store.dispatch('mergeMe', i);
|
||||
});
|
||||
|
||||
main.on('readAllNotifications', () => {
|
||||
os.store.dispatch('mergeMe', {
|
||||
hasUnreadNotification: false
|
||||
});
|
||||
});
|
||||
|
||||
main.on('unreadNotification', () => {
|
||||
os.store.dispatch('mergeMe', {
|
||||
hasUnreadNotification: true
|
||||
});
|
||||
});
|
||||
|
||||
main.on('readAllMessagingMessages', () => {
|
||||
os.store.dispatch('mergeMe', {
|
||||
hasUnreadMessagingMessage: false
|
||||
});
|
||||
});
|
||||
|
||||
main.on('unreadMessagingMessage', () => {
|
||||
os.store.dispatch('mergeMe', {
|
||||
hasUnreadMessagingMessage: true
|
||||
});
|
||||
});
|
||||
|
||||
main.on('unreadMention', () => {
|
||||
os.store.dispatch('mergeMe', {
|
||||
hasUnreadMentions: true
|
||||
});
|
||||
});
|
||||
|
||||
main.on('readAllUnreadMentions', () => {
|
||||
os.store.dispatch('mergeMe', {
|
||||
hasUnreadMentions: false
|
||||
});
|
||||
});
|
||||
|
||||
main.on('unreadSpecifiedNote', () => {
|
||||
os.store.dispatch('mergeMe', {
|
||||
hasUnreadSpecifiedNotes: true
|
||||
});
|
||||
});
|
||||
|
||||
main.on('readAllUnreadSpecifiedNotes', () => {
|
||||
os.store.dispatch('mergeMe', {
|
||||
hasUnreadSpecifiedNotes: false
|
||||
});
|
||||
});
|
||||
|
||||
main.on('clientSettingUpdated', x => {
|
||||
os.store.commit('settings/set', {
|
||||
key: x.key,
|
||||
value: x.value
|
||||
});
|
||||
});
|
||||
|
||||
main.on('homeUpdated', x => {
|
||||
os.store.commit('settings/setHome', x);
|
||||
});
|
||||
|
||||
main.on('mobileHomeUpdated', x => {
|
||||
os.store.commit('settings/setMobileHome', x);
|
||||
});
|
||||
|
||||
main.on('widgetUpdated', x => {
|
||||
os.store.commit('settings/setWidget', {
|
||||
id: x.id,
|
||||
data: x.data
|
||||
});
|
||||
});
|
||||
|
||||
// トークンが再生成されたとき
|
||||
// このままではMisskeyが利用できないので強制的にサインアウトさせる
|
||||
main.on('myTokenRegenerated', () => {
|
||||
alert('%i18n:common.my-token-regenerated%');
|
||||
os.signout();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public useSharedConnection = (channel: string): SharedConnection => {
|
||||
const existConnection = this.sharedConnections.find(c => c.channel === channel);
|
||||
|
||||
if (existConnection) {
|
||||
existConnection.use();
|
||||
return existConnection;
|
||||
} else {
|
||||
const connection = new SharedConnection(this, channel);
|
||||
connection.use();
|
||||
this.sharedConnections.push(connection);
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
public removeSharedConnection(connection: SharedConnection) {
|
||||
this.sharedConnections = this.sharedConnections.filter(c => c.id !== connection.id);
|
||||
}
|
||||
|
||||
public connectToChannel = (channel: string, params?: any): NonSharedConnection => {
|
||||
const connection = new NonSharedConnection(this, channel, params);
|
||||
this.nonSharedConnections.push(connection);
|
||||
return connection;
|
||||
}
|
||||
|
||||
@autobind
|
||||
public disconnectToChannel(connection: NonSharedConnection) {
|
||||
this.nonSharedConnections = this.nonSharedConnections.filter(c => c.id !== connection.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback of when open connection
|
||||
*/
|
||||
@autobind
|
||||
private onOpen() {
|
||||
const isReconnect = this.state == 'reconnecting';
|
||||
|
||||
this.state = 'connected';
|
||||
this.emit('_connected_');
|
||||
|
||||
// バッファーを処理
|
||||
const _buffer = [].concat(this.buffer); // Shallow copy
|
||||
this.buffer = []; // Clear buffer
|
||||
_buffer.forEach(data => {
|
||||
this.send(data); // Resend each buffered messages
|
||||
});
|
||||
|
||||
// チャンネル再接続
|
||||
if (isReconnect) {
|
||||
this.sharedConnections.forEach(c => {
|
||||
c.connect();
|
||||
});
|
||||
this.nonSharedConnections.forEach(c => {
|
||||
c.connect();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback of when close connection
|
||||
*/
|
||||
@autobind
|
||||
private onClose() {
|
||||
this.state = 'reconnecting';
|
||||
this.emit('_disconnected_');
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback of when received a message from connection
|
||||
*/
|
||||
@autobind
|
||||
private onMessage(message) {
|
||||
const { type, body } = JSON.parse(message.data);
|
||||
|
||||
if (type == 'channel') {
|
||||
const id = body.id;
|
||||
const connection = this.sharedConnections.find(c => c.id === id) || this.nonSharedConnections.find(c => c.id === id);
|
||||
connection.emit(body.type, body.body);
|
||||
} else {
|
||||
this.emit(type, body);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to connection
|
||||
*/
|
||||
@autobind
|
||||
public send(typeOrPayload, payload?) {
|
||||
const data = payload === undefined ? typeOrPayload : {
|
||||
type: typeOrPayload,
|
||||
body: payload
|
||||
};
|
||||
|
||||
// まだ接続が確立されていなかったらバッファリングして次に接続した時に送信する
|
||||
if (this.state != 'connected') {
|
||||
this.buffer.push(data);
|
||||
return;
|
||||
}
|
||||
|
||||
this.stream.send(JSON.stringify(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Close this connection
|
||||
*/
|
||||
@autobind
|
||||
public close() {
|
||||
this.stream.removeEventListener('open', this.onOpen);
|
||||
this.stream.removeEventListener('message', this.onMessage);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Connection extends EventEmitter {
|
||||
public channel: string;
|
||||
public id: string;
|
||||
protected params: any;
|
||||
protected stream: Stream;
|
||||
|
||||
constructor(stream: Stream, channel: string, params?: any) {
|
||||
super();
|
||||
|
||||
this.stream = stream;
|
||||
this.channel = channel;
|
||||
this.params = params;
|
||||
this.id = Math.random().toString();
|
||||
this.connect();
|
||||
}
|
||||
|
||||
@autobind
|
||||
public connect() {
|
||||
this.stream.send('connect', {
|
||||
channel: this.channel,
|
||||
id: this.id,
|
||||
params: this.params
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
public send(typeOrPayload, payload?) {
|
||||
const data = payload === undefined ? typeOrPayload : {
|
||||
type: typeOrPayload,
|
||||
body: payload
|
||||
};
|
||||
|
||||
this.stream.send('channel', {
|
||||
id: this.id,
|
||||
body: data
|
||||
});
|
||||
}
|
||||
|
||||
public abstract dispose: () => void;
|
||||
}
|
||||
|
||||
class SharedConnection extends Connection {
|
||||
private users = 0;
|
||||
private disposeTimerId: any;
|
||||
|
||||
constructor(stream: Stream, channel: string) {
|
||||
super(stream, channel);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public use() {
|
||||
this.users++;
|
||||
|
||||
// タイマー解除
|
||||
if (this.disposeTimerId) {
|
||||
clearTimeout(this.disposeTimerId);
|
||||
this.disposeTimerId = null;
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
public dispose() {
|
||||
this.users--;
|
||||
|
||||
// そのコネクションの利用者が誰もいなくなったら
|
||||
if (this.users === 0) {
|
||||
// また直ぐに再利用される可能性があるので、一定時間待ち、
|
||||
// 新たな利用者が現れなければコネクションを切断する
|
||||
this.disposeTimerId = setTimeout(() => {
|
||||
this.disposeTimerId = null;
|
||||
this.removeAllListeners();
|
||||
this.stream.send('disconnect', { id: this.id });
|
||||
this.stream.removeSharedConnection(this);
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NonSharedConnection extends Connection {
|
||||
constructor(stream: Stream, channel: string, params?: any) {
|
||||
super(stream, channel, params);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public dispose() {
|
||||
this.removeAllListeners();
|
||||
this.stream.send('disconnect', { id: this.id });
|
||||
this.stream.disconnectToChannel(this);
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
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.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;
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
import Stream from '../../stream';
|
||||
import MiOS from '../../../../../mios';
|
||||
|
||||
export class ReversiGameStream extends Stream {
|
||||
constructor(os: MiOS, me, game) {
|
||||
super(os, 'games/reversi-game', me ? {
|
||||
i: me.token,
|
||||
game: game.id
|
||||
} : {
|
||||
game: game.id
|
||||
});
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
import StreamManager from '../../stream-manager';
|
||||
import Stream from '../../stream';
|
||||
import MiOS from '../../../../../mios';
|
||||
|
||||
export class ReversiStream extends Stream {
|
||||
constructor(os: MiOS, me) {
|
||||
super(os, 'games/reversi', {
|
||||
i: me.token
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class ReversiStreamManager extends StreamManager<ReversiStream> {
|
||||
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 ReversiStream(this.os, this.me);
|
||||
}
|
||||
|
||||
return this.connection;
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
import Stream from './stream';
|
||||
import StreamManager from './stream-manager';
|
||||
import MiOS from '../../../mios';
|
||||
|
||||
/**
|
||||
* Global timeline stream connection
|
||||
*/
|
||||
export class GlobalTimelineStream extends Stream {
|
||||
constructor(os: MiOS, me) {
|
||||
super(os, 'global-timeline', {
|
||||
i: me.token
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class GlobalTimelineStreamManager extends StreamManager<GlobalTimelineStream> {
|
||||
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 GlobalTimelineStream(this.os, this.me);
|
||||
}
|
||||
|
||||
return this.connection;
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
import Stream from './stream';
|
||||
import MiOS from '../../../mios';
|
||||
|
||||
export class HashtagStream extends Stream {
|
||||
constructor(os: MiOS, me, q) {
|
||||
super(os, 'hashtag', me ? {
|
||||
i: me.token,
|
||||
q: JSON.stringify(q)
|
||||
} : {
|
||||
q: JSON.stringify(q)
|
||||
});
|
||||
}
|
||||
}
|
@ -1,126 +0,0 @@
|
||||
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.token
|
||||
});
|
||||
|
||||
// 最終利用日時を更新するため定期的にaliveメッセージを送信
|
||||
setInterval(() => {
|
||||
this.send({ type: 'alive' });
|
||||
me.lastUsedAt = new Date();
|
||||
}, 1000 * 60);
|
||||
|
||||
// 自分の情報が更新されたとき
|
||||
this.on('meUpdated', i => {
|
||||
if (os.debug) {
|
||||
console.log('I updated:', i);
|
||||
}
|
||||
|
||||
os.store.dispatch('mergeMe', i);
|
||||
});
|
||||
|
||||
this.on('read_all_notifications', () => {
|
||||
os.store.dispatch('mergeMe', {
|
||||
hasUnreadNotification: false
|
||||
});
|
||||
});
|
||||
|
||||
this.on('unread_notification', () => {
|
||||
os.store.dispatch('mergeMe', {
|
||||
hasUnreadNotification: true
|
||||
});
|
||||
});
|
||||
|
||||
this.on('read_all_messaging_messages', () => {
|
||||
os.store.dispatch('mergeMe', {
|
||||
hasUnreadMessagingMessage: false
|
||||
});
|
||||
});
|
||||
|
||||
this.on('unread_messaging_message', () => {
|
||||
os.store.dispatch('mergeMe', {
|
||||
hasUnreadMessagingMessage: true
|
||||
});
|
||||
});
|
||||
|
||||
this.on('unreadMention', () => {
|
||||
os.store.dispatch('mergeMe', {
|
||||
hasUnreadMentions: true
|
||||
});
|
||||
});
|
||||
|
||||
this.on('readAllUnreadMentions', () => {
|
||||
os.store.dispatch('mergeMe', {
|
||||
hasUnreadMentions: false
|
||||
});
|
||||
});
|
||||
|
||||
this.on('unreadSpecifiedNote', () => {
|
||||
os.store.dispatch('mergeMe', {
|
||||
hasUnreadSpecifiedNotes: true
|
||||
});
|
||||
});
|
||||
|
||||
this.on('readAllUnreadSpecifiedNotes', () => {
|
||||
os.store.dispatch('mergeMe', {
|
||||
hasUnreadSpecifiedNotes: false
|
||||
});
|
||||
});
|
||||
|
||||
this.on('clientSettingUpdated', x => {
|
||||
os.store.commit('settings/set', {
|
||||
key: x.key,
|
||||
value: x.value
|
||||
});
|
||||
});
|
||||
|
||||
this.on('home_updated', x => {
|
||||
os.store.commit('settings/setHome', x);
|
||||
});
|
||||
|
||||
this.on('mobile_home_updated', x => {
|
||||
os.store.commit('settings/setMobileHome', x);
|
||||
});
|
||||
|
||||
this.on('widgetUpdated', x => {
|
||||
os.store.commit('settings/setWidget', {
|
||||
id: x.id,
|
||||
data: x.data
|
||||
});
|
||||
});
|
||||
|
||||
// トークンが再生成されたとき
|
||||
// このままではMisskeyが利用できないので強制的にサインアウトさせる
|
||||
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;
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
import Stream from './stream';
|
||||
import StreamManager from './stream-manager';
|
||||
import MiOS from '../../../mios';
|
||||
|
||||
/**
|
||||
* Hybrid timeline stream connection
|
||||
*/
|
||||
export class HybridTimelineStream extends Stream {
|
||||
constructor(os: MiOS, me) {
|
||||
super(os, 'hybrid-timeline', {
|
||||
i: me.token
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class HybridTimelineStreamManager extends StreamManager<HybridTimelineStream> {
|
||||
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 HybridTimelineStream(this.os, this.me);
|
||||
}
|
||||
|
||||
return this.connection;
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
import Stream from './stream';
|
||||
import StreamManager from './stream-manager';
|
||||
import MiOS from '../../../mios';
|
||||
|
||||
/**
|
||||
* Local timeline stream connection
|
||||
*/
|
||||
export class LocalTimelineStream extends Stream {
|
||||
constructor(os: MiOS, me) {
|
||||
super(os, 'local-timeline', me ? {
|
||||
i: me.token
|
||||
} : {});
|
||||
}
|
||||
}
|
||||
|
||||
export class LocalTimelineStreamManager extends StreamManager<LocalTimelineStream> {
|
||||
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 LocalTimelineStream(this.os, this.me);
|
||||
}
|
||||
|
||||
return this.connection;
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
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.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;
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
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.token,
|
||||
otherparty
|
||||
});
|
||||
|
||||
(this as any).on('_connected_', () => {
|
||||
this.send({
|
||||
i: me.token
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
import Stream from './stream';
|
||||
import StreamManager from './stream-manager';
|
||||
import MiOS from '../../../mios';
|
||||
|
||||
/**
|
||||
* Notes stats stream connection
|
||||
*/
|
||||
export class NotesStatsStream extends Stream {
|
||||
constructor(os: MiOS) {
|
||||
super(os, 'notes-stats');
|
||||
}
|
||||
}
|
||||
|
||||
export class NotesStatsStreamManager extends StreamManager<NotesStatsStream> {
|
||||
private os: MiOS;
|
||||
|
||||
constructor(os: MiOS) {
|
||||
super();
|
||||
|
||||
this.os = os;
|
||||
}
|
||||
|
||||
public getConnection() {
|
||||
if (this.connection == null) {
|
||||
this.connection = new NotesStatsStream(this.os);
|
||||
}
|
||||
|
||||
return this.connection;
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
import Stream from './stream';
|
||||
import StreamManager from './stream-manager';
|
||||
import MiOS from '../../../mios';
|
||||
|
||||
/**
|
||||
* Server stats stream connection
|
||||
*/
|
||||
export class ServerStatsStream extends Stream {
|
||||
constructor(os: MiOS) {
|
||||
super(os, 'server-stats');
|
||||
}
|
||||
}
|
||||
|
||||
export class ServerStatsStreamManager extends StreamManager<ServerStatsStream> {
|
||||
private os: MiOS;
|
||||
|
||||
constructor(os: MiOS) {
|
||||
super();
|
||||
|
||||
this.os = os;
|
||||
}
|
||||
|
||||
public getConnection() {
|
||||
if (this.connection == null) {
|
||||
this.connection = new ServerStatsStream(this.os);
|
||||
}
|
||||
|
||||
return this.connection;
|
||||
}
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import * as uuid from 'uuid';
|
||||
import Connection from './stream';
|
||||
import { erase } from '../../../../../prelude/array';
|
||||
|
||||
/**
|
||||
* ストリーム接続を管理するクラス
|
||||
* 複数の場所から同じストリームを利用する際、接続をまとめたりする
|
||||
*/
|
||||
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 = erase(userId, this.users);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,137 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
import Stream from './stream';
|
||||
import MiOS from '../../mios';
|
||||
|
||||
export class UserListStream extends Stream {
|
||||
constructor(os: MiOS, me, listId) {
|
||||
super(os, 'user-list', {
|
||||
i: me.token,
|
||||
listId
|
||||
});
|
||||
|
||||
(this as any).on('_connected_', () => {
|
||||
this.send({
|
||||
i: me.token
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
@ -9,7 +9,6 @@
|
||||
import Vue from 'vue';
|
||||
import XGame from './reversi.game.vue';
|
||||
import XRoom from './reversi.room.vue';
|
||||
import { ReversiGameStream } from '../../../../scripts/streaming/games/reversi/reversi-game';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
@ -34,12 +33,13 @@ export default Vue.extend({
|
||||
},
|
||||
created() {
|
||||
this.g = this.game;
|
||||
this.connection = new ReversiGameStream((this as any).os, this.$store.state.i, this.game);
|
||||
this.connection = (this as any).os.stream.connectToChannel('gamesReversiGame', {
|
||||
gameId: this.game.id
|
||||
});
|
||||
this.connection.on('started', this.onStarted);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.connection.off('started', this.onStarted);
|
||||
this.connection.close();
|
||||
this.connection.dispose();
|
||||
},
|
||||
methods: {
|
||||
onStarted(game) {
|
||||
|
@ -59,15 +59,13 @@ export default Vue.extend({
|
||||
myGames: [],
|
||||
matching: null,
|
||||
invitations: [],
|
||||
connection: null,
|
||||
connectionId: null
|
||||
connection: null
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection = (this as any).os.streams.reversiStream.getConnection();
|
||||
this.connectionId = (this as any).os.streams.reversiStream.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('gamesReversi');
|
||||
|
||||
this.connection.on('invited', this.onInvited);
|
||||
|
||||
@ -90,8 +88,7 @@ export default Vue.extend({
|
||||
|
||||
beforeDestroy() {
|
||||
if (this.connection) {
|
||||
this.connection.off('invited', this.onInvited);
|
||||
(this as any).os.streams.reversiStream.dispose(this.connectionId);
|
||||
this.connection.dispose();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -47,7 +47,6 @@ export default Vue.extend({
|
||||
game: null,
|
||||
matching: null,
|
||||
connection: null,
|
||||
connectionId: null,
|
||||
pingClock: null
|
||||
};
|
||||
},
|
||||
@ -66,8 +65,7 @@ export default Vue.extend({
|
||||
this.fetch();
|
||||
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection = (this as any).os.streams.reversiStream.getConnection();
|
||||
this.connectionId = (this as any).os.streams.reversiStream.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('gamesReversi');
|
||||
|
||||
this.connection.on('matched', this.onMatched);
|
||||
|
||||
@ -84,9 +82,7 @@ export default Vue.extend({
|
||||
|
||||
beforeDestroy() {
|
||||
if (this.connection) {
|
||||
this.connection.off('matched', this.onMatched);
|
||||
(this as any).os.streams.reversiStream.dispose(this.connectionId);
|
||||
|
||||
this.connection.dispose();
|
||||
clearInterval(this.pingClock);
|
||||
}
|
||||
},
|
||||
|
@ -30,7 +30,6 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { MessagingStream } from '../../scripts/streaming/messaging';
|
||||
import XMessage from './messaging-room.message.vue';
|
||||
import XForm from './messaging-room.form.vue';
|
||||
import { url } from '../../../config';
|
||||
@ -72,7 +71,7 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.connection = new MessagingStream((this as any).os, this.$store.state.i, this.user.id);
|
||||
this.connection =((this as any).os.stream.connectToChannel('messaging', { otherparty: this.user.id });
|
||||
|
||||
this.connection.on('message', this.onMessage);
|
||||
this.connection.on('read', this.onRead);
|
||||
@ -92,9 +91,7 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.connection.off('message', this.onMessage);
|
||||
this.connection.off('read', this.onRead);
|
||||
this.connection.close();
|
||||
this.connection.dispose();
|
||||
|
||||
if (this.isNaked) {
|
||||
window.removeEventListener('scroll', this.onScroll);
|
||||
@ -166,6 +163,7 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
onMessage(message) {
|
||||
console.log(message);
|
||||
// サウンドを再生する
|
||||
if (this.$store.state.device.enableSounds) {
|
||||
const sound = new Audio(`${url}/assets/message.mp3`);
|
||||
|
@ -71,13 +71,11 @@ export default Vue.extend({
|
||||
messages: [],
|
||||
q: null,
|
||||
result: [],
|
||||
connection: null,
|
||||
connectionId: null
|
||||
connection: null
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.connection = (this as any).os.streams.messagingIndexStream.getConnection();
|
||||
this.connectionId = (this as any).os.streams.messagingIndexStream.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('messagingIndex');
|
||||
|
||||
this.connection.on('message', this.onMessage);
|
||||
this.connection.on('read', this.onRead);
|
||||
@ -88,9 +86,7 @@ export default Vue.extend({
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.connection.off('message', this.onMessage);
|
||||
this.connection.off('read', this.onRead);
|
||||
(this as any).os.streams.messagingIndexStream.dispose(this.connectionId);
|
||||
this.connection.dispose();
|
||||
},
|
||||
methods: {
|
||||
getAcct,
|
||||
|
@ -56,7 +56,7 @@ export default Vue.extend({
|
||||
username: this.username,
|
||||
password: this.password,
|
||||
token: this.user && this.user.twoFactorEnabled ? this.token : undefined
|
||||
}).then(() => {
|
||||
}, true).then(() => {
|
||||
location.reload();
|
||||
}).catch(() => {
|
||||
alert('%i18n:@login-failed%');
|
||||
|
@ -131,11 +131,11 @@ export default Vue.extend({
|
||||
password: this.password,
|
||||
invitationCode: this.invitationCode,
|
||||
'g-recaptcha-response': this.meta.recaptchaSitekey != null ? (window as any).grecaptcha.getResponse() : null
|
||||
}).then(() => {
|
||||
}, true).then(() => {
|
||||
(this as any).api('signin', {
|
||||
username: this.username,
|
||||
password: this.password
|
||||
}).then(() => {
|
||||
}, true).then(() => {
|
||||
location.href = '/';
|
||||
});
|
||||
}).catch(() => {
|
||||
|
@ -22,7 +22,7 @@ import * as anime from 'animejs';
|
||||
export default Vue.extend({
|
||||
computed: {
|
||||
stream() {
|
||||
return (this as any).os.stream;
|
||||
return (this as any).os.stream.useSharedConnection('main');
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
@ -38,23 +38,20 @@ export default Vue.extend({
|
||||
return {
|
||||
fetching: true,
|
||||
notes: [],
|
||||
connection: null,
|
||||
connectionId: null
|
||||
connection: null
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.fetch();
|
||||
|
||||
this.connection = (this as any).os.streams.localTimelineStream.getConnection();
|
||||
this.connectionId = (this as any).os.streams.localTimelineStream.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('localTimeline');
|
||||
|
||||
this.connection.on('note', this.onNote);
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.connection.off('note', this.onNote);
|
||||
(this as any).os.streams.localTimelineStream.dispose(this.connectionId);
|
||||
this.connection.dispose();
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@ -24,15 +24,13 @@ export default define({
|
||||
return {
|
||||
images: [],
|
||||
fetching: true,
|
||||
connection: null,
|
||||
connectionId: null
|
||||
connection: null
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.connection = (this as any).os.stream.getConnection();
|
||||
this.connectionId = (this as any).os.stream.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('main');
|
||||
|
||||
this.connection.on('drive_file_created', this.onDriveFileCreated);
|
||||
this.connection.on('driveFileCreated', this.onDriveFileCreated);
|
||||
|
||||
(this as any).api('drive/stream', {
|
||||
type: 'image/*',
|
||||
@ -43,8 +41,7 @@ export default define({
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.connection.off('drive_file_created', this.onDriveFileCreated);
|
||||
(this as any).os.stream.dispose(this.connectionId);
|
||||
this.connection.dispose();
|
||||
},
|
||||
methods: {
|
||||
onDriveFileCreated(file) {
|
||||
|
@ -82,7 +82,6 @@ export default define({
|
||||
data() {
|
||||
return {
|
||||
connection: null,
|
||||
connectionId: null,
|
||||
viewBoxY: 30,
|
||||
stats: [],
|
||||
fediGradientId: uuid(),
|
||||
@ -110,8 +109,7 @@ export default define({
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.connection = (this as any).os.streams.notesStatsStream.getConnection();
|
||||
this.connectionId = (this as any).os.streams.notesStatsStream.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('notesStats');
|
||||
|
||||
this.connection.on('stats', this.onStats);
|
||||
this.connection.on('statsLog', this.onStatsLog);
|
||||
@ -121,9 +119,7 @@ export default define({
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.connection.off('stats', this.onStats);
|
||||
this.connection.off('statsLog', this.onStatsLog);
|
||||
(this as any).os.streams.notesStatsStream.dispose(this.connectionId);
|
||||
this.connection.dispose();
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
|
@ -45,8 +45,7 @@ export default define({
|
||||
return {
|
||||
fetching: true,
|
||||
meta: null,
|
||||
connection: null,
|
||||
connectionId: null
|
||||
connection: null
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
@ -55,11 +54,10 @@ export default define({
|
||||
this.fetching = false;
|
||||
});
|
||||
|
||||
this.connection = (this as any).os.streams.serverStatsStream.getConnection();
|
||||
this.connectionId = (this as any).os.streams.serverStatsStream.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('serverStats');
|
||||
},
|
||||
beforeDestroy() {
|
||||
(this as any).os.streams.serverStatsStream.dispose(this.connectionId);
|
||||
this.connection.dispose();
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
|
@ -12,7 +12,7 @@ export const host = address.host;
|
||||
export const hostname = address.hostname;
|
||||
export const url = address.origin;
|
||||
export const apiUrl = url + '/api';
|
||||
export const wsUrl = url.replace('http://', 'ws://').replace('https://', 'wss://');
|
||||
export const wsUrl = url.replace('http://', 'ws://').replace('https://', 'wss://') + '/streaming';
|
||||
export const lang = _LANG_;
|
||||
export const langs = _LANGS_;
|
||||
export const themeColor = _THEME_COLOR_;
|
||||
|
@ -9,7 +9,6 @@ import './style.styl';
|
||||
|
||||
import init from '../init';
|
||||
import fuckAdBlock from '../common/scripts/fuck-ad-block';
|
||||
import { HomeStreamManager } from '../common/scripts/streaming/home';
|
||||
import composeNotification from '../common/scripts/compose-notification';
|
||||
|
||||
import chooseDriveFolder from './api/choose-drive-folder';
|
||||
@ -37,6 +36,7 @@ import MkTag from './views/pages/tag.vue';
|
||||
import MkReversi from './views/pages/games/reversi.vue';
|
||||
import MkShare from './views/pages/share.vue';
|
||||
import MkFollow from '../common/views/pages/follow.vue';
|
||||
import MiOS from '../mios';
|
||||
|
||||
/**
|
||||
* init
|
||||
@ -102,62 +102,56 @@ init(async (launch) => {
|
||||
}
|
||||
|
||||
if ((Notification as any).permission == 'granted') {
|
||||
registerNotifications(os.stream);
|
||||
registerNotifications(os);
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
|
||||
function registerNotifications(stream: HomeStreamManager) {
|
||||
function registerNotifications(os: MiOS) {
|
||||
const stream = os.stream;
|
||||
|
||||
if (stream == null) return;
|
||||
|
||||
if (stream.hasConnection) {
|
||||
attach(stream.borrow());
|
||||
}
|
||||
const connection = stream.useSharedConnection('main');
|
||||
|
||||
stream.on('connected', connection => {
|
||||
attach(connection);
|
||||
connection.on('notification', notification => {
|
||||
const _n = composeNotification('notification', notification);
|
||||
const n = new Notification(_n.title, {
|
||||
body: _n.body,
|
||||
icon: _n.icon
|
||||
});
|
||||
setTimeout(n.close.bind(n), 6000);
|
||||
});
|
||||
|
||||
function attach(connection) {
|
||||
connection.on('notification', notification => {
|
||||
const _n = composeNotification('notification', notification);
|
||||
const n = new Notification(_n.title, {
|
||||
body: _n.body,
|
||||
icon: _n.icon
|
||||
});
|
||||
setTimeout(n.close.bind(n), 6000);
|
||||
connection.on('driveFileCreated', file => {
|
||||
const _n = composeNotification('driveFileCreated', file);
|
||||
const n = new Notification(_n.title, {
|
||||
body: _n.body,
|
||||
icon: _n.icon
|
||||
});
|
||||
setTimeout(n.close.bind(n), 5000);
|
||||
});
|
||||
|
||||
connection.on('drive_file_created', file => {
|
||||
const _n = composeNotification('drive_file_created', file);
|
||||
const n = new Notification(_n.title, {
|
||||
body: _n.body,
|
||||
icon: _n.icon
|
||||
});
|
||||
setTimeout(n.close.bind(n), 5000);
|
||||
connection.on('unreadMessagingMessage', message => {
|
||||
const _n = composeNotification('unreadMessagingMessage', message);
|
||||
const n = new Notification(_n.title, {
|
||||
body: _n.body,
|
||||
icon: _n.icon
|
||||
});
|
||||
n.onclick = () => {
|
||||
n.close();
|
||||
/*(riot as any).mount(document.body.appendChild(document.createElement('mk-messaging-room-window')), {
|
||||
user: message.user
|
||||
});*/
|
||||
};
|
||||
setTimeout(n.close.bind(n), 7000);
|
||||
});
|
||||
|
||||
connection.on('unread_messaging_message', message => {
|
||||
const _n = composeNotification('unread_messaging_message', message);
|
||||
const n = new Notification(_n.title, {
|
||||
body: _n.body,
|
||||
icon: _n.icon
|
||||
});
|
||||
n.onclick = () => {
|
||||
n.close();
|
||||
/*(riot as any).mount(document.body.appendChild(document.createElement('mk-messaging-room-window')), {
|
||||
user: message.user
|
||||
});*/
|
||||
};
|
||||
setTimeout(n.close.bind(n), 7000);
|
||||
connection.on('reversiInvited', matching => {
|
||||
const _n = composeNotification('reversiInvited', matching);
|
||||
const n = new Notification(_n.title, {
|
||||
body: _n.body,
|
||||
icon: _n.icon
|
||||
});
|
||||
|
||||
connection.on('reversi_invited', matching => {
|
||||
const _n = composeNotification('reversi_invited', matching);
|
||||
const n = new Notification(_n.title, {
|
||||
body: _n.body,
|
||||
icon: _n.icon
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -98,8 +98,7 @@ export default Vue.extend({
|
||||
hierarchyFolders: [],
|
||||
selectedFiles: [],
|
||||
uploadings: [],
|
||||
connection: null,
|
||||
connectionId: null,
|
||||
connection: null
|
||||
|
||||
/**
|
||||
* ドロップされようとしているか
|
||||
@ -116,8 +115,7 @@ export default Vue.extend({
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.connection = (this as any).os.streams.driveStream.getConnection();
|
||||
this.connectionId = (this as any).os.streams.driveStream.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('drive');
|
||||
|
||||
this.connection.on('file_created', this.onStreamDriveFileCreated);
|
||||
this.connection.on('file_updated', this.onStreamDriveFileUpdated);
|
||||
@ -132,12 +130,7 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.connection.off('file_created', this.onStreamDriveFileCreated);
|
||||
this.connection.off('file_updated', this.onStreamDriveFileUpdated);
|
||||
this.connection.off('file_deleted', this.onStreamDriveFileDeleted);
|
||||
this.connection.off('folder_created', this.onStreamDriveFolderCreated);
|
||||
this.connection.off('folder_updated', this.onStreamDriveFolderUpdated);
|
||||
(this as any).os.streams.driveStream.dispose(this.connectionId);
|
||||
this.connection.dispose();
|
||||
},
|
||||
methods: {
|
||||
onContextmenu(e) {
|
||||
|
@ -34,23 +34,18 @@ export default Vue.extend({
|
||||
return {
|
||||
u: this.user,
|
||||
wait: false,
|
||||
connection: null,
|
||||
connectionId: null
|
||||
connection: null
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.connection = (this as any).os.stream.getConnection();
|
||||
this.connectionId = (this as any).os.stream.use();
|
||||
|
||||
this.connection = (this as any).os.stream.useSharedConnection('main');
|
||||
this.connection.on('follow', this.onFollow);
|
||||
this.connection.on('unfollow', this.onUnfollow);
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.connection.off('follow', this.onFollow);
|
||||
this.connection.off('unfollow', this.onUnfollow);
|
||||
(this as any).os.stream.dispose(this.connectionId);
|
||||
this.connection.dispose();
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@ -141,7 +141,6 @@ export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
connection: null,
|
||||
connectionId: null,
|
||||
widgetAdderSelected: null,
|
||||
trash: []
|
||||
};
|
||||
@ -176,12 +175,11 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.connection = (this as any).os.stream.getConnection();
|
||||
this.connectionId = (this as any).os.stream.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('main');
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
(this as any).os.stream.dispose(this.connectionId);
|
||||
this.connection.dispose();
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@ -93,12 +93,15 @@ import MkNoteMenu from '../../../common/views/components/note-menu.vue';
|
||||
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
|
||||
import XSub from './notes.note.sub.vue';
|
||||
import { sum } from '../../../../../prelude/array';
|
||||
import noteSubscriber from '../../../common/scripts/note-subscriber';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XSub
|
||||
},
|
||||
|
||||
mixins: [noteSubscriber('note')],
|
||||
|
||||
props: {
|
||||
note: {
|
||||
type: Object,
|
||||
|
@ -77,6 +77,7 @@ import MkNoteMenu from '../../../common/views/components/note-menu.vue';
|
||||
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
|
||||
import XSub from './notes.note.sub.vue';
|
||||
import { sum } from '../../../../../prelude/array';
|
||||
import noteSubscriber from '../../../common/scripts/note-subscriber';
|
||||
|
||||
function focus(el, fn) {
|
||||
const target = fn(el);
|
||||
@ -94,6 +95,8 @@ export default Vue.extend({
|
||||
XSub
|
||||
},
|
||||
|
||||
mixins: [noteSubscriber('note')],
|
||||
|
||||
props: {
|
||||
note: {
|
||||
type: Object,
|
||||
@ -104,9 +107,7 @@ export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
showContent: false,
|
||||
isDetailOpened: false,
|
||||
connection: null,
|
||||
connectionId: null
|
||||
isDetailOpened: false
|
||||
};
|
||||
},
|
||||
|
||||
@ -168,86 +169,7 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection = (this as any).os.stream.getConnection();
|
||||
this.connectionId = (this as any).os.stream.use();
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.capture(true);
|
||||
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection.on('_connected_', this.onStreamConnected);
|
||||
}
|
||||
|
||||
// Draw map
|
||||
if (this.p.geo) {
|
||||
const shouldShowMap = this.$store.getters.isSignedIn ? this.$store.state.settings.showMaps : true;
|
||||
if (shouldShowMap) {
|
||||
(this as any).os.getGoogleMaps().then(maps => {
|
||||
const uluru = new maps.LatLng(this.p.geo.coordinates[1], this.p.geo.coordinates[0]);
|
||||
const map = new maps.Map(this.$refs.map, {
|
||||
center: uluru,
|
||||
zoom: 15
|
||||
});
|
||||
new maps.Marker({
|
||||
position: uluru,
|
||||
map: map
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.decapture(true);
|
||||
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection.off('_connected_', this.onStreamConnected);
|
||||
(this as any).os.stream.dispose(this.connectionId);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
capture(withHandler = false) {
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
const data = {
|
||||
type: 'capture',
|
||||
id: this.p.id
|
||||
} as any;
|
||||
if ((this.p.visibleUserIds || []).includes(this.$store.state.i.id) || (this.p.mentions || []).includes(this.$store.state.i.id)) {
|
||||
data.read = true;
|
||||
}
|
||||
this.connection.send(data);
|
||||
if (withHandler) this.connection.on('note-updated', this.onStreamNoteUpdated);
|
||||
}
|
||||
},
|
||||
|
||||
decapture(withHandler = false) {
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection.send({
|
||||
type: 'decapture',
|
||||
id: this.p.id
|
||||
});
|
||||
if (withHandler) this.connection.off('note-updated', this.onStreamNoteUpdated);
|
||||
}
|
||||
},
|
||||
|
||||
onStreamConnected() {
|
||||
this.capture();
|
||||
},
|
||||
|
||||
onStreamNoteUpdated(data) {
|
||||
const note = data.note;
|
||||
if (note.id == this.note.id) {
|
||||
this.$emit('update:note', note);
|
||||
} else if (note.id == this.note.renoteId) {
|
||||
this.note.renote = note;
|
||||
}
|
||||
},
|
||||
|
||||
reply(viaKeyboard = false) {
|
||||
(this as any).os.new(MkPostFormWindow, {
|
||||
reply: this.p,
|
||||
|
@ -118,10 +118,10 @@ export default Vue.extend({
|
||||
notifications: [],
|
||||
moreNotifications: false,
|
||||
connection: null,
|
||||
connectionId: null,
|
||||
getNoteSummary
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
_notifications(): any[] {
|
||||
return (this.notifications as any).map(notification => {
|
||||
@ -133,9 +133,9 @@ export default Vue.extend({
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.connection = (this as any).os.stream.getConnection();
|
||||
this.connectionId = (this as any).os.stream.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('main');
|
||||
|
||||
this.connection.on('notification', this.onNotification);
|
||||
|
||||
@ -153,10 +153,11 @@ export default Vue.extend({
|
||||
this.fetching = false;
|
||||
});
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.connection.off('notification', this.onNotification);
|
||||
(this as any).os.stream.dispose(this.connectionId);
|
||||
this.connection.dispose();
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchMoreNotifications() {
|
||||
this.fetchingMoreNotifications = true;
|
||||
@ -177,10 +178,11 @@ export default Vue.extend({
|
||||
this.fetchingMoreNotifications = false;
|
||||
});
|
||||
},
|
||||
|
||||
onNotification(notification) {
|
||||
// TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない
|
||||
this.connection.send({
|
||||
type: 'read_notification',
|
||||
type: 'readNotification',
|
||||
id: notification.id
|
||||
});
|
||||
|
||||
|
@ -23,25 +23,25 @@ export default Vue.extend({
|
||||
return {
|
||||
fetching: true,
|
||||
signins: [],
|
||||
connection: null,
|
||||
connectionId: null
|
||||
connection: null
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
(this as any).api('i/signin_history').then(signins => {
|
||||
this.signins = signins;
|
||||
this.fetching = false;
|
||||
});
|
||||
|
||||
this.connection = (this as any).os.stream.getConnection();
|
||||
this.connectionId = (this as any).os.stream.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('main');
|
||||
|
||||
this.connection.on('signin', this.onSignin);
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.connection.off('signin', this.onSignin);
|
||||
(this as any).os.stream.dispose(this.connectionId);
|
||||
this.connection.dispose();
|
||||
},
|
||||
|
||||
methods: {
|
||||
onSignin(signin) {
|
||||
this.signins.unshift(signin);
|
||||
|
@ -15,7 +15,6 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { HashtagStream } from '../../../common/scripts/streaming/hashtag';
|
||||
|
||||
const fetchLimit = 10;
|
||||
|
||||
@ -35,9 +34,7 @@ export default Vue.extend({
|
||||
fetching: true,
|
||||
moreFetching: false,
|
||||
existMore: false,
|
||||
streamManager: null,
|
||||
connection: null,
|
||||
connectionId: null,
|
||||
date: null,
|
||||
baseQuery: {
|
||||
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
||||
@ -69,69 +66,33 @@ export default Vue.extend({
|
||||
this.query = {
|
||||
query: this.tagTl.query
|
||||
};
|
||||
this.connection = new HashtagStream((this as any).os, this.$store.state.i, this.tagTl.query);
|
||||
this.connection = (this as any).os.stream.connectToChannel('hashtag', { q: this.tagTl.query });
|
||||
this.connection.on('note', prepend);
|
||||
this.$once('beforeDestroy', () => {
|
||||
this.connection.off('note', prepend);
|
||||
this.connection.close();
|
||||
});
|
||||
} else if (this.src == 'home') {
|
||||
this.endpoint = 'notes/timeline';
|
||||
const onChangeFollowing = () => {
|
||||
this.fetch();
|
||||
};
|
||||
this.streamManager = (this as any).os.stream;
|
||||
this.connection = this.streamManager.getConnection();
|
||||
this.connectionId = this.streamManager.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('homeTimeline');
|
||||
this.connection.on('note', prepend);
|
||||
this.connection.on('follow', onChangeFollowing);
|
||||
this.connection.on('unfollow', onChangeFollowing);
|
||||
this.$once('beforeDestroy', () => {
|
||||
this.connection.off('note', prepend);
|
||||
this.connection.off('follow', onChangeFollowing);
|
||||
this.connection.off('unfollow', onChangeFollowing);
|
||||
this.streamManager.dispose(this.connectionId);
|
||||
});
|
||||
} else if (this.src == 'local') {
|
||||
this.endpoint = 'notes/local-timeline';
|
||||
this.streamManager = (this as any).os.streams.localTimelineStream;
|
||||
this.connection = this.streamManager.getConnection();
|
||||
this.connectionId = this.streamManager.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('localTimeline');
|
||||
this.connection.on('note', prepend);
|
||||
this.$once('beforeDestroy', () => {
|
||||
this.connection.off('note', prepend);
|
||||
this.streamManager.dispose(this.connectionId);
|
||||
});
|
||||
} else if (this.src == 'hybrid') {
|
||||
this.endpoint = 'notes/hybrid-timeline';
|
||||
this.streamManager = (this as any).os.streams.hybridTimelineStream;
|
||||
this.connection = this.streamManager.getConnection();
|
||||
this.connectionId = this.streamManager.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('hybridTimeline');
|
||||
this.connection.on('note', prepend);
|
||||
this.$once('beforeDestroy', () => {
|
||||
this.connection.off('note', prepend);
|
||||
this.streamManager.dispose(this.connectionId);
|
||||
});
|
||||
} else if (this.src == 'global') {
|
||||
this.endpoint = 'notes/global-timeline';
|
||||
this.streamManager = (this as any).os.streams.globalTimelineStream;
|
||||
this.connection = this.streamManager.getConnection();
|
||||
this.connectionId = this.streamManager.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('globalTimeline');
|
||||
this.connection.on('note', prepend);
|
||||
this.$once('beforeDestroy', () => {
|
||||
this.connection.off('note', prepend);
|
||||
this.streamManager.dispose(this.connectionId);
|
||||
});
|
||||
} else if (this.src == 'mentions') {
|
||||
this.endpoint = 'notes/mentions';
|
||||
this.streamManager = (this as any).os.stream;
|
||||
this.connection = this.streamManager.getConnection();
|
||||
this.connectionId = this.streamManager.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('main');
|
||||
this.connection.on('mention', prepend);
|
||||
this.$once('beforeDestroy', () => {
|
||||
this.connection.off('mention', prepend);
|
||||
this.streamManager.dispose(this.connectionId);
|
||||
});
|
||||
} else if (this.src == 'messages') {
|
||||
this.endpoint = 'notes/mentions';
|
||||
this.query = {
|
||||
@ -142,21 +103,15 @@ export default Vue.extend({
|
||||
prepend(note);
|
||||
}
|
||||
};
|
||||
this.streamManager = (this as any).os.stream;
|
||||
this.connection = this.streamManager.getConnection();
|
||||
this.connectionId = this.streamManager.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('main');
|
||||
this.connection.on('mention', onNote);
|
||||
this.$once('beforeDestroy', () => {
|
||||
this.connection.off('mention', onNote);
|
||||
this.streamManager.dispose(this.connectionId);
|
||||
});
|
||||
}
|
||||
|
||||
this.fetch();
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.$emit('beforeDestroy');
|
||||
this.connection.dispose();
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@ -42,8 +42,7 @@ export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
hasGameInvitations: false,
|
||||
connection: null,
|
||||
connectionId: null
|
||||
connection: null
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -53,18 +52,15 @@ export default Vue.extend({
|
||||
},
|
||||
mounted() {
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection = (this as any).os.stream.getConnection();
|
||||
this.connectionId = (this as any).os.stream.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('main');
|
||||
|
||||
this.connection.on('reversi_invited', this.onReversiInvited);
|
||||
this.connection.on('reversiInvited', this.onReversiInvited);
|
||||
this.connection.on('reversi_no_invites', this.onReversiNoInvites);
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection.off('reversi_invited', this.onReversiInvited);
|
||||
this.connection.off('reversi_no_invites', this.onReversiNoInvites);
|
||||
(this as any).os.stream.dispose(this.connectionId);
|
||||
this.connection.dispose();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { UserListStream } from '../../../common/scripts/streaming/user-list';
|
||||
|
||||
const fetchLimit = 10;
|
||||
|
||||
|
@ -56,13 +56,11 @@ export default Vue.extend({
|
||||
disableLocalTimeline: false,
|
||||
bannerUrl: null,
|
||||
inviteCode: null,
|
||||
connection: null,
|
||||
connectionId: null
|
||||
connection: null
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.connection = (this as any).os.streams.serverStatsStream.getConnection();
|
||||
this.connectionId = (this as any).os.streams.serverStatsStream.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('serverStats');
|
||||
|
||||
(this as any).os.getMeta().then(meta => {
|
||||
this.disableRegistration = meta.disableRegistration;
|
||||
@ -75,7 +73,7 @@ export default Vue.extend({
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
(this as any).os.streams.serverStatsStream.dispose(this.connectionId);
|
||||
this.connection.dispose();
|
||||
},
|
||||
methods: {
|
||||
invite() {
|
||||
|
@ -21,23 +21,19 @@ export default Vue.extend({
|
||||
fetching: true,
|
||||
moreFetching: false,
|
||||
existMore: false,
|
||||
connection: null,
|
||||
connectionId: null
|
||||
connection: null
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.connection = (this as any).os.stream.getConnection();
|
||||
this.connectionId = (this as any).os.stream.use();
|
||||
|
||||
this.connection = (this as any).os.stream.useSharedConnection('main');
|
||||
this.connection.on('mention', this.onNote);
|
||||
|
||||
this.fetch();
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.connection.off('mention', this.onNote);
|
||||
(this as any).os.stream.dispose(this.connectionId);
|
||||
this.connection.dispose();
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@ -5,7 +5,6 @@
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import XNotes from './deck.notes.vue';
|
||||
import { HashtagStream } from '../../../../common/scripts/streaming/hashtag';
|
||||
|
||||
const fetchLimit = 10;
|
||||
|
||||
@ -48,7 +47,7 @@ export default Vue.extend({
|
||||
|
||||
mounted() {
|
||||
if (this.connection) this.connection.close();
|
||||
this.connection = new HashtagStream((this as any).os, this.$store.state.i, this.tagTl.query);
|
||||
this.connection = (this as any).os.stream.connectToChannel('hashtag', this.tagTl.query);
|
||||
this.connection.on('note', this.onNote);
|
||||
|
||||
this.fetch();
|
||||
|
@ -5,7 +5,6 @@
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import XNotes from './deck.notes.vue';
|
||||
import { UserListStream } from '../../../../common/scripts/streaming/user-list';
|
||||
|
||||
const fetchLimit = 10;
|
||||
|
||||
|
@ -21,23 +21,19 @@ export default Vue.extend({
|
||||
fetching: true,
|
||||
moreFetching: false,
|
||||
existMore: false,
|
||||
connection: null,
|
||||
connectionId: null
|
||||
connection: null
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.connection = (this as any).os.stream.getConnection();
|
||||
this.connectionId = (this as any).os.stream.use();
|
||||
|
||||
this.connection = (this as any).os.stream.useSharedConnection('main');
|
||||
this.connection.on('mention', this.onNote);
|
||||
|
||||
this.fetch();
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.connection.off('mention', this.onNote);
|
||||
(this as any).os.stream.dispose(this.connectionId);
|
||||
this.connection.dispose();
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@ -70,12 +70,15 @@ import parse from '../../../../../../mfm/parse';
|
||||
import MkNoteMenu from '../../../../common/views/components/note-menu.vue';
|
||||
import MkReactionPicker from '../../../../common/views/components/reaction-picker.vue';
|
||||
import XSub from './deck.note.sub.vue';
|
||||
import noteSubscriber from '../../../../common/scripts/note-subscriber';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XSub
|
||||
},
|
||||
|
||||
mixins: [noteSubscriber('note')],
|
||||
|
||||
props: {
|
||||
note: {
|
||||
type: Object,
|
||||
@ -90,9 +93,7 @@ export default Vue.extend({
|
||||
|
||||
data() {
|
||||
return {
|
||||
showContent: false,
|
||||
connection: null,
|
||||
connectionId: null
|
||||
showContent: false
|
||||
};
|
||||
},
|
||||
|
||||
@ -120,68 +121,7 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection = (this as any).os.stream.getConnection();
|
||||
this.connectionId = (this as any).os.stream.use();
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.capture(true);
|
||||
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection.on('_connected_', this.onStreamConnected);
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.decapture(true);
|
||||
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection.off('_connected_', this.onStreamConnected);
|
||||
(this as any).os.stream.dispose(this.connectionId);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
capture(withHandler = false) {
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
const data = {
|
||||
type: 'capture',
|
||||
id: this.p.id
|
||||
} as any;
|
||||
if ((this.p.visibleUserIds || []).includes(this.$store.state.i.id) || (this.p.mentions || []).includes(this.$store.state.i.id)) {
|
||||
data.read = true;
|
||||
}
|
||||
this.connection.send(data);
|
||||
if (withHandler) this.connection.on('note-updated', this.onStreamNoteUpdated);
|
||||
}
|
||||
},
|
||||
|
||||
decapture(withHandler = false) {
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection.send({
|
||||
type: 'decapture',
|
||||
id: this.p.id
|
||||
});
|
||||
if (withHandler) this.connection.off('note-updated', this.onStreamNoteUpdated);
|
||||
}
|
||||
},
|
||||
|
||||
onStreamConnected() {
|
||||
this.capture();
|
||||
},
|
||||
|
||||
onStreamNoteUpdated(data) {
|
||||
const note = data.note;
|
||||
if (note.id == this.note.id) {
|
||||
this.$emit('update:note', note);
|
||||
} else if (note.id == this.note.renoteId) {
|
||||
this.note.renote = note;
|
||||
}
|
||||
},
|
||||
|
||||
reply() {
|
||||
(this as any).apis.post({
|
||||
reply: this.p
|
||||
|
@ -38,8 +38,7 @@ export default Vue.extend({
|
||||
notifications: [],
|
||||
queue: [],
|
||||
moreNotifications: false,
|
||||
connection: null,
|
||||
connectionId: null
|
||||
connection: null
|
||||
};
|
||||
},
|
||||
|
||||
@ -62,8 +61,7 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.connection = (this as any).os.stream.getConnection();
|
||||
this.connectionId = (this as any).os.stream.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('main');
|
||||
|
||||
this.connection.on('notification', this.onNotification);
|
||||
|
||||
@ -86,8 +84,7 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.connection.off('notification', this.onNotification);
|
||||
(this as any).os.stream.dispose(this.connectionId);
|
||||
this.connection.dispose();
|
||||
|
||||
this.column.$off('top', this.onTop);
|
||||
this.column.$off('bottom', this.onBottom);
|
||||
@ -117,7 +114,7 @@ export default Vue.extend({
|
||||
onNotification(notification) {
|
||||
// TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない
|
||||
this.connection.send({
|
||||
type: 'read_notification',
|
||||
type: 'readNotification',
|
||||
id: notification.id
|
||||
});
|
||||
|
||||
|
@ -36,18 +36,17 @@ export default Vue.extend({
|
||||
fetching: true,
|
||||
moreFetching: false,
|
||||
existMore: false,
|
||||
connection: null,
|
||||
connectionId: null
|
||||
connection: null
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
stream(): any {
|
||||
switch (this.src) {
|
||||
case 'home': return (this as any).os.stream;
|
||||
case 'local': return (this as any).os.streams.localTimelineStream;
|
||||
case 'hybrid': return (this as any).os.streams.hybridTimelineStream;
|
||||
case 'global': return (this as any).os.streams.globalTimelineStream;
|
||||
case 'home': return (this as any).os.stream.useSharedConnection('homeTimeline');
|
||||
case 'local': return (this as any).os.stream.useSharedConnection('localTimeline');
|
||||
case 'hybrid': return (this as any).os.stream.useSharedConnection('hybridTimeline');
|
||||
case 'global': return (this as any).os.stream.useSharedConnection('globalTimeline');
|
||||
}
|
||||
},
|
||||
|
||||
@ -68,8 +67,7 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.connection = this.stream.getConnection();
|
||||
this.connectionId = this.stream.use();
|
||||
this.connection = this.stream;
|
||||
|
||||
this.connection.on('note', this.onNote);
|
||||
if (this.src == 'home') {
|
||||
@ -81,12 +79,7 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.connection.off('note', this.onNote);
|
||||
if (this.src == 'home') {
|
||||
this.connection.off('follow', this.onChangeFollowing);
|
||||
this.connection.off('unfollow', this.onChangeFollowing);
|
||||
}
|
||||
this.stream.dispose(this.connectionId);
|
||||
this.connection.dispose();
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@ -1,3 +1,4 @@
|
||||
import autobind from 'autobind-decorator';
|
||||
import Vue from 'vue';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import * as uuid from 'uuid';
|
||||
@ -5,19 +6,9 @@ import * as uuid from 'uuid';
|
||||
import initStore from './store';
|
||||
import { apiUrl, version, lang } from './config';
|
||||
import Progress from './common/scripts/loading';
|
||||
import Connection from './common/scripts/streaming/stream';
|
||||
import { HomeStreamManager } from './common/scripts/streaming/home';
|
||||
import { DriveStreamManager } from './common/scripts/streaming/drive';
|
||||
import { ServerStatsStreamManager } from './common/scripts/streaming/server-stats';
|
||||
import { NotesStatsStreamManager } from './common/scripts/streaming/notes-stats';
|
||||
import { MessagingIndexStreamManager } from './common/scripts/streaming/messaging-index';
|
||||
import { ReversiStreamManager } from './common/scripts/streaming/games/reversi/reversi';
|
||||
|
||||
import Err from './common/views/components/connect-failed.vue';
|
||||
import { LocalTimelineStreamManager } from './common/scripts/streaming/local-timeline';
|
||||
import { HybridTimelineStreamManager } from './common/scripts/streaming/hybrid-timeline';
|
||||
import { GlobalTimelineStreamManager } from './common/scripts/streaming/global-timeline';
|
||||
import { erase } from '../../prelude/array';
|
||||
import Stream from './common/scripts/stream';
|
||||
|
||||
//#region api requests
|
||||
let spinner = null;
|
||||
@ -102,30 +93,7 @@ export default class MiOS extends EventEmitter {
|
||||
/**
|
||||
* A connection manager of home stream
|
||||
*/
|
||||
public stream: HomeStreamManager;
|
||||
|
||||
/**
|
||||
* Connection managers
|
||||
*/
|
||||
public streams: {
|
||||
localTimelineStream: LocalTimelineStreamManager;
|
||||
hybridTimelineStream: HybridTimelineStreamManager;
|
||||
globalTimelineStream: GlobalTimelineStreamManager;
|
||||
driveStream: DriveStreamManager;
|
||||
serverStatsStream: ServerStatsStreamManager;
|
||||
notesStatsStream: NotesStatsStreamManager;
|
||||
messagingIndexStream: MessagingIndexStreamManager;
|
||||
reversiStream: ReversiStreamManager;
|
||||
} = {
|
||||
localTimelineStream: null,
|
||||
hybridTimelineStream: null,
|
||||
globalTimelineStream: null,
|
||||
driveStream: null,
|
||||
serverStatsStream: null,
|
||||
notesStatsStream: null,
|
||||
messagingIndexStream: null,
|
||||
reversiStream: null
|
||||
};
|
||||
public stream: Stream;
|
||||
|
||||
/**
|
||||
* A registration of service worker
|
||||
@ -151,71 +119,36 @@ export default class MiOS extends EventEmitter {
|
||||
|
||||
this.shouldRegisterSw = shouldRegisterSw;
|
||||
|
||||
//#region BIND
|
||||
this.log = this.log.bind(this);
|
||||
this.logInfo = this.logInfo.bind(this);
|
||||
this.logWarn = this.logWarn.bind(this);
|
||||
this.logError = this.logError.bind(this);
|
||||
this.init = this.init.bind(this);
|
||||
this.api = this.api.bind(this);
|
||||
this.getMeta = this.getMeta.bind(this);
|
||||
this.registerSw = this.registerSw.bind(this);
|
||||
//#endregion
|
||||
|
||||
if (this.debug) {
|
||||
(window as any).os = this;
|
||||
}
|
||||
}
|
||||
|
||||
private googleMapsIniting = false;
|
||||
|
||||
public getGoogleMaps() {
|
||||
return new Promise((res, rej) => {
|
||||
if ((window as any).google && (window as any).google.maps) {
|
||||
res((window as any).google.maps);
|
||||
} else {
|
||||
this.once('init-google-maps', () => {
|
||||
res((window as any).google.maps);
|
||||
});
|
||||
|
||||
//#region load google maps api
|
||||
if (!this.googleMapsIniting) {
|
||||
this.googleMapsIniting = true;
|
||||
(window as any).initGoogleMaps = () => {
|
||||
this.emit('init-google-maps');
|
||||
};
|
||||
const head = document.getElementsByTagName('head')[0];
|
||||
const script = document.createElement('script');
|
||||
script.setAttribute('src', `https://maps.googleapis.com/maps/api/js?key=${googleMapsApiKey}&callback=initGoogleMaps`);
|
||||
script.setAttribute('async', 'true');
|
||||
script.setAttribute('defer', 'true');
|
||||
head.appendChild(script);
|
||||
}
|
||||
//#endregion
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
public log(...args) {
|
||||
if (!this.debug) return;
|
||||
console.log.apply(null, args);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public logInfo(...args) {
|
||||
if (!this.debug) return;
|
||||
console.info.apply(null, args);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public logWarn(...args) {
|
||||
if (!this.debug) return;
|
||||
console.warn.apply(null, args);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public logError(...args) {
|
||||
if (!this.debug) return;
|
||||
console.error.apply(null, args);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public signout() {
|
||||
this.store.dispatch('logout');
|
||||
location.href = '/';
|
||||
@ -225,27 +158,10 @@ export default class MiOS extends EventEmitter {
|
||||
* Initialize MiOS (boot)
|
||||
* @param callback A function that call when initialized
|
||||
*/
|
||||
@autobind
|
||||
public async init(callback) {
|
||||
this.store = initStore(this);
|
||||
|
||||
//#region Init stream managers
|
||||
this.streams.serverStatsStream = new ServerStatsStreamManager(this);
|
||||
this.streams.notesStatsStream = new NotesStatsStreamManager(this);
|
||||
this.streams.localTimelineStream = new LocalTimelineStreamManager(this, this.store.state.i);
|
||||
|
||||
this.once('signedin', () => {
|
||||
// Init home stream manager
|
||||
this.stream = new HomeStreamManager(this, this.store.state.i);
|
||||
|
||||
// Init other stream manager
|
||||
this.streams.hybridTimelineStream = new HybridTimelineStreamManager(this, this.store.state.i);
|
||||
this.streams.globalTimelineStream = new GlobalTimelineStreamManager(this, this.store.state.i);
|
||||
this.streams.driveStream = new DriveStreamManager(this, this.store.state.i);
|
||||
this.streams.messagingIndexStream = new MessagingIndexStreamManager(this, this.store.state.i);
|
||||
this.streams.reversiStream = new ReversiStreamManager(this, this.store.state.i);
|
||||
});
|
||||
//#endregion
|
||||
|
||||
// ユーザーをフェッチしてコールバックする
|
||||
const fetchme = (token, cb) => {
|
||||
let me = null;
|
||||
@ -296,6 +212,8 @@ export default class MiOS extends EventEmitter {
|
||||
const fetched = () => {
|
||||
this.emit('signedin');
|
||||
|
||||
this.stream = new Stream(this);
|
||||
|
||||
// Finish init
|
||||
callback();
|
||||
|
||||
@ -328,6 +246,8 @@ export default class MiOS extends EventEmitter {
|
||||
} else {
|
||||
// Finish init
|
||||
callback();
|
||||
|
||||
this.stream = new Stream(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -336,6 +256,7 @@ export default class MiOS extends EventEmitter {
|
||||
/**
|
||||
* Register service worker
|
||||
*/
|
||||
@autobind
|
||||
private registerSw() {
|
||||
// Check whether service worker and push manager supported
|
||||
const isSwSupported =
|
||||
@ -418,7 +339,8 @@ export default class MiOS extends EventEmitter {
|
||||
* @param endpoint エンドポイント名
|
||||
* @param data パラメータ
|
||||
*/
|
||||
public api(endpoint: string, data: { [x: string]: any } = {}): Promise<{ [x: string]: any }> {
|
||||
@autobind
|
||||
public api(endpoint: string, data: { [x: string]: any } = {}, forceFetch = false): Promise<{ [x: string]: any }> {
|
||||
if (++pending === 1) {
|
||||
spinner = document.createElement('div');
|
||||
spinner.setAttribute('id', 'wait');
|
||||
@ -430,13 +352,12 @@ export default class MiOS extends EventEmitter {
|
||||
};
|
||||
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
const viaStream = this.stream && this.stream.hasConnection && this.store.state.device.apiViaStream;
|
||||
const viaStream = this.stream && this.store.state.device.apiViaStream && !forceFetch;
|
||||
|
||||
if (viaStream) {
|
||||
const stream = this.stream.borrow();
|
||||
const id = Math.random().toString();
|
||||
|
||||
stream.once(`api-res:${id}`, res => {
|
||||
this.stream.once(`api:${id}`, res => {
|
||||
if (res == null || Object.keys(res).length == 0) {
|
||||
resolve(null);
|
||||
} else if (res.res) {
|
||||
@ -446,11 +367,10 @@ export default class MiOS extends EventEmitter {
|
||||
}
|
||||
});
|
||||
|
||||
stream.send({
|
||||
type: 'api',
|
||||
id,
|
||||
endpoint,
|
||||
data
|
||||
this.stream.send('api', {
|
||||
id: id,
|
||||
ep: endpoint,
|
||||
data: data
|
||||
});
|
||||
} else {
|
||||
// Append a credential
|
||||
@ -503,6 +423,7 @@ export default class MiOS extends EventEmitter {
|
||||
* Misskeyのメタ情報を取得します
|
||||
* @param force キャッシュを無視するか否か
|
||||
*/
|
||||
@autobind
|
||||
public getMeta(force = false) {
|
||||
return new Promise<{ [x: string]: any }>(async (res, rej) => {
|
||||
if (this.isMetaFetching) {
|
||||
@ -530,16 +451,6 @@ export default class MiOS extends EventEmitter {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public connections: Connection[] = [];
|
||||
|
||||
public registerStreamConnection(connection: Connection) {
|
||||
this.connections.push(connection);
|
||||
}
|
||||
|
||||
public unregisterStreamConnection(connection: Connection) {
|
||||
this.connections = erase(connection, this.connections);
|
||||
}
|
||||
}
|
||||
|
||||
class WindowSystem extends EventEmitter {
|
||||
|
@ -81,8 +81,7 @@ export default Vue.extend({
|
||||
hierarchyFolders: [],
|
||||
selectedFiles: [],
|
||||
info: null,
|
||||
connection: null,
|
||||
connectionId: null,
|
||||
connection: null
|
||||
|
||||
fetching: true,
|
||||
fetchingMoreFiles: false,
|
||||
@ -102,8 +101,7 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.connection = (this as any).os.streams.driveStream.getConnection();
|
||||
this.connectionId = (this as any).os.streams.driveStream.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('drive');
|
||||
|
||||
this.connection.on('file_created', this.onStreamDriveFileCreated);
|
||||
this.connection.on('file_updated', this.onStreamDriveFileUpdated);
|
||||
@ -124,12 +122,7 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.connection.off('file_created', this.onStreamDriveFileCreated);
|
||||
this.connection.off('file_updated', this.onStreamDriveFileUpdated);
|
||||
this.connection.off('file_deleted', this.onStreamDriveFileDeleted);
|
||||
this.connection.off('folder_created', this.onStreamDriveFolderCreated);
|
||||
this.connection.off('folder_updated', this.onStreamDriveFolderUpdated);
|
||||
(this as any).os.streams.driveStream.dispose(this.connectionId);
|
||||
this.connection.dispose();
|
||||
},
|
||||
methods: {
|
||||
onStreamDriveFileCreated(file) {
|
||||
|
@ -28,21 +28,17 @@ export default Vue.extend({
|
||||
return {
|
||||
u: this.user,
|
||||
wait: false,
|
||||
connection: null,
|
||||
connectionId: null
|
||||
connection: null
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.connection = (this as any).os.stream.getConnection();
|
||||
this.connectionId = (this as any).os.stream.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('main');
|
||||
|
||||
this.connection.on('follow', this.onFollow);
|
||||
this.connection.on('unfollow', this.onUnfollow);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.connection.off('follow', this.onFollow);
|
||||
this.connection.off('unfollow', this.onUnfollow);
|
||||
(this as any).os.stream.dispose(this.connectionId);
|
||||
this.connection.dispose();
|
||||
},
|
||||
methods: {
|
||||
|
||||
|
@ -92,12 +92,15 @@ import MkNoteMenu from '../../../common/views/components/note-menu.vue';
|
||||
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
|
||||
import XSub from './note.sub.vue';
|
||||
import { sum } from '../../../../../prelude/array';
|
||||
import noteSubscriber from '../../../common/scripts/note-subscriber';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XSub
|
||||
},
|
||||
|
||||
mixins: [noteSubscriber('note')],
|
||||
|
||||
props: {
|
||||
note: {
|
||||
type: Object,
|
||||
|
@ -69,19 +69,20 @@ import MkNoteMenu from '../../../common/views/components/note-menu.vue';
|
||||
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
|
||||
import XSub from './note.sub.vue';
|
||||
import { sum } from '../../../../../prelude/array';
|
||||
import noteSubscriber from '../../../common/scripts/note-subscriber';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XSub
|
||||
},
|
||||
|
||||
mixins: [noteSubscriber('note')],
|
||||
|
||||
props: ['note'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
showContent: false,
|
||||
connection: null,
|
||||
connectionId: null
|
||||
showContent: false
|
||||
};
|
||||
},
|
||||
|
||||
@ -115,86 +116,7 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection = (this as any).os.stream.getConnection();
|
||||
this.connectionId = (this as any).os.stream.use();
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.capture(true);
|
||||
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection.on('_connected_', this.onStreamConnected);
|
||||
}
|
||||
|
||||
// Draw map
|
||||
if (this.p.geo) {
|
||||
const shouldShowMap = this.$store.getters.isSignedIn ? this.$store.state.settings.showMaps : true;
|
||||
if (shouldShowMap) {
|
||||
(this as any).os.getGoogleMaps().then(maps => {
|
||||
const uluru = new maps.LatLng(this.p.geo.coordinates[1], this.p.geo.coordinates[0]);
|
||||
const map = new maps.Map(this.$refs.map, {
|
||||
center: uluru,
|
||||
zoom: 15
|
||||
});
|
||||
new maps.Marker({
|
||||
position: uluru,
|
||||
map: map
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.decapture(true);
|
||||
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection.off('_connected_', this.onStreamConnected);
|
||||
(this as any).os.stream.dispose(this.connectionId);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
capture(withHandler = false) {
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
const data = {
|
||||
type: 'capture',
|
||||
id: this.p.id
|
||||
} as any;
|
||||
if ((this.p.visibleUserIds || []).includes(this.$store.state.i.id) || (this.p.mentions || []).includes(this.$store.state.i.id)) {
|
||||
data.read = true;
|
||||
}
|
||||
this.connection.send(data);
|
||||
if (withHandler) this.connection.on('note-updated', this.onStreamNoteUpdated);
|
||||
}
|
||||
},
|
||||
|
||||
decapture(withHandler = false) {
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection.send({
|
||||
type: 'decapture',
|
||||
id: this.p.id
|
||||
});
|
||||
if (withHandler) this.connection.off('note-updated', this.onStreamNoteUpdated);
|
||||
}
|
||||
},
|
||||
|
||||
onStreamConnected() {
|
||||
this.capture();
|
||||
},
|
||||
|
||||
onStreamNoteUpdated(data) {
|
||||
const note = data.note;
|
||||
if (note.id == this.note.id) {
|
||||
this.$emit('update:note', note);
|
||||
} else if (note.id == this.note.renoteId) {
|
||||
this.note.renote = note;
|
||||
}
|
||||
},
|
||||
|
||||
reply() {
|
||||
(this as any).apis.post({
|
||||
reply: this.p
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
@ -30,10 +31,10 @@ export default Vue.extend({
|
||||
fetchingMoreNotifications: false,
|
||||
notifications: [],
|
||||
moreNotifications: false,
|
||||
connection: null,
|
||||
connectionId: null
|
||||
connection: null
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
_notifications(): any[] {
|
||||
return (this.notifications as any).map(notification => {
|
||||
@ -45,9 +46,9 @@ export default Vue.extend({
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.connection = (this as any).os.stream.getConnection();
|
||||
this.connectionId = (this as any).os.stream.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('main');
|
||||
|
||||
this.connection.on('notification', this.onNotification);
|
||||
|
||||
@ -66,10 +67,11 @@ export default Vue.extend({
|
||||
this.$emit('fetched');
|
||||
});
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.connection.off('notification', this.onNotification);
|
||||
(this as any).os.stream.dispose(this.connectionId);
|
||||
this.connection.dispose();
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchMoreNotifications() {
|
||||
this.fetchingMoreNotifications = true;
|
||||
@ -90,10 +92,11 @@ export default Vue.extend({
|
||||
this.fetchingMoreNotifications = false;
|
||||
});
|
||||
},
|
||||
|
||||
onNotification(notification) {
|
||||
// TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない
|
||||
this.connection.send({
|
||||
type: 'read_notification',
|
||||
type: 'readNotification',
|
||||
id: notification.id
|
||||
});
|
||||
|
||||
|
@ -24,44 +24,47 @@ import { env } from '../../../config';
|
||||
|
||||
export default Vue.extend({
|
||||
props: ['func'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
hasGameInvitation: false,
|
||||
connection: null,
|
||||
connectionId: null,
|
||||
env: env
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
hasUnreadNotification(): boolean {
|
||||
return this.$store.getters.isSignedIn && this.$store.state.i.hasUnreadNotification;
|
||||
},
|
||||
|
||||
hasUnreadMessagingMessage(): boolean {
|
||||
return this.$store.getters.isSignedIn && this.$store.state.i.hasUnreadMessagingMessage;
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$store.commit('setUiHeaderHeight', this.$refs.root.offsetHeight);
|
||||
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection = (this as any).os.stream.getConnection();
|
||||
this.connectionId = (this as any).os.stream.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('main');
|
||||
|
||||
this.connection.on('reversi_invited', this.onReversiInvited);
|
||||
this.connection.on('reversiInvited', this.onReversiInvited);
|
||||
this.connection.on('reversi_no_invites', this.onReversiNoInvites);
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection.off('reversi_invited', this.onReversiInvited);
|
||||
this.connection.off('reversi_no_invites', this.onReversiNoInvites);
|
||||
(this as any).os.stream.dispose(this.connectionId);
|
||||
this.connection.dispose();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onReversiInvited() {
|
||||
this.hasGameInvitation = true;
|
||||
},
|
||||
|
||||
onReversiNoInvites() {
|
||||
this.hasGameInvitation = false;
|
||||
}
|
||||
|
@ -57,7 +57,6 @@ export default Vue.extend({
|
||||
return {
|
||||
hasGameInvitation: false,
|
||||
connection: null,
|
||||
connectionId: null,
|
||||
aboutUrl: `/docs/${lang}/about`,
|
||||
announcements: []
|
||||
};
|
||||
@ -79,19 +78,16 @@ export default Vue.extend({
|
||||
});
|
||||
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection = (this as any).os.stream.getConnection();
|
||||
this.connectionId = (this as any).os.stream.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('main');
|
||||
|
||||
this.connection.on('reversi_invited', this.onReversiInvited);
|
||||
this.connection.on('reversiInvited', this.onReversiInvited);
|
||||
this.connection.on('reversi_no_invites', this.onReversiNoInvites);
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection.off('reversi_invited', this.onReversiInvited);
|
||||
this.connection.off('reversi_no_invites', this.onReversiNoInvites);
|
||||
(this as any).os.stream.dispose(this.connectionId);
|
||||
this.connection.dispose();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -23,40 +23,43 @@ export default Vue.extend({
|
||||
XHeader,
|
||||
XNav
|
||||
},
|
||||
|
||||
props: ['title'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
isDrawerOpening: false,
|
||||
connection: null,
|
||||
connectionId: null
|
||||
connection: null
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
'$store.state.uiHeaderHeight'() {
|
||||
this.$el.style.paddingTop = this.$store.state.uiHeaderHeight + 'px';
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$el.style.paddingTop = this.$store.state.uiHeaderHeight + 'px';
|
||||
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection = (this as any).os.stream.getConnection();
|
||||
this.connectionId = (this as any).os.stream.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('main');
|
||||
|
||||
this.connection.on('notification', this.onNotification);
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection.off('notification', this.onNotification);
|
||||
(this as any).os.stream.dispose(this.connectionId);
|
||||
this.connection.dispose();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onNotification(notification) {
|
||||
// TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない
|
||||
this.connection.send({
|
||||
type: 'read_notification',
|
||||
type: 'readNotification',
|
||||
id: notification.id
|
||||
});
|
||||
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { UserListStream } from '../../../common/scripts/streaming/user-list';
|
||||
|
||||
const fetchLimit = 10;
|
||||
|
||||
|
@ -13,7 +13,6 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { HashtagStream } from '../../../common/scripts/streaming/hashtag';
|
||||
|
||||
const fetchLimit = 10;
|
||||
|
||||
@ -35,7 +34,6 @@ export default Vue.extend({
|
||||
existMore: false,
|
||||
streamManager: null,
|
||||
connection: null,
|
||||
connectionId: null,
|
||||
unreadCount: 0,
|
||||
date: null,
|
||||
baseQuery: {
|
||||
@ -68,69 +66,33 @@ export default Vue.extend({
|
||||
this.query = {
|
||||
query: this.tagTl.query
|
||||
};
|
||||
this.connection = new HashtagStream((this as any).os, this.$store.state.i, this.tagTl.query);
|
||||
this.connection = (this as any).os.stream.connectToChannel('hashtag', { q: this.tagTl.query });
|
||||
this.connection.on('note', prepend);
|
||||
this.$once('beforeDestroy', () => {
|
||||
this.connection.off('note', prepend);
|
||||
this.connection.close();
|
||||
});
|
||||
} else if (this.src == 'home') {
|
||||
this.endpoint = 'notes/timeline';
|
||||
const onChangeFollowing = () => {
|
||||
this.fetch();
|
||||
};
|
||||
this.streamManager = (this as any).os.stream;
|
||||
this.connection = this.streamManager.getConnection();
|
||||
this.connectionId = this.streamManager.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('homeTimeline');
|
||||
this.connection.on('note', prepend);
|
||||
this.connection.on('follow', onChangeFollowing);
|
||||
this.connection.on('unfollow', onChangeFollowing);
|
||||
this.$once('beforeDestroy', () => {
|
||||
this.connection.off('note', prepend);
|
||||
this.connection.off('follow', onChangeFollowing);
|
||||
this.connection.off('unfollow', onChangeFollowing);
|
||||
this.streamManager.dispose(this.connectionId);
|
||||
});
|
||||
} else if (this.src == 'local') {
|
||||
this.endpoint = 'notes/local-timeline';
|
||||
this.streamManager = (this as any).os.streams.localTimelineStream;
|
||||
this.connection = this.streamManager.getConnection();
|
||||
this.connectionId = this.streamManager.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('localTimeline');
|
||||
this.connection.on('note', prepend);
|
||||
this.$once('beforeDestroy', () => {
|
||||
this.connection.off('note', prepend);
|
||||
this.streamManager.dispose(this.connectionId);
|
||||
});
|
||||
} else if (this.src == 'hybrid') {
|
||||
this.endpoint = 'notes/hybrid-timeline';
|
||||
this.streamManager = (this as any).os.streams.hybridTimelineStream;
|
||||
this.connection = this.streamManager.getConnection();
|
||||
this.connectionId = this.streamManager.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('hybridTimeline');
|
||||
this.connection.on('note', prepend);
|
||||
this.$once('beforeDestroy', () => {
|
||||
this.connection.off('note', prepend);
|
||||
this.streamManager.dispose(this.connectionId);
|
||||
});
|
||||
} else if (this.src == 'global') {
|
||||
this.endpoint = 'notes/global-timeline';
|
||||
this.streamManager = (this as any).os.streams.globalTimelineStream;
|
||||
this.connection = this.streamManager.getConnection();
|
||||
this.connectionId = this.streamManager.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('globalTimeline');
|
||||
this.connection.on('note', prepend);
|
||||
this.$once('beforeDestroy', () => {
|
||||
this.connection.off('note', prepend);
|
||||
this.streamManager.dispose(this.connectionId);
|
||||
});
|
||||
} else if (this.src == 'mentions') {
|
||||
this.endpoint = 'notes/mentions';
|
||||
this.streamManager = (this as any).os.stream;
|
||||
this.connection = this.streamManager.getConnection();
|
||||
this.connectionId = this.streamManager.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('main');
|
||||
this.connection.on('mention', prepend);
|
||||
this.$once('beforeDestroy', () => {
|
||||
this.connection.off('mention', prepend);
|
||||
this.streamManager.dispose(this.connectionId);
|
||||
});
|
||||
} else if (this.src == 'messages') {
|
||||
this.endpoint = 'notes/mentions';
|
||||
this.query = {
|
||||
@ -141,21 +103,15 @@ export default Vue.extend({
|
||||
prepend(note);
|
||||
}
|
||||
};
|
||||
this.streamManager = (this as any).os.stream;
|
||||
this.connection = this.streamManager.getConnection();
|
||||
this.connectionId = this.streamManager.use();
|
||||
this.connection = (this as any).os.stream.useSharedConnection('main');
|
||||
this.connection.on('mention', onNote);
|
||||
this.$once('beforeDestroy', () => {
|
||||
this.connection.off('mention', onNote);
|
||||
this.streamManager.dispose(this.connectionId);
|
||||
});
|
||||
}
|
||||
|
||||
this.fetch();
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.$emit('beforeDestroy');
|
||||
this.connection.dispose();
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@ -14,7 +14,8 @@
|
||||
"removeComments": false,
|
||||
"noLib": false,
|
||||
"strict": true,
|
||||
"strictNullChecks": false
|
||||
"strictNullChecks": false,
|
||||
"experimentalDecorators": true
|
||||
},
|
||||
"compileOnSave": false,
|
||||
"include": [
|
||||
|
Reference in New Issue
Block a user