Compare commits

...

8 Commits
5.3.0 ... 5.4.0

Author SHA1 Message Date
deee7361f0 5.4.0 2018-07-27 04:26:48 +09:00
bdcf09c618 Update doc 2018-07-27 04:25:38 +09:00
7b5d6dcd9b Update doc 2018-07-27 04:21:48 +09:00
0595d87759 Update doc 2018-07-27 04:10:16 +09:00
fab0a0d6e2 ログインしていないとリバーシを観戦できない問題を修正 2018-07-27 04:01:12 +09:00
3eb6b36866 Fix bug 2018-07-27 03:46:12 +09:00
50327158e2 wip doc 2018-07-27 03:43:23 +09:00
a99756ef85 ✌️ 2018-07-27 03:34:28 +09:00
7 changed files with 176 additions and 32 deletions

View File

@ -1,8 +1,8 @@
{ {
"name": "misskey", "name": "misskey",
"author": "syuilo <i@syuilo.com>", "author": "syuilo <i@syuilo.com>",
"version": "5.3.0", "version": "5.4.0",
"clientVersion": "1.0.7588", "clientVersion": "1.0.7596",
"codename": "nighthike", "codename": "nighthike",
"main": "./built/index.js", "main": "./built/index.js",
"private": true, "private": true,

View File

@ -105,7 +105,8 @@ export default Vue.extend({
} }
}, },
isMyTurn(): boolean { isMyTurn(): boolean {
if (this.turnUser == null) return null; if (!this.iAmPlayer) return false;
if (this.turnUser == null) return false;
return this.turnUser.id == this.$store.state.i.id; return this.turnUser.id == this.$store.state.i.id;
}, },
cellsStyle(): any { cellsStyle(): any {

View File

@ -67,7 +67,9 @@ export default Vue.extend({
components: { components: {
XGameroom XGameroom
}, },
props: ['initGame'], props: ['initGame'],
data() { data() {
return { return {
game: null, game: null,
@ -82,54 +84,63 @@ export default Vue.extend({
pingClock: null pingClock: null
}; };
}, },
watch: { watch: {
game(g) { game(g) {
this.$emit('gamed', g); this.$emit('gamed', g);
} }
}, },
created() { created() {
if (this.initGame) { if (this.initGame) {
this.game = this.initGame; this.game = this.initGame;
} }
}, },
mounted() { mounted() {
this.connection = (this as any).os.streams.reversiStream.getConnection(); if (this.$store.getters.isSignedIn) {
this.connectionId = (this as any).os.streams.reversiStream.use(); this.connection = (this as any).os.streams.reversiStream.getConnection();
this.connectionId = (this as any).os.streams.reversiStream.use();
this.connection.on('matched', this.onMatched); this.connection.on('matched', this.onMatched);
this.connection.on('invited', this.onInvited); this.connection.on('invited', this.onInvited);
(this as any).api('games/reversi/games', { (this as any).api('games/reversi/games', {
my: true my: true
}).then(games => { }).then(games => {
this.myGames = games; this.myGames = games;
}); });
(this as any).api('games/reversi/invitations').then(invitations => {
this.invitations = this.invitations.concat(invitations);
});
this.pingClock = setInterval(() => {
if (this.matching) {
this.connection.send({
type: 'ping',
id: this.matching.id
});
}
}, 3000);
}
(this as any).api('games/reversi/games').then(games => { (this as any).api('games/reversi/games').then(games => {
this.games = games; this.games = games;
this.gamesFetching = false; this.gamesFetching = false;
}); });
(this as any).api('games/reversi/invitations').then(invitations => {
this.invitations = this.invitations.concat(invitations);
});
this.pingClock = setInterval(() => {
if (this.matching) {
this.connection.send({
type: 'ping',
id: this.matching.id
});
}
}, 3000);
}, },
beforeDestroy() { beforeDestroy() {
this.connection.off('matched', this.onMatched); if (this.connection) {
this.connection.off('invited', this.onInvited); this.connection.off('matched', this.onMatched);
(this as any).os.streams.reversiStream.dispose(this.connectionId); this.connection.off('invited', this.onInvited);
(this as any).os.streams.reversiStream.dispose(this.connectionId);
clearInterval(this.pingClock); clearInterval(this.pingClock);
}
}, },
methods: { methods: {
go(game) { go(game) {
(this as any).api('games/reversi/games/show', { (this as any).api('games/reversi/games/show', {
@ -139,6 +150,7 @@ export default Vue.extend({
this.game = game; this.game = game;
}); });
}, },
match() { match() {
(this as any).apis.input({ (this as any).apis.input({
title: 'ユーザー名を入力してください' title: 'ユーザー名を入力してください'
@ -158,10 +170,12 @@ export default Vue.extend({
}); });
}); });
}, },
cancel() { cancel() {
this.matching = null; this.matching = null;
(this as any).api('games/reversi/match/cancel'); (this as any).api('games/reversi/match/cancel');
}, },
accept(invitation) { accept(invitation) {
(this as any).api('games/reversi/match', { (this as any).api('games/reversi/match', {
userId: invitation.parent.id userId: invitation.parent.id
@ -172,10 +186,12 @@ export default Vue.extend({
} }
}); });
}, },
onMatched(game) { onMatched(game) {
this.matching = null; this.matching = null;
this.game = game; this.game = game;
}, },
onInvited(invite) { onInvited(invite) {
this.invitations.unshift(invite); this.invitations.unshift(invite);
} }

View File

@ -27,7 +27,7 @@ const nativeDbConn = async (): Promise<mongodb.Db> => {
if (mdb) return mdb; if (mdb) return mdb;
const db = await ((): Promise<mongodb.Db> => new Promise((resolve, reject) => { const db = await ((): Promise<mongodb.Db> => new Promise((resolve, reject) => {
mongodb.MongoClient.connect(uri, (e: Error, client: any) => { mongodb.MongoClient.connect(uri, { useNewUrlParser: true }, (e: Error, client: any) => {
if (e) return reject(e); if (e) return reject(e);
resolve(client.db(config.mongodb.db)); resolve(client.db(config.mongodb.db));
}); });

127
src/docs/stream.ja.md Normal file
View File

@ -0,0 +1,127 @@
# ストリーミングAPI
ストリーミングAPIを使うと、リアルタイムで様々な情報(例えばタイムラインに新しい投稿が流れてきた、メッセージが届いた、フォローされた、など)を受け取ったり、HTTPリクエストを発生させることなくAPIにアクセスしたりすることができます。
ストリーミングAPIは複数の種類がありますが、ここではメインとなる「ホームストリーム」について説明します。
## ストリームに接続する
以下のURLに**websocket**接続します。
```
%URL%
```
接続する際は、`i`というパラメータ名で認証情報を含めます。例:
```
%URL%/?i=xxxxxxxxxxxxxxx
```
## ストリームを経由してAPIリクエストする
ストリームを経由してAPIリクエストすると、HTTPリクエストを発生させずにAPIを利用できます。そのため、コードを簡潔にできたり、パフォーマンスの向上を見込めるかもしれません。
ストリームを経由してAPIリクエストするには、次のようなメッセージをストリームに送信します:
```json
{
type: 'api',
id: 'xxxxxxxxxxxxxxxx',
endpoint: 'notes/create',
data: {
text: 'yee haw!'
}
}
```
`id`には、APIのレスポンスを識別するための、APIリクエストごとの一意なIDを設定する必要があります。UUIDや、簡単な乱数のようなもので構いません。
`endpoint`には、あなたがリクエストしたいAPIのエンドポイントを指定します。
`data`には、エンドポイントのパラメータを含めます。
<div class="ui info">
<p><i class="fas fa-info-circle"></i> APIのエンドポイントやパラメータについてはAPIリファレンスをご確認ください。</p>
</div>
### レスポンスの受信
APIへリクエストすると、レスポンスがストリームから次のような形式で流れてきます。
```json
{
type: 'api-res:xxxxxxxxxxxxxxxx',
body: {
...
}
}
```
`xxxxxxxxxxxxxxxx`の部分には、リクエストの際に設定された`id`が含まれています。これにより、どのリクエストに対するレスポンスなのか判別することができます。
`body`には、レスポンスが含まれています。
## 投稿のキャプチャ
Misskeyは投稿のキャプチャと呼ばれる仕組みを提供しています。これは、指定した投稿のイベントをストリームで受け取る機能です。
例えばタイムラインを取得してユーザーに表示したとします。ここで誰かがそのタイムラインに含まれるどれかの投稿に対してリアクションしたとします。
しかし、クライアントからするとある投稿にリアクションが付いたことなどは知る由がないため、リアルタイムでリアクションをタイムライン上の投稿に反映して表示するといったことができません。
この問題を解決するために、Misskeyは投稿のキャプチャ機構を用意しています。投稿をキャプチャすると、その投稿に関するイベントを受け取ることができるため、リアルタイムでリアクションを反映させたりすることが可能になります。
### 投稿をキャプチャする
投稿をキャプチャするには、ストリームに次のようなメッセージを送信します:
```json
{
type: 'capture',
id: 'xxxxxxxxxxxxxxxx'
}
```
`id`には、キャプチャしたい投稿の`id`を設定します。
このメッセージを送信すると、Misskeyにキャプチャを要請したことになり、以後、その投稿に関するイベントが流れてくるようになります。
例えば投稿にリアクションが付いたとすると、次のようなメッセージが流れてきます:
```json
{
type: 'note-updated',
body: {
note: {
...
}
}
}
```
`body`内の`note`には、その投稿の最新の情報が含まれています。
### 投稿のキャプチャを解除する
その投稿がもう画面に表示されなくなったりして、その投稿に関するイベントをもう受け取る必要がなくなったときは、キャプチャの解除を申請してください。
次のメッセージを送信します:
```json
{
type: 'decapture',
id: 'xxxxxxxxxxxxxxxx'
}
```
`id`には、キャプチャを解除したい投稿の`id`を設定します。
このメッセージを送信すると、以後、その投稿に関するイベントは流れてこないようになります。
## 流れてくるイベント一覧
流れてくるすべてのメッセージはJSON形式で、必ず`type`というプロパティが含まれています。これにより、メッセージの種類(イベント)を判別することができます。
### `note`
タイムラインに新しい投稿が流れてきたときに発生するイベントです。
`body`プロパティの中に、投稿情報が含まれています。

View File

@ -3,7 +3,6 @@ import ReversiGame, { pack } from '../../../../../models/games/reversi/game';
import { ILocalUser } from '../../../../../models/user'; import { ILocalUser } from '../../../../../models/user';
export const meta = { export const meta = {
requireCredential: true
}; };
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {

View File

@ -22,7 +22,8 @@ export default async function(user: IUser, note: INote) {
text: null, text: null,
tags: [], tags: [],
mediaIds: [], mediaIds: [],
poll: null poll: null,
geo: null
} }
}); });