From dab6fd56628426aa1deb203e7f13b65504061017 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Tue, 17 Oct 2017 00:15:25 +0000 Subject: [PATCH 001/111] chore(package): update @types/gm to version 1.17.33 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 688664fa6..0c6d3ff3d 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@types/elasticsearch": "5.0.14", "@types/event-stream": "3.3.32", "@types/express": "4.0.37", - "@types/gm": "1.17.32", + "@types/gm": "1.17.33", "@types/gulp": "4.0.3", "@types/gulp-htmlmin": "1.3.30", "@types/gulp-mocha": "0.0.30", From f10f11292d26a83bf94556a2633604403724daba Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Tue, 17 Oct 2017 07:08:45 +0000 Subject: [PATCH 002/111] fix(package): update file-type to version 7.2.0 Closes #821 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 688664fa6..a7f52d064 100644 --- a/package.json +++ b/package.json @@ -114,7 +114,7 @@ "elasticsearch": "13.3.1", "escape-regexp": "0.0.1", "express": "4.15.4", - "file-type": "6.2.0", + "file-type": "7.2.0", "fuckadblock": "3.2.1", "gm": "1.23.0", "inquirer": "3.3.0", From 7398f31b58571ba1509ef75fddbc27043ccd8351 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Tue, 17 Oct 2017 18:37:46 +0000 Subject: [PATCH 003/111] chore(package): update @types/uuid to version 3.4.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 688664fa6..daf3e3dc0 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "@types/rimraf": "2.0.0", "@types/riot": "3.6.0", "@types/serve-favicon": "2.2.28", - "@types/uuid": "3.4.2", + "@types/uuid": "3.4.3", "@types/webpack": "3.0.13", "@types/webpack-stream": "3.2.7", "@types/websocket": "0.0.34", From f45b680d5012429a18229e07ce38362b496e6c33 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Tue, 24 Oct 2017 02:40:07 +0000 Subject: [PATCH 004/111] chore(package): update uglifyjs-webpack-plugin to version 1.0.1 Closes #841 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4ddb3cb45..0b990ea40 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "tslint": "5.7.0", "uglify-es": "3.0.27", "uglify-js": "git+https://github.com/mishoo/UglifyJS2.git#harmony", - "uglifyjs-webpack-plugin": "1.0.0-beta.2", + "uglifyjs-webpack-plugin": "1.0.1", "webpack": "3.8.1" }, "dependencies": { From 6ec1afb2c331454200bc7ac1aa3501576ee9b7fc Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Tue, 24 Oct 2017 04:31:04 +0000 Subject: [PATCH 005/111] fix(package): update chalk to version 2.3.0 Closes #833 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4ddb3cb45..2284bb01b 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "bcryptjs": "2.4.3", "body-parser": "1.18.2", "cafy": "3.0.0", - "chalk": "2.1.0", + "chalk": "2.3.0", "compression": "1.7.1", "cors": "2.8.4", "cropperjs": "1.1.3", From 9eecd9f3d18c2e2f5a8bd4dda78fcef7e132655c Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Wed, 25 Oct 2017 01:30:35 +0000 Subject: [PATCH 006/111] chore(package): update @types/mongodb to version 2.2.15 Closes #836 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4ddb3cb45..5105ea727 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "@types/is-url": "1.2.28", "@types/js-yaml": "3.9.0", "@types/mocha": "2.2.43", - "@types/mongodb": "2.2.13", + "@types/mongodb": "2.2.15", "@types/monk": "1.0.6", "@types/morgan": "1.7.33", "@types/ms": "0.7.30", From d68f30ad938ded1a35d6b7f14d2ee635dd9b591c Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Wed, 25 Oct 2017 02:13:31 +0000 Subject: [PATCH 007/111] chore(package): update @types/node to version 8.0.47 Closes #822 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4ddb3cb45..0f63f1f0a 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "@types/morgan": "1.7.33", "@types/ms": "0.7.30", "@types/multer": "1.3.2", - "@types/node": "8.0.33", + "@types/node": "8.0.47", "@types/ratelimiter": "2.1.28", "@types/redis": "2.6.0", "@types/request": "2.0.4", From 35f5f96031c7dd510192b3e42d88b153c92e7819 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 25 Oct 2017 17:56:28 +0900 Subject: [PATCH 008/111] [Client] Implement troubleshooting --- locales/en.yml | 19 ++++ locales/ja.yml | 19 ++++ src/web/app/common/tags/error.tag | 150 ++++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+) diff --git a/locales/en.yml b/locales/en.yml index d4dfbf76b..fbe8a22fb 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -66,6 +66,25 @@ common: title: "Unable to connect to the server" description: "There is a problem with Internet connection, or the server may be down or maintaining. Please {try again} later." thanks: "Thank you for using Misskey." + troubleshoot: "Troubleshoot" + + troubleshooter: + title: "TroubleShooting" + network: "Network connection" + checking-network: "Checking network connection" + internet: "Internet connection" + checking-internet: "Checking internet connection" + server: "Server connection" + checking-server: "Checking server connection" + finding: "Finding a problem" + no-network: "No network connection" + no-network-desc: "Please make sure you are connected to the Network." + no-internet: "No internet connection" + no-internet-desc: "Please make sure you are connected to the Internet." + no-server: "Unable to connect to the server" + no-server-desc: "The network connection of your PC is normal, but you could not connect to Misskey's server. There is a possibility that the server is down or maintaining, please try to access it again after a while." + success: "Successfully connect to the Misskey's server" + success-desc: "It seems to be able to connect normally. Please reload the page." mk-forkit: open-github-link: "View source on Github" diff --git a/locales/ja.yml b/locales/ja.yml index 9a8490dec..023bac4d4 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -66,6 +66,25 @@ common: title: "サーバーに接続できません" description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。" thanks: "いつもMisskeyをご利用いただきありがとうございます。" + troubleshoot: "トラブルシュート" + + troubleshooter: + title: "トラブルシューティング" + network: "ネットワーク接続" + checking-network: "ネットワーク接続を確認中" + internet: "インターネット接続" + checking-internet: "インターネット接続を確認中" + server: "サーバー接続" + checking-server: "サーバー接続を確認中" + finding: "問題を調べています" + no-network: "ネットワークに接続されていません" + no-network-desc: "お使いのPCのネットワーク接続が正常か確認してください。" + no-internet: "インターネットに接続されていません" + no-internet-desc: "ネットワークには接続されていますが、インターネットには接続されていないようです。お使いのPCのインターネット接続が正常か確認してください。" + no-server: "Misskeyのサーバーに接続できません" + no-server-desc: "お使いのPCのネットワーク接続は正常ですが、Misskeyのサーバーには接続できませんでした。サーバーがダウンまたはメンテナンスしている可能性があるので、しばらくしてから再度御アクセスください。" + success: "Misskeyのサーバーに接続できました" + success-desc: "正常に接続できるようです。ページを再度読み込みしてください。" mk-forkit: open-github-link: "View source on Github" diff --git a/src/web/app/common/tags/error.tag b/src/web/app/common/tags/error.tag index 7a2976541..62f4563e5 100644 --- a/src/web/app/common/tags/error.tag +++ b/src/web/app/common/tags/error.tag @@ -8,6 +8,8 @@ }{ '%i18n:common.tags.mk-error.description%'.substr('%i18n:common.tags.mk-error.description%'.indexOf('}') + 1) }

+ +

%i18n:common.tags.mk-error.thanks%

+ + +

%i18n:common.tags.mk-error.troubleshooter.title%

+
+

{ network == null ? '%i18n:common.tags.mk-error.troubleshooter.checking-network%' : '%i18n:common.tags.mk-error.troubleshooter.network%' }

+

{ internet == null ? '%i18n:common.tags.mk-error.troubleshooter.checking-internet%' : '%i18n:common.tags.mk-error.troubleshooter.internet%' }

+

{ server == null ? '%i18n:common.tags.mk-error.troubleshooter.checking-server%' : '%i18n:common.tags.mk-error.troubleshooter.server%' }

+
+

%i18n:common.tags.mk-error.troubleshooter.finding%

+

%i18n:common.tags.mk-error.troubleshooter.no-network%
%i18n:common.tags.mk-error.troubleshooter.no-network-desc%

+

%i18n:common.tags.mk-error.troubleshooter.no-internet%
%i18n:common.tags.mk-error.troubleshooter.no-internet-desc%

+

%i18n:common.tags.mk-error.troubleshooter.no-server%
%i18n:common.tags.mk-error.troubleshooter.no-server-desc%

+

%i18n:common.tags.mk-error.troubleshooter.success%
%i18n:common.tags.mk-error.troubleshooter.success-desc%

+ + + +
From 569e468178e3496d23ece8edfbf8255595963eb6 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 25 Oct 2017 17:57:30 +0900 Subject: [PATCH 009/111] v2742 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba307ece9..675ffe62b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ChangeLog (Release Notes) ========================= 主に notable な changes を書いていきます +2742 (2017/10/25) +----------------- +* トラブルシューティングを実装するなど + 2735 (2017/10/22) ----------------- * モバイル版からでもクライアントバージョンを確認できるように diff --git a/package.json b/package.json index 4ddb3cb45..1f604b9ce 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "misskey", "author": "syuilo ", - "version": "0.0.2735", + "version": "0.0.2742", "license": "MIT", "description": "A miniblog-based SNS", "bugs": "https://github.com/syuilo/misskey/issues", From 381600d24eddf20673f4961776fa1213e226a784 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 25 Oct 2017 20:27:24 +0900 Subject: [PATCH 010/111] =?UTF-8?q?=E3=83=A1=E3=82=B8=E3=83=A3=E3=83=BC?= =?UTF-8?q?=E3=83=90=E3=83=BC=E3=82=B8=E3=83=A7=E3=83=B3=E3=82=92=E6=98=8E?= =?UTF-8?q?=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/web/app/desktop/tags/home-widgets/version.tag | 2 +- src/web/app/mobile/tags/page/settings.tag | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/web/app/desktop/tags/home-widgets/version.tag b/src/web/app/desktop/tags/home-widgets/version.tag index fa92afc49..ea5307061 100644 --- a/src/web/app/desktop/tags/home-widgets/version.tag +++ b/src/web/app/desktop/tags/home-widgets/version.tag @@ -1,5 +1,5 @@ -

ver { version }

+

ver { version } (葵 aoi)

+ + diff --git a/src/web/app/desktop/tags/ui.tag b/src/web/app/desktop/tags/ui.tag index e0d7393b0..452a72c00 100644 --- a/src/web/app/desktop/tags/ui.tag +++ b/src/web/app/desktop/tags/ui.tag @@ -319,18 +319,26 @@ -
    -
  • - - -

    %i18n:desktop.tags.mk-ui-header-nav.home%

    -
    -
  • -
  • - - -

    %i18n:desktop.tags.mk-ui-header-nav.messaging%

    - +
      + +
    • + + +

      %i18n:desktop.tags.mk-ui-header-nav.home%

      +
      +
    • +
    • + + +

      %i18n:desktop.tags.mk-ui-header-nav.messaging%

      + +
      +
    • + +
    • + + +

      %i18n:desktop.tags.mk-ui-header-nav.bbs%

    • From 98bce735a088ee6ec3d367cbd9454febfeca5e03 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 30 Oct 2017 22:12:10 +0900 Subject: [PATCH 022/111] =?UTF-8?q?=E6=9C=AA=E8=AA=AD=E3=81=AE=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E3=81=8C=E3=81=82=E3=82=8B=E5=A0=B4=E5=90=88=E3=82=A2?= =?UTF-8?q?=E3=82=A4=E3=82=B3=E3=83=B3=E3=82=92=E8=A1=A8=E7=A4=BA=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 + locales/en.yml | 1 + locales/ja.yml | 1 + src/api/common/read-notification.ts | 52 +++ src/api/endpoints.ts | 10 +- src/api/endpoints/i/notifications.ts | 14 +- .../notifications/get_unread_count.ts | 23 ++ .../endpoints/notifications/mark_as_read.ts | 47 --- .../notifications/mark_as_read_all.ts | 32 ++ src/api/models/notification.ts | 5 + src/api/stream/home.ts | 6 + src/web/app/desktop/tags/notifications.tag | 6 + src/web/app/mobile/tags/index.js | 2 - src/web/app/mobile/tags/notifications.tag | 6 + .../app/mobile/tags/page/notifications.tag | 14 + src/web/app/mobile/tags/ui-header.tag | 156 -------- src/web/app/mobile/tags/ui-nav.tag | 170 -------- src/web/app/mobile/tags/ui.tag | 368 ++++++++++++++++++ 18 files changed, 525 insertions(+), 392 deletions(-) create mode 100644 src/api/common/read-notification.ts create mode 100644 src/api/endpoints/notifications/get_unread_count.ts delete mode 100644 src/api/endpoints/notifications/mark_as_read.ts create mode 100644 src/api/endpoints/notifications/mark_as_read_all.ts delete mode 100644 src/web/app/mobile/tags/ui-header.tag delete mode 100644 src/web/app/mobile/tags/ui-nav.tag diff --git a/CHANGELOG.md b/CHANGELOG.md index ca41d016c..bf5c1fcb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ChangeLog (Release Notes) ========================= 主に notable な changes を書いていきます +unreleased +---------- +* New: 未読の通知がある場合アイコンを表示するように + 2747 (2017/10/25) ----------------- * Fix: 非ログイン状態ですべてのページが致命的な問題を発生させる (#89) diff --git a/locales/en.yml b/locales/en.yml index 03d5306d3..020813ddb 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -389,6 +389,7 @@ mobile: mk-notifications-page: notifications: "Notifications" + read-all: "Are you sure you want to mark as read all your notifications?" mk-post-page: title: "Post" diff --git a/locales/ja.yml b/locales/ja.yml index b640f0f24..1b3058fe0 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -389,6 +389,7 @@ mobile: mk-notifications-page: notifications: "通知" + read-all: "すべての通知を既読にしますか?" mk-post-page: title: "投稿" diff --git a/src/api/common/read-notification.ts b/src/api/common/read-notification.ts new file mode 100644 index 000000000..3009cc5d0 --- /dev/null +++ b/src/api/common/read-notification.ts @@ -0,0 +1,52 @@ +import * as mongo from 'mongodb'; +import { default as Notification, INotification } from '../models/notification'; +import publishUserStream from '../event'; + +/** + * Mark as read notification(s) + */ +export default ( + user: string | mongo.ObjectID, + message: string | string[] | INotification | INotification[] | mongo.ObjectID | mongo.ObjectID[] +) => new Promise(async (resolve, reject) => { + + const userId = mongo.ObjectID.prototype.isPrototypeOf(user) + ? user + : new mongo.ObjectID(user); + + const ids: mongo.ObjectID[] = Array.isArray(message) + ? mongo.ObjectID.prototype.isPrototypeOf(message[0]) + ? (message as mongo.ObjectID[]) + : typeof message[0] === 'string' + ? (message as string[]).map(m => new mongo.ObjectID(m)) + : (message as INotification[]).map(m => m._id) + : mongo.ObjectID.prototype.isPrototypeOf(message) + ? [(message as mongo.ObjectID)] + : typeof message === 'string' + ? [new mongo.ObjectID(message)] + : [(message as INotification)._id]; + + // Update documents + await Notification.update({ + _id: { $in: ids }, + is_read: false + }, { + $set: { + is_read: true + } + }, { + multi: true + }); + + // Calc count of my unread notifications + const count = await Notification + .count({ + notifiee_id: userId, + is_read: false + }); + + if (count == 0) { + // 全ての(いままで未読だった)通知を(これで)読みましたよというイベントを発行 + publishUserStream(userId, 'read_all_notifications'); + } +}); diff --git a/src/api/endpoints.ts b/src/api/endpoints.ts index f05762340..29a97bcb8 100644 --- a/src/api/endpoints.ts +++ b/src/api/endpoints.ts @@ -195,6 +195,11 @@ const endpoints: Endpoint[] = [ withCredential: true, kind: 'notification-read' }, + { + name: 'notifications/get_unread_count', + withCredential: true, + kind: 'notification-read' + }, { name: 'notifications/delete', withCredential: true, @@ -205,11 +210,6 @@ const endpoints: Endpoint[] = [ withCredential: true, kind: 'notification-write' }, - { - name: 'notifications/mark_as_read', - withCredential: true, - kind: 'notification-write' - }, { name: 'notifications/mark_as_read_all', withCredential: true, diff --git a/src/api/endpoints/i/notifications.ts b/src/api/endpoints/i/notifications.ts index 5575fb741..607e0768a 100644 --- a/src/api/endpoints/i/notifications.ts +++ b/src/api/endpoints/i/notifications.ts @@ -5,6 +5,7 @@ import $ from 'cafy'; import Notification from '../../models/notification'; import serialize from '../../serializers/notification'; import getFriends from '../../common/get-friends'; +import read from '../../common/read-notification'; /** * Get notifications @@ -91,17 +92,6 @@ module.exports = (params, user) => new Promise(async (res, rej) => { // Mark as read all if (notifications.length > 0 && markAsRead) { - const ids = notifications - .filter(x => x.is_read == false) - .map(x => x._id); - - // Update documents - await Notification.update({ - _id: { $in: ids } - }, { - $set: { is_read: true } - }, { - multi: true - }); + read(user._id, notifications); } }); diff --git a/src/api/endpoints/notifications/get_unread_count.ts b/src/api/endpoints/notifications/get_unread_count.ts new file mode 100644 index 000000000..9514e7871 --- /dev/null +++ b/src/api/endpoints/notifications/get_unread_count.ts @@ -0,0 +1,23 @@ +/** + * Module dependencies + */ +import Notification from '../../models/notification'; + +/** + * Get count of unread notifications + * + * @param {any} params + * @param {any} user + * @return {Promise} + */ +module.exports = (params, user) => new Promise(async (res, rej) => { + const count = await Notification + .count({ + notifiee_id: user._id, + is_read: false + }); + + res({ + count: count + }); +}); diff --git a/src/api/endpoints/notifications/mark_as_read.ts b/src/api/endpoints/notifications/mark_as_read.ts deleted file mode 100644 index 5cce33e85..000000000 --- a/src/api/endpoints/notifications/mark_as_read.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Module dependencies - */ -import $ from 'cafy'; -import Notification from '../../models/notification'; -import serialize from '../../serializers/notification'; -import event from '../../event'; - -/** - * Mark as read a notification - * - * @param {any} params - * @param {any} user - * @return {Promise} - */ -module.exports = (params, user) => new Promise(async (res, rej) => { - const [notificationId, notificationIdErr] = $(params.notification_id).id().$; - if (notificationIdErr) return rej('invalid notification_id param'); - - // Get notification - const notification = await Notification - .findOne({ - _id: notificationId, - i: user._id - }); - - if (notification === null) { - return rej('notification-not-found'); - } - - // Update - notification.is_read = true; - Notification.update({ _id: notification._id }, { - $set: { - is_read: true - } - }); - - // Response - res(); - - // Serialize - const notificationObj = await serialize(notification); - - // Publish read_notification event - event(user._id, 'read_notification', notificationObj); -}); diff --git a/src/api/endpoints/notifications/mark_as_read_all.ts b/src/api/endpoints/notifications/mark_as_read_all.ts new file mode 100644 index 000000000..3550e344c --- /dev/null +++ b/src/api/endpoints/notifications/mark_as_read_all.ts @@ -0,0 +1,32 @@ +/** + * Module dependencies + */ +import Notification from '../../models/notification'; +import event from '../../event'; + +/** + * Mark as read all notifications + * + * @param {any} params + * @param {any} user + * @return {Promise} + */ +module.exports = (params, user) => new Promise(async (res, rej) => { + // Update documents + await Notification.update({ + notifiee_id: user._id, + is_read: false + }, { + $set: { + is_read: true + } + }, { + multi: true + }); + + // Response + res(); + + // 全ての通知を読みましたよというイベントを発行 + event(user._id, 'read_all_notifications'); +}); diff --git a/src/api/models/notification.ts b/src/api/models/notification.ts index 1c1f429a0..1065e8baa 100644 --- a/src/api/models/notification.ts +++ b/src/api/models/notification.ts @@ -1,3 +1,8 @@ +import * as mongo from 'mongodb'; import db from '../../db/mongodb'; export default db.get('notifications') as any; // fuck type definition + +export interface INotification { + _id: mongo.ObjectID; +} diff --git a/src/api/stream/home.ts b/src/api/stream/home.ts index d5fe01c26..7c8f3bfec 100644 --- a/src/api/stream/home.ts +++ b/src/api/stream/home.ts @@ -4,6 +4,7 @@ import * as debug from 'debug'; import User from '../models/user'; import serializePost from '../serializers/post'; +import readNotification from '../common/read-notification'; const log = debug('misskey'); @@ -45,6 +46,11 @@ export default function homeStream(request: websocket.request, connection: webso }); break; + case 'read_notification': + if (!msg.id) return; + readNotification(user._id, msg.id); + break; + case 'capture': if (!msg.id) return; const postId = msg.id; diff --git a/src/web/app/desktop/tags/notifications.tag b/src/web/app/desktop/tags/notifications.tag index 1046358ce..a4f66105a 100644 --- a/src/web/app/desktop/tags/notifications.tag +++ b/src/web/app/desktop/tags/notifications.tag @@ -252,6 +252,12 @@ }); this.onNotification = notification => { + // TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない + this.stream.send({ + type: 'read_notification', + id: notification.id + }); + this.notifications.unshift(notification); this.update(); }; diff --git a/src/web/app/mobile/tags/index.js b/src/web/app/mobile/tags/index.js index c5aafd20b..a79f4f7e7 100644 --- a/src/web/app/mobile/tags/index.js +++ b/src/web/app/mobile/tags/index.js @@ -1,6 +1,4 @@ require('./ui.tag'); -require('./ui-header.tag'); -require('./ui-nav.tag'); require('./page/entrance.tag'); require('./page/entrance/signin.tag'); require('./page/entrance/signup.tag'); diff --git a/src/web/app/mobile/tags/notifications.tag b/src/web/app/mobile/tags/notifications.tag index 7370aa84d..2e9599031 100644 --- a/src/web/app/mobile/tags/notifications.tag +++ b/src/web/app/mobile/tags/notifications.tag @@ -123,6 +123,12 @@ }); this.onNotification = notification => { + // TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない + this.stream.send({ + type: 'read_notification', + id: notification.id + }); + this.notifications.unshift(notification); this.update(); }; diff --git a/src/web/app/mobile/tags/page/notifications.tag b/src/web/app/mobile/tags/page/notifications.tag index 06a5be039..743de0439 100644 --- a/src/web/app/mobile/tags/page/notifications.tag +++ b/src/web/app/mobile/tags/page/notifications.tag @@ -10,16 +10,30 @@ import ui from '../../scripts/ui-event'; import Progress from '../../../common/scripts/loading'; + this.mixin('api'); + this.on('mount', () => { document.title = 'Misskey | %i18n:mobile.tags.mk-notifications-page.notifications%'; ui.trigger('title', '%i18n:mobile.tags.mk-notifications-page.notifications%'); document.documentElement.style.background = '#313a42'; + ui.trigger('func', () => { + this.readAll(); + }, 'check'); + Progress.start(); this.refs.ui.refs.notifications.on('fetched', () => { Progress.done(); }); }); + + this.readAll = () => { + const ok = window.confirm('%i18n:mobile.tags.mk-notifications-page.read-all%'); + + if (!ok) return; + + this.api('notifications/mark_as_read_all'); + }; diff --git a/src/web/app/mobile/tags/ui-header.tag b/src/web/app/mobile/tags/ui-header.tag deleted file mode 100644 index 10b44b215..000000000 --- a/src/web/app/mobile/tags/ui-header.tag +++ /dev/null @@ -1,156 +0,0 @@ - - -
      -
      -
      - - -

      Misskey

      - -
      -
      - - -
      diff --git a/src/web/app/mobile/tags/ui-nav.tag b/src/web/app/mobile/tags/ui-nav.tag deleted file mode 100644 index 34235ba4f..000000000 --- a/src/web/app/mobile/tags/ui-nav.tag +++ /dev/null @@ -1,170 +0,0 @@ - -
      - - - -
      diff --git a/src/web/app/mobile/tags/ui.tag b/src/web/app/mobile/tags/ui.tag index 9d9cd4d74..fb8cbcdbd 100644 --- a/src/web/app/mobile/tags/ui.tag +++ b/src/web/app/mobile/tags/ui.tag @@ -30,9 +30,377 @@ }; this.onStreamNotification = notification => { + // TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない + this.stream.send({ + type: 'read_notification', + id: notification.id + }); + riot.mount(document.body.appendChild(document.createElement('mk-notify')), { notification: notification }); }; + + + +
      +
      +
      + + +

      Misskey

      + +
      +
      + + +
      + + +
      + + + +
      From 13b1dd0fa8e6cc80233429ec4fe6fec76fda4ba4 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 30 Oct 2017 22:12:52 +0900 Subject: [PATCH 023/111] v2752 --- CHANGELOG.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf5c1fcb2..2f75462e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,8 @@ ChangeLog (Release Notes) ========================= 主に notable な changes を書いていきます -unreleased ----------- +2752 (2017/10/30) +----------------- * New: 未読の通知がある場合アイコンを表示するように 2747 (2017/10/25) diff --git a/package.json b/package.json index 43a015961..7a81bed7a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "misskey", "author": "syuilo ", - "version": "0.0.2747", + "version": "0.0.2752", "license": "MIT", "description": "A miniblog-based SNS", "bugs": "https://github.com/syuilo/misskey/issues", From f085a762243aba8fd845c7d960e469b8bae0ce52 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 31 Oct 2017 21:42:11 +0900 Subject: [PATCH 024/111] RENAME: bbs -> channel --- locales/en.yml | 7 ++- locales/ja.yml | 7 ++- src/api/endpoints/bbs/threads/create.ts | 12 ++--- src/api/models/{bbs-thread.ts => channel.ts} | 4 +- src/api/serializers/bbs-thread.ts | 44 ------------------- src/api/serializers/channel.ts | 44 +++++++++++++++++++ src/web/app/desktop/tags/index.js | 2 +- .../tags/pages/{bbs.tag => channels.tag} | 12 ++--- 8 files changed, 65 insertions(+), 67 deletions(-) rename src/api/models/{bbs-thread.ts => channel.ts} (75%) delete mode 100644 src/api/serializers/bbs-thread.ts create mode 100644 src/api/serializers/channel.ts rename src/web/app/desktop/tags/pages/{bbs.tag => channels.tag} (64%) diff --git a/locales/en.yml b/locales/en.yml index f0204b52c..da532fc78 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -352,10 +352,9 @@ desktop: mk-repost-form-window: title: "Are you sure you want to repost this post?" - mk-bbs-page: - title: "Misskey BBS" - new: "Create new thread" - thread-title: "Thread title" + mk-channels-page: + new: "Create new channel" + channel-title: "Channel title" mobile: tags: diff --git a/locales/ja.yml b/locales/ja.yml index 65d92782f..1ae94652b 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -352,10 +352,9 @@ desktop: mk-repost-form-window: title: "この投稿をRepostしますか?" - mk-bbs-page: - title: "Misskey掲示板" - new: "スレッドを作成" - thread-title: "スレッドのタイトル" + mk-channels-page: + new: "チャンネルを作成" + channel-title: "チャンネルのタイトル" mobile: tags: diff --git a/src/api/endpoints/bbs/threads/create.ts b/src/api/endpoints/bbs/threads/create.ts index 71d61d871..d9b4d34a0 100644 --- a/src/api/endpoints/bbs/threads/create.ts +++ b/src/api/endpoints/bbs/threads/create.ts @@ -2,11 +2,11 @@ * Module dependencies */ import $ from 'cafy'; -import Thread from '../../../models/bbs-thread'; -import serialize from '../../../serializers/bbs-thread'; +import Channel from '../../../models/channel'; +import serialize from '../../../serializers/channel'; /** - * Create a thread + * Create a channel * * @param {any} params * @param {any} user @@ -17,13 +17,13 @@ module.exports = async (params, user) => new Promise(async (res, rej) => { const [title, titleErr] = $(params.title).string().range(1, 100).$; if (titleErr) return rej('invalid title param'); - // Create a thread - const thread = await Thread.insert({ + // Create a channel + const channel = await Channel.insert({ created_at: new Date(), user_id: user._id, title: title }); // Response - res(await serialize(thread)); + res(await serialize(channel)); }); diff --git a/src/api/models/bbs-thread.ts b/src/api/models/channel.ts similarity index 75% rename from src/api/models/bbs-thread.ts rename to src/api/models/channel.ts index a92157c6f..79edb7136 100644 --- a/src/api/models/bbs-thread.ts +++ b/src/api/models/channel.ts @@ -1,11 +1,11 @@ import * as mongo from 'mongodb'; import db from '../../db/mongodb'; -const collection = db.get('bbs_threads'); +const collection = db.get('channels'); export default collection as any; // fuck type definition -export type IBbsThread = { +export type IChannel = { _id: mongo.ObjectID; created_at: Date; title: string; diff --git a/src/api/serializers/bbs-thread.ts b/src/api/serializers/bbs-thread.ts deleted file mode 100644 index d9e41a846..000000000 --- a/src/api/serializers/bbs-thread.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Module dependencies - */ -import * as mongo from 'mongodb'; -import deepcopy = require('deepcopy'); -import { IUser } from '../models/user'; -import { default as Thread, IBbsThread } from '../models/bbs-thread'; - -/** - * Serialize a thread - * - * @param thread target - * @param me? serializee - * @return response - */ -export default ( - thread: string | mongo.ObjectID | IBbsThread, - me?: string | mongo.ObjectID | IUser -) => new Promise(async (resolve, reject) => { - - let _thread: any; - - // Populate the thread if 'thread' is ID - if (mongo.ObjectID.prototype.isPrototypeOf(thread)) { - _thread = await Thread.findOne({ - _id: thread - }); - } else if (typeof thread === 'string') { - _thread = await Thread.findOne({ - _id: new mongo.ObjectID(thread) - }); - } else { - _thread = deepcopy(thread); - } - - // Rename _id to id - _thread.id = _thread._id; - delete _thread._id; - - // Remove needless properties - delete _thread.user_id; - - resolve(_thread); -}); diff --git a/src/api/serializers/channel.ts b/src/api/serializers/channel.ts new file mode 100644 index 000000000..d4e16d6be --- /dev/null +++ b/src/api/serializers/channel.ts @@ -0,0 +1,44 @@ +/** + * Module dependencies + */ +import * as mongo from 'mongodb'; +import deepcopy = require('deepcopy'); +import { IUser } from '../models/user'; +import { default as Channel, IChannel } from '../models/channel'; + +/** + * Serialize a channel + * + * @param channel target + * @param me? serializee + * @return response + */ +export default ( + channel: string | mongo.ObjectID | IChannel, + me?: string | mongo.ObjectID | IUser +) => new Promise(async (resolve, reject) => { + + let _channel: any; + + // Populate the channel if 'channel' is ID + if (mongo.ObjectID.prototype.isPrototypeOf(channel)) { + _channel = await Channel.findOne({ + _id: channel + }); + } else if (typeof channel === 'string') { + _channel = await Channel.findOne({ + _id: new mongo.ObjectID(channel) + }); + } else { + _channel = deepcopy(channel); + } + + // Rename _id to id + _channel.id = _channel._id; + delete _channel._id; + + // Remove needless properties + delete _channel.user_id; + + resolve(_channel); +}); diff --git a/src/web/app/desktop/tags/index.js b/src/web/app/desktop/tags/index.js index fa7161ddf..6d4900652 100644 --- a/src/web/app/desktop/tags/index.js +++ b/src/web/app/desktop/tags/index.js @@ -61,7 +61,7 @@ require('./pages/user.tag'); require('./pages/post.tag'); require('./pages/search.tag'); require('./pages/not-found.tag'); -require('./pages/bbs.tag'); +require('./pages/channels.tag'); require('./autocomplete-suggestion.tag'); require('./progress-dialog.tag'); require('./user-preview.tag'); diff --git a/src/web/app/desktop/tags/pages/bbs.tag b/src/web/app/desktop/tags/pages/channels.tag similarity index 64% rename from src/web/app/desktop/tags/pages/bbs.tag rename to src/web/app/desktop/tags/pages/channels.tag index cb58af193..9e47e52d2 100644 --- a/src/web/app/desktop/tags/pages/bbs.tag +++ b/src/web/app/desktop/tags/pages/channels.tag @@ -1,4 +1,4 @@ - +

      %i18n:desktop.tags.mk-bbs-page.title%

      @@ -18,13 +18,13 @@ }); this.new = () => { - const title = window.prompt('%i18n:desktop.tags.mk-bbs-page.thread-title%'); + const title = window.prompt('%i18n:desktop.tags.mk-bbs-page.channel-title%'); - this.api('bbs/threads/create', { + this.api('bbs/channels/create', { title: title - }).then(thread => { - location.href = '/bbs/' + thread.id; + }).then(channel => { + location.href = '/bbs/' + channel.id; }); }; - + From 639569f51f25b4ac62faafa325dfd1495017b1de Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 31 Oct 2017 22:09:09 +0900 Subject: [PATCH 025/111] wip --- src/api/endpoints/posts/create.ts | 47 +++++++++++++++++++++++++++---- src/api/models/post.ts | 1 + 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/api/endpoints/posts/create.ts b/src/api/endpoints/posts/create.ts index 805dba7f8..42a55f850 100644 --- a/src/api/endpoints/posts/create.ts +++ b/src/api/endpoints/posts/create.ts @@ -4,9 +4,9 @@ import $ from 'cafy'; import deepEqual = require('deep-equal'); import parse from '../../common/text'; -import Post from '../../models/post'; -import { isValidText } from '../../models/post'; +import { default as Post, IPost, isValidText } from '../../models/post'; import { default as User, IUser } from '../../models/user'; +import { default as Channel, IChannel } from '../../models/channel'; import Following from '../../models/following'; import DriveFile from '../../models/drive-file'; import Watching from '../../models/post-watching'; @@ -62,7 +62,8 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { const [repostId, repostIdErr] = $(params.repost_id).optional.id().$; if (repostIdErr) return rej('invalid repost_id'); - let repost = null; + let repost: IPost = null; + let isQuote = false; if (repostId !== undefined) { // Fetch repost to post repost = await Post.findOne({ @@ -84,18 +85,20 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { } }); + isQuote = text != null || files != null; + // 直近と同じRepost対象かつ引用じゃなかったらエラー if (latestPost && latestPost.repost_id && latestPost.repost_id.equals(repost._id) && - text === undefined && files === null) { + !isQuote) { return rej('cannot repost same post that already reposted in your latest post'); } // 直近がRepost対象かつ引用じゃなかったらエラー if (latestPost && latestPost._id.equals(repost._id) && - text === undefined && files === null) { + !isQuote) { return rej('cannot repost your latest post'); } } @@ -104,7 +107,7 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { const [inReplyToPostId, inReplyToPostIdErr] = $(params.reply_to_id).optional.id().$; if (inReplyToPostIdErr) return rej('invalid in_reply_to_post_id'); - let inReplyToPost = null; + let inReplyToPost: IPost = null; if (inReplyToPostId !== undefined) { // Fetch reply inReplyToPost = await Post.findOne({ @@ -121,6 +124,37 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { } } + // Get 'channel_id' parameter + const [channelId, channelIdErr] = $(params.channel_id).optional.id().$; + if (channelIdErr) return rej('invalid channel_id'); + + let channel: IChannel = null; + if (channelId !== undefined) { + // Fetch channel + channel = await Channel.findOne({ + _id: channelId + }); + + if (channel === null) { + return rej('channel not found'); + } + + // 返信対象の投稿がこのチャンネルじゃなかったらダメ + if (inReplyToPost && !channelId.equals(inReplyToPost.channel_id)) { + return rej('チャンネル内部からチャンネル外部の投稿に返信することはできません'); + } + + // Repost対象の投稿がこのチャンネルじゃなかったらダメ + if (repost && !channelId.equals(repost.channel_id)) { + return rej('チャンネル内部からチャンネル外部の投稿をRepostすることはできません'); + } + + // 引用ではないRepostはダメ + if (repost && !isQuote) { + return rej('チャンネル内部では引用ではないRepostをすることはできません'); + } + } + // Get 'poll' parameter const [poll, pollErr] = $(params.poll).optional.strict.object() .have('choices', $().array('string') @@ -164,6 +198,7 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { // 投稿を作成 const post = await Post.insert({ created_at: new Date(), + channel_id: channel ? channel._id : undefined, media_ids: files ? files.map(file => file._id) : undefined, reply_to_id: inReplyToPost ? inReplyToPost._id : undefined, repost_id: repost ? repost._id : undefined, diff --git a/src/api/models/post.ts b/src/api/models/post.ts index 8b9f7f5ef..fe07dcb0b 100644 --- a/src/api/models/post.ts +++ b/src/api/models/post.ts @@ -10,6 +10,7 @@ export function isValidText(text: string): boolean { export type IPost = { _id: mongo.ObjectID; + channel_id: mongo.ObjectID; created_at: Date; media_ids: mongo.ObjectID[]; reply_to_id: mongo.ObjectID; From ba0ef67ac9b577043898cb4fab612f9802722ae0 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 31 Oct 2017 22:14:12 +0900 Subject: [PATCH 026/111] Fix indent --- src/api/endpoints/posts/create.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/api/endpoints/posts/create.ts b/src/api/endpoints/posts/create.ts index 42a55f850..e0a02fa4a 100644 --- a/src/api/endpoints/posts/create.ts +++ b/src/api/endpoints/posts/create.ts @@ -186,11 +186,11 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { repost: user.latest_post.repost_id ? user.latest_post.repost_id.toString() : null, media_ids: (user.latest_post.media_ids || []).map(id => id.toString()) }, { - text: text, - reply: inReplyToPost ? inReplyToPost._id.toString() : null, - repost: repost ? repost._id.toString() : null, - media_ids: (files || []).map(file => file._id.toString()) - })) { + text: text, + reply: inReplyToPost ? inReplyToPost._id.toString() : null, + repost: repost ? repost._id.toString() : null, + media_ids: (files || []).map(file => file._id.toString()) + })) { return rej('duplicate'); } } From 097890c73b40cddb93898c547fa8271454af7577 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 31 Oct 2017 22:35:31 +0900 Subject: [PATCH 027/111] wip --- locales/en.yml | 2 +- locales/ja.yml | 2 +- src/api/endpoints.ts | 12 ++++++++++-- .../endpoints/{bbs/threads => channels}/create.ts | 4 ++-- src/web/app/desktop/router.js | 5 +++++ src/web/app/desktop/tags/pages/channels.tag | 8 +++----- src/web/app/desktop/tags/ui.tag | 8 ++++---- 7 files changed, 26 insertions(+), 15 deletions(-) rename src/api/endpoints/{bbs/threads => channels}/create.ts (84%) diff --git a/locales/en.yml b/locales/en.yml index da532fc78..5c7a1165b 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -241,7 +241,7 @@ desktop: mk-ui-header-nav: home: "Home" messaging: "Messages" - bbs: "BBS" + channels: "Channels" info: "News" mk-ui-header-search: diff --git a/locales/ja.yml b/locales/ja.yml index 1ae94652b..dd76a2b90 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -241,7 +241,7 @@ desktop: mk-ui-header-nav: home: "ホーム" messaging: "メッセージ" - bbs: "掲示板" + channels: "チャンネル" info: "お知らせ" mk-ui-header-search: diff --git a/src/api/endpoints.ts b/src/api/endpoints.ts index 29a97bcb8..26177b877 100644 --- a/src/api/endpoints.ts +++ b/src/api/endpoints.ts @@ -474,8 +474,16 @@ const endpoints: Endpoint[] = [ name: 'messaging/messages/create', withCredential: true, kind: 'messaging-write' - } - + }, + { + name: 'channels/create', + withCredential: true, + limit: { + duration: ms('1hour'), + max: 3, + minInterval: ms('10seconds') + } + }, ]; export default endpoints; diff --git a/src/api/endpoints/bbs/threads/create.ts b/src/api/endpoints/channels/create.ts similarity index 84% rename from src/api/endpoints/bbs/threads/create.ts rename to src/api/endpoints/channels/create.ts index d9b4d34a0..74b089dfc 100644 --- a/src/api/endpoints/bbs/threads/create.ts +++ b/src/api/endpoints/channels/create.ts @@ -2,8 +2,8 @@ * Module dependencies */ import $ from 'cafy'; -import Channel from '../../../models/channel'; -import serialize from '../../../serializers/channel'; +import Channel from '../../models/channel'; +import serialize from '../../serializers/channel'; /** * Create a channel diff --git a/src/web/app/desktop/router.js b/src/web/app/desktop/router.js index afa8a2dce..51738f3af 100644 --- a/src/web/app/desktop/router.js +++ b/src/web/app/desktop/router.js @@ -9,6 +9,7 @@ let page = null; export default me => { route('/', index); route('/i>mentions', mentions); + route('/channel', channels); route('/post::post', post); route('/search::query', search); route('/:user', user.bind(null, 'home')); @@ -54,6 +55,10 @@ export default me => { mount(el); } + function channels() { + mount(document.createElement('mk-channels-page')); + } + function notFound() { mount(document.createElement('mk-not-found')); } diff --git a/src/web/app/desktop/tags/pages/channels.tag b/src/web/app/desktop/tags/pages/channels.tag index 9e47e52d2..03fae3c8d 100644 --- a/src/web/app/desktop/tags/pages/channels.tag +++ b/src/web/app/desktop/tags/pages/channels.tag @@ -1,8 +1,7 @@
      -

      %i18n:desktop.tags.mk-bbs-page.title%

      - +
      + + diff --git a/src/web/app/desktop/tags/pages/channels.tag b/src/web/app/desktop/tags/pages/channels.tag index 03fae3c8d..220f1ca50 100644 --- a/src/web/app/desktop/tags/pages/channels.tag +++ b/src/web/app/desktop/tags/pages/channels.tag @@ -18,7 +18,7 @@ this.new = () => { const title = window.prompt('%i18n:desktop.tags.mk-channels-page.channel-title%'); - this.api('bbs/channels/create', { + this.api('channels/create', { title: title }).then(channel => { location.href = '/channel/' + channel.id; diff --git a/src/web/app/desktop/tags/pages/user.tag b/src/web/app/desktop/tags/pages/user.tag index 864fe2273..811ca5c0f 100644 --- a/src/web/app/desktop/tags/pages/user.tag +++ b/src/web/app/desktop/tags/pages/user.tag @@ -16,7 +16,7 @@ this.refs.ui.refs.user.on('user-fetched', user => { Progress.set(0.5); - document.title = user.name + ' | Misskey' + document.title = user.name + ' | Misskey'; }); this.refs.ui.refs.user.on('loaded', () => { From ee14d33d53755d4b932aa9c4e9f5228f782f3816 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 1 Nov 2017 00:10:30 +0900 Subject: [PATCH 029/111] wip --- src/api/endpoints.ts | 3 + src/api/endpoints/channels/posts.ts | 79 +++++++++++++++++ src/web/app/common/scripts/channel-stream.js | 14 +++ src/web/app/desktop/tags/pages/channel.tag | 87 +++++++++++++++++++ .../app/desktop/tags/pages/drive-chooser.tag | 44 ++++++++++ 5 files changed, 227 insertions(+) create mode 100644 src/api/endpoints/channels/posts.ts create mode 100644 src/web/app/common/scripts/channel-stream.js create mode 100644 src/web/app/desktop/tags/pages/drive-chooser.tag diff --git a/src/api/endpoints.ts b/src/api/endpoints.ts index 45b83fc9e..88c01d4e7 100644 --- a/src/api/endpoints.ts +++ b/src/api/endpoints.ts @@ -487,6 +487,9 @@ const endpoints: Endpoint[] = [ { name: 'channels/show' }, + { + name: 'channels/posts' + }, ]; export default endpoints; diff --git a/src/api/endpoints/channels/posts.ts b/src/api/endpoints/channels/posts.ts new file mode 100644 index 000000000..fa91fb93e --- /dev/null +++ b/src/api/endpoints/channels/posts.ts @@ -0,0 +1,79 @@ +/** + * Module dependencies + */ +import $ from 'cafy'; +import { default as Channel, IChannel } from '../../models/channel'; +import { default as Post, IPost } from '../../models/post'; +import serialize from '../../serializers/post'; + +/** + * Show a posts of a channel + * + * @param {any} params + * @param {any} user + * @return {Promise} + */ +module.exports = (params, user) => new Promise(async (res, rej) => { + // Get 'limit' parameter + const [limit = 1000, limitErr] = $(params.limit).optional.number().range(1, 1000).$; + if (limitErr) return rej('invalid limit param'); + + // Get 'since_id' parameter + const [sinceId, sinceIdErr] = $(params.since_id).optional.id().$; + if (sinceIdErr) return rej('invalid since_id param'); + + // Get 'max_id' parameter + const [maxId, maxIdErr] = $(params.max_id).optional.id().$; + if (maxIdErr) return rej('invalid max_id param'); + + // Check if both of since_id and max_id is specified + if (sinceId && maxId) { + return rej('cannot set since_id and max_id'); + } + + // Get 'channel_id' parameter + const [channelId, channelIdErr] = $(params.channel_id).id().$; + if (channelIdErr) return rej('invalid channel_id param'); + + // Fetch channel + const channel: IChannel = await Channel.findOne({ + _id: channelId + }); + + if (channel === null) { + return rej('channel not found'); + } + + //#region Construct query + const sort = { + _id: -1 + }; + + const query = { + channel_id: channel._id + } as any; + + if (sinceId) { + sort._id = 1; + query._id = { + $gt: sinceId + }; + } else if (maxId) { + query._id = { + $lt: maxId + }; + } + //#endregion Construct query + + // Issue query + const posts = await Post + .find(query, { + limit: limit, + sort: sort + }); + + // Serialize + res(await Promise.all(posts.map(async (post) => + await serialize(post, user) + ))); +}); diff --git a/src/web/app/common/scripts/channel-stream.js b/src/web/app/common/scripts/channel-stream.js new file mode 100644 index 000000000..38e7d9113 --- /dev/null +++ b/src/web/app/common/scripts/channel-stream.js @@ -0,0 +1,14 @@ +'use strict'; + +import Stream from './stream'; + +/** + * Channel stream connection + */ +class Connection extends Stream { + constructor() { + super('channel'); + } +} + +export default Connection; diff --git a/src/web/app/desktop/tags/pages/channel.tag b/src/web/app/desktop/tags/pages/channel.tag index 4fa172f99..8a3034f40 100644 --- a/src/web/app/desktop/tags/pages/channel.tag +++ b/src/web/app/desktop/tags/pages/channel.tag @@ -2,6 +2,8 @@

      { parent.channel.title }

      + +
      + + +
      + { post.user.name } +
      +
      + { post.text } +
      + + +
      + + +

      { reply.user.name }への返信: (or キャンセル)

      + + + + + +
      diff --git a/src/web/app/desktop/tags/pages/drive-chooser.tag b/src/web/app/desktop/tags/pages/drive-chooser.tag new file mode 100644 index 000000000..49741ad40 --- /dev/null +++ b/src/web/app/desktop/tags/pages/drive-chooser.tag @@ -0,0 +1,44 @@ + + +
      + + + +
      + + + +
      From fbec3765d4212a958b8ee95f11d3ee7e4eae1962 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 1 Nov 2017 01:38:19 +0900 Subject: [PATCH 030/111] wip --- src/api/endpoints/channels/create.ts | 3 +- src/api/endpoints/posts/create.ts | 17 +++++++++++ src/api/models/channel.ts | 1 + src/api/serializers/post.ts | 8 ++++- src/web/app/desktop/tags/pages/channel.tag | 35 ++++++++++++++++++---- src/web/app/desktop/tags/timeline.tag | 4 +++ src/web/app/mobile/tags/timeline.tag | 4 +++ 7 files changed, 65 insertions(+), 7 deletions(-) diff --git a/src/api/endpoints/channels/create.ts b/src/api/endpoints/channels/create.ts index 74b089dfc..e0c0e0192 100644 --- a/src/api/endpoints/channels/create.ts +++ b/src/api/endpoints/channels/create.ts @@ -21,7 +21,8 @@ module.exports = async (params, user) => new Promise(async (res, rej) => { const channel = await Channel.insert({ created_at: new Date(), user_id: user._id, - title: title + title: title, + index: 0 }); // Response diff --git a/src/api/endpoints/posts/create.ts b/src/api/endpoints/posts/create.ts index e0a02fa4a..183cabf13 100644 --- a/src/api/endpoints/posts/create.ts +++ b/src/api/endpoints/posts/create.ts @@ -153,6 +153,16 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { if (repost && !isQuote) { return rej('チャンネル内部では引用ではないRepostをすることはできません'); } + } else { + // 返信対象の投稿がチャンネルへの投稿だったらダメ + if (inReplyToPost && inReplyToPost.channel_id != null) { + return rej('チャンネル外部からチャンネル内部の投稿に返信することはできません'); + } + + // Repost対象の投稿がチャンネルへの投稿だったらダメ + if (repost && repost.channel_id != null) { + return rej('チャンネル外部からチャンネル内部の投稿をRepostすることはできません'); + } } // Get 'poll' parameter @@ -199,6 +209,7 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { const post = await Post.insert({ created_at: new Date(), channel_id: channel ? channel._id : undefined, + index: channel ? channel.index + 1 : undefined, media_ids: files ? files.map(file => file._id) : undefined, reply_to_id: inReplyToPost ? inReplyToPost._id : undefined, repost_id: repost ? repost._id : undefined, @@ -217,6 +228,12 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { // ----------------------------------------------------------- // Post processes + Channel.update({ _id: channel._id }, { + $inc: { + index: 1 + } + }); + User.update({ _id: user._id }, { $set: { latest_post: post diff --git a/src/api/models/channel.ts b/src/api/models/channel.ts index 79edb7136..c80e84dbc 100644 --- a/src/api/models/channel.ts +++ b/src/api/models/channel.ts @@ -10,4 +10,5 @@ export type IChannel = { created_at: Date; title: string; user_id: mongo.ObjectID; + index: number; }; diff --git a/src/api/serializers/post.ts b/src/api/serializers/post.ts index df917a859..7d40df2d6 100644 --- a/src/api/serializers/post.ts +++ b/src/api/serializers/post.ts @@ -8,6 +8,7 @@ import Reaction from '../models/post-reaction'; import { IUser } from '../models/user'; import Vote from '../models/poll-vote'; import serializeApp from './app'; +import serializeChannel from './channel'; import serializeUser from './user'; import serializeDriveFile from './drive-file'; import parse from '../common/text'; @@ -76,8 +77,13 @@ const self = ( _post.app = await serializeApp(_post.app_id); } + // Populate channel + if (_post.channel_id) { + _post.channel = await serializeChannel(_post.channel_id); + } + + // Populate media if (_post.media_ids) { - // Populate media _post.media = await Promise.all(_post.media_ids.map(async fileId => await serializeDriveFile(fileId) )); diff --git a/src/web/app/desktop/tags/pages/channel.tag b/src/web/app/desktop/tags/pages/channel.tag index 8a3034f40..ebd26f07b 100644 --- a/src/web/app/desktop/tags/pages/channel.tag +++ b/src/web/app/desktop/tags/pages/channel.tag @@ -2,8 +2,9 @@

      { parent.channel.title }

      - - + +
      +
      @@ -57,9 +60,13 @@
      - { post.user.name } + { post.index }: + { post.user.name } + + ID:{ post.user.username }
      + >>{ post.reply_to.index } { post.text }
      -

      { reply.user.name }への返信: (or キャンセル)

      +

      >>{ reply.index } ({ reply.user.name }): [x]

      - - - - - - - diff --git a/src/web/app/desktop/tags/pages/selectdrive.tag b/src/web/app/desktop/tags/pages/selectdrive.tag new file mode 100644 index 000000000..b196357d8 --- /dev/null +++ b/src/web/app/desktop/tags/pages/selectdrive.tag @@ -0,0 +1,159 @@ + + +
      + + + +
      + + + +
      From ef3100fb8e826e22cba61ef18107860fa92f9662 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 1 Nov 2017 03:17:14 +0900 Subject: [PATCH 032/111] wip --- docs/setup.en.md | 1 + docs/setup.ja.md | 1 + locales/en.yml | 13 +++-- locales/ja.yml | 13 +++-- src/api/endpoints/posts/create.ts | 7 ++- src/api/event.ts | 6 +++ src/api/stream/channel.ts | 12 +++++ src/api/streaming.ts | 22 +++++--- src/config.ts | 2 + src/web/app/ch/router.js | 32 ++++++++++++ src/web/app/ch/script.js | 18 +++++++ src/web/app/ch/style.styl | 4 ++ .../tags/pages => ch/tags}/channel.tag | 52 +++++++++++++------ src/web/app/ch/tags/index.js | 2 + src/web/app/ch/tags/index.tag | 24 +++++++++ src/web/app/common/scripts/channel-stream.js | 6 ++- src/web/app/common/scripts/config.js | 2 + src/web/app/desktop/router.js | 12 ----- src/web/app/desktop/tags/index.js | 2 - src/web/app/desktop/tags/pages/channels.tag | 28 ---------- src/web/app/desktop/tags/timeline.tag | 2 +- src/web/app/desktop/tags/ui.tag | 6 +-- src/web/app/mobile/tags/timeline.tag | 2 +- src/web/app/mobile/tags/ui.tag | 5 +- webpack/webpack.config.ts | 1 + 25 files changed, 189 insertions(+), 86 deletions(-) create mode 100644 src/api/stream/channel.ts create mode 100644 src/web/app/ch/router.js create mode 100644 src/web/app/ch/script.js create mode 100644 src/web/app/ch/style.styl rename src/web/app/{desktop/tags/pages => ch/tags}/channel.tag (76%) create mode 100644 src/web/app/ch/tags/index.js create mode 100644 src/web/app/ch/tags/index.tag delete mode 100644 src/web/app/desktop/tags/pages/channels.tag diff --git a/docs/setup.en.md b/docs/setup.en.md index 3e4893534..dbc0599b5 100644 --- a/docs/setup.en.md +++ b/docs/setup.en.md @@ -25,6 +25,7 @@ Note that Misskey uses following subdomains: * **api**.*{primary domain}* * **auth**.*{primary domain}* * **about**.*{primary domain}* +* **ch**.*{primary domain}* * **stats**.*{primary domain}* * **status**.*{primary domain}* * **dev**.*{primary domain}* diff --git a/docs/setup.ja.md b/docs/setup.ja.md index 4f48a0808..602fd9b6a 100644 --- a/docs/setup.ja.md +++ b/docs/setup.ja.md @@ -26,6 +26,7 @@ Misskeyは以下のサブドメインを使います: * **api**.*{primary domain}* * **auth**.*{primary domain}* * **about**.*{primary domain}* +* **ch**.*{primary domain}* * **stats**.*{primary domain}* * **status**.*{primary domain}* * **dev**.*{primary domain}* diff --git a/locales/en.yml b/locales/en.yml index 5c7a1165b..643649b46 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -164,6 +164,12 @@ common: mk-uploader: waiting: "Waiting" +ch: + tags: + mk-index: + new: "Create new channel" + channel-title: "Channel title" + desktop: tags: mk-api-info: @@ -241,7 +247,7 @@ desktop: mk-ui-header-nav: home: "Home" messaging: "Messages" - channels: "Channels" + ch: "Channels" info: "News" mk-ui-header-search: @@ -352,10 +358,6 @@ desktop: mk-repost-form-window: title: "Are you sure you want to repost this post?" - mk-channels-page: - new: "Create new channel" - channel-title: "Channel title" - mobile: tags: mk-drive-file-viewer: @@ -496,6 +498,7 @@ mobile: home: "Home" notifications: "Notifications" messaging: "Messages" + ch: "Channels" drive: "Drive" settings: "Settings" about: "About Misskey" diff --git a/locales/ja.yml b/locales/ja.yml index dd76a2b90..9fd7d94f0 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -164,6 +164,12 @@ common: mk-uploader: waiting: "待機中" +ch: + tags: + mk-index: + new: "チャンネルを作成" + channel-title: "チャンネルのタイトル" + desktop: tags: mk-api-info: @@ -241,7 +247,7 @@ desktop: mk-ui-header-nav: home: "ホーム" messaging: "メッセージ" - channels: "チャンネル" + ch: "チャンネル" info: "お知らせ" mk-ui-header-search: @@ -352,10 +358,6 @@ desktop: mk-repost-form-window: title: "この投稿をRepostしますか?" - mk-channels-page: - new: "チャンネルを作成" - channel-title: "チャンネルのタイトル" - mobile: tags: mk-drive-file-viewer: @@ -496,6 +498,7 @@ mobile: home: "ホーム" notifications: "通知" messaging: "メッセージ" + ch: "チャンネル" search: "検索" drive: "ドライブ" settings: "設定" diff --git a/src/api/endpoints/posts/create.ts b/src/api/endpoints/posts/create.ts index 183cabf13..34265dcbc 100644 --- a/src/api/endpoints/posts/create.ts +++ b/src/api/endpoints/posts/create.ts @@ -13,7 +13,7 @@ import Watching from '../../models/post-watching'; import serialize from '../../serializers/post'; import notify from '../../common/notify'; import watch from '../../common/watch-post'; -import event from '../../event'; +import { default as event, publishChannelStream } from '../../event'; import config from '../../../conf'; /** @@ -258,6 +258,11 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { // Publish event to myself's stream event(user._id, 'post', postObj); + // Publish event to channel + if (channel) { + publishChannelStream(channel._id, 'post', postObj); + } + // Fetch all followers const followers = await Following .find({ diff --git a/src/api/event.ts b/src/api/event.ts index 9613a9f7c..909b0d255 100644 --- a/src/api/event.ts +++ b/src/api/event.ts @@ -25,6 +25,10 @@ class MisskeyEvent { this.publish(`messaging-stream:${userId}-${otherpartyId}`, type, typeof value === 'undefined' ? null : value); } + public publishChannelStream(channelId: ID, type: string, value?: any): void { + this.publish(`channel-stream:${channelId}`, type, typeof value === 'undefined' ? null : value); + } + private publish(channel: string, type: string, value?: any): void { const message = value == null ? { type: type } : @@ -41,3 +45,5 @@ export default ev.publishUserStream.bind(ev); export const publishPostStream = ev.publishPostStream.bind(ev); export const publishMessagingStream = ev.publishMessagingStream.bind(ev); + +export const publishChannelStream = ev.publishChannelStream.bind(ev); diff --git a/src/api/stream/channel.ts b/src/api/stream/channel.ts new file mode 100644 index 000000000..d67d77cbf --- /dev/null +++ b/src/api/stream/channel.ts @@ -0,0 +1,12 @@ +import * as websocket from 'websocket'; +import * as redis from 'redis'; + +export default function(request: websocket.request, connection: websocket.connection, subscriber: redis.RedisClient): void { + const channel = request.resourceURL.query.channel; + + // Subscribe channel stream + subscriber.subscribe(`misskey:channel-stream:${channel}`); + subscriber.on('message', (_, data) => { + connection.send(data); + }); +} diff --git a/src/api/streaming.ts b/src/api/streaming.ts index db600013b..0e512fb21 100644 --- a/src/api/streaming.ts +++ b/src/api/streaming.ts @@ -9,6 +9,7 @@ import isNativeToken from './common/is-native-token'; import homeStream from './stream/home'; import messagingStream from './stream/messaging'; import serverStream from './stream/server'; +import channelStream from './stream/channel'; module.exports = (server: http.Server) => { /** @@ -26,14 +27,6 @@ module.exports = (server: http.Server) => { return; } - const user = await authenticate(request.resourceURL.query.i); - - if (user == null) { - connection.send('authentication-failed'); - connection.close(); - return; - } - // Connect to Redis const subscriber = redis.createClient( config.redis.port, config.redis.host); @@ -43,6 +36,19 @@ module.exports = (server: http.Server) => { subscriber.quit(); }); + if (request.resourceURL.pathname === '/channel') { + channelStream(request, connection, subscriber); + return; + } + + const user = await authenticate(request.resourceURL.query.i); + + if (user == null) { + connection.send('authentication-failed'); + connection.close(); + return; + } + const channel = request.resourceURL.pathname === '/' ? homeStream : request.resourceURL.pathname === '/messaging' ? messagingStream : diff --git a/src/config.ts b/src/config.ts index 46a93f5fe..18017e974 100644 --- a/src/config.ts +++ b/src/config.ts @@ -88,6 +88,7 @@ type Mixin = { api_url: string; auth_url: string; about_url: string; + ch_url: stirng; stats_url: string; status_url: string; dev_url: string; @@ -122,6 +123,7 @@ export default function load() { mixin.secondary_scheme = config.secondary_url.substr(0, config.secondary_url.indexOf('://')); mixin.api_url = `${mixin.scheme}://api.${mixin.host}`; mixin.auth_url = `${mixin.scheme}://auth.${mixin.host}`; + mixin.ch_url = `${mixin.scheme}://ch.${mixin.host}`; mixin.dev_url = `${mixin.scheme}://dev.${mixin.host}`; mixin.about_url = `${mixin.scheme}://about.${mixin.host}`; mixin.stats_url = `${mixin.scheme}://stats.${mixin.host}`; diff --git a/src/web/app/ch/router.js b/src/web/app/ch/router.js new file mode 100644 index 000000000..424158f40 --- /dev/null +++ b/src/web/app/ch/router.js @@ -0,0 +1,32 @@ +import * as riot from 'riot'; +const route = require('page'); +let page = null; + +export default me => { + route('/', index); + route('/:channel', channel); + route('*', notFound); + + function index() { + mount(document.createElement('mk-index')); + } + + function channel(ctx) { + const el = document.createElement('mk-channel'); + el.setAttribute('id', ctx.params.channel); + mount(el); + } + + function notFound() { + mount(document.createElement('mk-not-found')); + } + + // EXEC + route(); +}; + +function mount(content) { + if (page) page.unmount(); + const body = document.getElementById('app'); + page = riot.mount(body.appendChild(content))[0]; +} diff --git a/src/web/app/ch/script.js b/src/web/app/ch/script.js new file mode 100644 index 000000000..760d405c5 --- /dev/null +++ b/src/web/app/ch/script.js @@ -0,0 +1,18 @@ +/** + * Channels + */ + +// Style +import './style.styl'; + +require('./tags'); +import init from '../init'; +import route from './router'; + +/** + * init + */ +init(me => { + // Start routing + route(me); +}); diff --git a/src/web/app/ch/style.styl b/src/web/app/ch/style.styl new file mode 100644 index 000000000..2fc3ac3fc --- /dev/null +++ b/src/web/app/ch/style.styl @@ -0,0 +1,4 @@ +@import "../base" + +html + background #efefef diff --git a/src/web/app/desktop/tags/pages/channel.tag b/src/web/app/ch/tags/channel.tag similarity index 76% rename from src/web/app/desktop/tags/pages/channel.tag rename to src/web/app/ch/tags/channel.tag index a14c0648c..b16844b8b 100644 --- a/src/web/app/desktop/tags/pages/channel.tag +++ b/src/web/app/ch/tags/channel.tag @@ -1,14 +1,19 @@ - - -
      -

      { parent.channel.title }

      - - - -
      - -
      -
      + +
      +

      { channel.title }

      + + + +
      + +
      +

      参加するにはログインまたは新規登録してください

      +
      +
      +
      + Misskey ver { version } (葵 aoi) +
      +
      -
      +
      @@ -127,7 +147,7 @@ + diff --git a/src/web/app/common/scripts/channel-stream.js b/src/web/app/common/scripts/channel-stream.js index 38e7d9113..17944dbe4 100644 --- a/src/web/app/common/scripts/channel-stream.js +++ b/src/web/app/common/scripts/channel-stream.js @@ -6,8 +6,10 @@ import Stream from './stream'; * Channel stream connection */ class Connection extends Stream { - constructor() { - super('channel'); + constructor(channelId) { + super('channel', { + channel: channelId + }); } } diff --git a/src/web/app/common/scripts/config.js b/src/web/app/common/scripts/config.js index 75a7abba2..c5015622f 100644 --- a/src/web/app/common/scripts/config.js +++ b/src/web/app/common/scripts/config.js @@ -6,6 +6,7 @@ const host = isRoot ? Url.host : Url.host.substring(Url.host.indexOf('.') + 1, U const scheme = Url.protocol; const url = `${scheme}//${host}`; const apiUrl = `${scheme}//api.${host}`; +const chUrl = `${scheme}//ch.${host}`; const devUrl = `${scheme}//dev.${host}`; const aboutUrl = `${scheme}//about.${host}`; const statsUrl = `${scheme}//stats.${host}`; @@ -16,6 +17,7 @@ export default { scheme, url, apiUrl, + chUrl, devUrl, aboutUrl, statsUrl, diff --git a/src/web/app/desktop/router.js b/src/web/app/desktop/router.js index df67bb7b7..977e3fa9a 100644 --- a/src/web/app/desktop/router.js +++ b/src/web/app/desktop/router.js @@ -10,8 +10,6 @@ export default me => { route('/', index); route('/selectdrive', selectDrive); route('/i>mentions', mentions); - route('/channel', channels); - route('/channel/:channel', channel); route('/post::post', post); route('/search::query', search); route('/:user', user.bind(null, 'home')); @@ -57,16 +55,6 @@ export default me => { mount(el); } - function channel(ctx) { - const el = document.createElement('mk-channel-page'); - el.setAttribute('id', ctx.params.channel); - mount(el); - } - - function channels() { - mount(document.createElement('mk-channels-page')); - } - function selectDrive() { mount(document.createElement('mk-selectdrive-page')); } diff --git a/src/web/app/desktop/tags/index.js b/src/web/app/desktop/tags/index.js index 0b92d8c23..37fdfe37e 100644 --- a/src/web/app/desktop/tags/index.js +++ b/src/web/app/desktop/tags/index.js @@ -61,8 +61,6 @@ require('./pages/user.tag'); require('./pages/post.tag'); require('./pages/search.tag'); require('./pages/not-found.tag'); -require('./pages/channel.tag'); -require('./pages/channels.tag'); require('./pages/selectdrive.tag'); require('./autocomplete-suggestion.tag'); require('./progress-dialog.tag'); diff --git a/src/web/app/desktop/tags/pages/channels.tag b/src/web/app/desktop/tags/pages/channels.tag deleted file mode 100644 index 220f1ca50..000000000 --- a/src/web/app/desktop/tags/pages/channels.tag +++ /dev/null @@ -1,28 +0,0 @@ - - -
      - -
      -
      - - -
      diff --git a/src/web/app/desktop/tags/timeline.tag b/src/web/app/desktop/tags/timeline.tag index 17b2c66dc..64b64f902 100644 --- a/src/web/app/desktop/tags/timeline.tag +++ b/src/web/app/desktop/tags/timeline.tag @@ -112,7 +112,7 @@
      -

      { p.channel.title }:

      +

      { p.channel.title }:

      diff --git a/src/web/app/desktop/tags/ui.tag b/src/web/app/desktop/tags/ui.tag index 7527358dc..3123c34f4 100644 --- a/src/web/app/desktop/tags/ui.tag +++ b/src/web/app/desktop/tags/ui.tag @@ -335,10 +335,10 @@
    • -
    • - +
    • + -

      %i18n:desktop.tags.mk-ui-header-nav.channels%

      +

      %i18n:desktop.tags.mk-ui-header-nav.ch%

    • diff --git a/src/web/app/mobile/tags/timeline.tag b/src/web/app/mobile/tags/timeline.tag index b26a5cb10..ad18521df 100644 --- a/src/web/app/mobile/tags/timeline.tag +++ b/src/web/app/mobile/tags/timeline.tag @@ -164,7 +164,7 @@
      -

      { p.channel.title }:

      +

      { p.channel.title }:

      diff --git a/src/web/app/mobile/tags/ui.tag b/src/web/app/mobile/tags/ui.tag index fb8cbcdbd..b2d96f6b8 100644 --- a/src/web/app/mobile/tags/ui.tag +++ b/src/web/app/mobile/tags/ui.tag @@ -231,10 +231,11 @@
    • %i18n:mobile.tags.mk-ui-nav.messaging%
    • %i18n:mobile.tags.mk-ui-nav.settings%
    • diff --git a/webpack/webpack.config.ts b/webpack/webpack.config.ts index 5199285d5..066df1815 100644 --- a/webpack/webpack.config.ts +++ b/webpack/webpack.config.ts @@ -16,6 +16,7 @@ module.exports = langs.map(([lang, locale]) => { const entry = { desktop: './src/web/app/desktop/script.js', mobile: './src/web/app/mobile/script.js', + ch: './src/web/app/ch/script.js', stats: './src/web/app/stats/script.js', status: './src/web/app/status/script.js', dev: './src/web/app/dev/script.js', From bc529801122986b80266efe454f9bddc3431a389 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 1 Nov 2017 03:31:36 +0900 Subject: [PATCH 033/111] wip --- src/web/app/ch/tags/channel.tag | 2 +- src/web/app/mobile/router.js | 5 ++ src/web/app/mobile/tags/drive.tag | 6 +- src/web/app/mobile/tags/index.js | 1 + src/web/app/mobile/tags/page/selectdrive.tag | 83 ++++++++++++++++++++ 5 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 src/web/app/mobile/tags/page/selectdrive.tag diff --git a/src/web/app/ch/tags/channel.tag b/src/web/app/ch/tags/channel.tag index b16844b8b..d43113a55 100644 --- a/src/web/app/ch/tags/channel.tag +++ b/src/web/app/ch/tags/channel.tag @@ -11,7 +11,7 @@
      - Misskey ver { version } (葵 aoi) + Misskey ver { version } (葵 aoi)
      + + From 1dca32e84d2006081c9ca2b1d7c7d943ccabc814 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 1 Nov 2017 03:41:34 +0900 Subject: [PATCH 034/111] wip --- locales/en.yml | 3 +++ locales/ja.yml | 3 +++ src/web/app/ch/tags/channel.tag | 24 +++++++++++++++++++++--- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/locales/en.yml b/locales/en.yml index 643649b46..afb6d2f2f 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -360,6 +360,9 @@ desktop: mobile: tags: + mk-selectdrive-page: + select-file: "Select file(s)" + mk-drive-file-viewer: download: "Download" rename: "Rename" diff --git a/locales/ja.yml b/locales/ja.yml index 9fd7d94f0..03975556b 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -360,6 +360,9 @@ desktop: mobile: tags: + mk-selectdrive-page: + select-file: "ファイルを選択" + mk-drive-file-viewer: download: "ダウンロード" rename: "名前を変更" diff --git a/src/web/app/ch/tags/channel.tag b/src/web/app/ch/tags/channel.tag index d43113a55..e8537e3f0 100644 --- a/src/web/app/ch/tags/channel.tag +++ b/src/web/app/ch/tags/channel.tag @@ -1,9 +1,13 @@

      { channel.title }

      - - - +

      読み込み中

      +
      +

      > + + + +

      @@ -33,6 +37,7 @@ this.id = this.opts.id; this.fetching = true; + this.postsFetching = true; this.channel = null; this.posts = null; this.connection = new ChannelStream(this.id); @@ -60,6 +65,7 @@ channel_id: this.id }).then(posts => { this.update({ + postsFetching: false, posts: posts }); }); @@ -84,6 +90,7 @@
      { post.index }: { post.user.name } + ID:{ post.user.username }
      @@ -114,6 +121,17 @@ > mk-time margin-right 0.5em + &:first-of-type + display none + + @media (max-width 600px) + > mk-time + &:first-of-type + display initial + + &:last-of-type + display none + > div padding 0 0 1em 2em From 5fae9a89252387ebe42a6f70790351ca2ac9f55b Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 1 Nov 2017 03:42:50 +0900 Subject: [PATCH 035/111] wip --- src/web/app/ch/tags/channel.tag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web/app/ch/tags/channel.tag b/src/web/app/ch/tags/channel.tag index e8537e3f0..43a1f851f 100644 --- a/src/web/app/ch/tags/channel.tag +++ b/src/web/app/ch/tags/channel.tag @@ -3,7 +3,7 @@

      { channel.title }

      読み込み中

      -

      > +

      まだ投稿がありません

      From 250875e248b03fc74638bd6f15a24cd37897c4d3 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 1 Nov 2017 04:11:56 +0900 Subject: [PATCH 036/111] wip --- src/api/endpoints.ts | 3 ++ src/api/endpoints/channels.ts | 59 +++++++++++++++++++++++++++++++++ src/web/app/ch/tags/channel.tag | 7 ++-- src/web/app/ch/tags/index.tag | 13 ++++++-- 4 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 src/api/endpoints/channels.ts diff --git a/src/api/endpoints.ts b/src/api/endpoints.ts index 88c01d4e7..c4dacad85 100644 --- a/src/api/endpoints.ts +++ b/src/api/endpoints.ts @@ -490,6 +490,9 @@ const endpoints: Endpoint[] = [ { name: 'channels/posts' }, + { + name: 'channels' + }, ]; export default endpoints; diff --git a/src/api/endpoints/channels.ts b/src/api/endpoints/channels.ts new file mode 100644 index 000000000..e10c94389 --- /dev/null +++ b/src/api/endpoints/channels.ts @@ -0,0 +1,59 @@ +/** + * Module dependencies + */ +import $ from 'cafy'; +import Channel from '../models/channel'; +import serialize from '../serializers/channel'; + +/** + * Get all channels + * + * @param {any} params + * @param {any} me + * @return {Promise} + */ +module.exports = (params, me) => new Promise(async (res, rej) => { + // Get 'limit' parameter + const [limit = 10, limitErr] = $(params.limit).optional.number().range(1, 100).$; + if (limitErr) return rej('invalid limit param'); + + // Get 'since_id' parameter + const [sinceId, sinceIdErr] = $(params.since_id).optional.id().$; + if (sinceIdErr) return rej('invalid since_id param'); + + // Get 'max_id' parameter + const [maxId, maxIdErr] = $(params.max_id).optional.id().$; + if (maxIdErr) return rej('invalid max_id param'); + + // Check if both of since_id and max_id is specified + if (sinceId && maxId) { + return rej('cannot set since_id and max_id'); + } + + // Construct query + const sort = { + _id: -1 + }; + const query = {} as any; + if (sinceId) { + sort._id = 1; + query._id = { + $gt: sinceId + }; + } else if (maxId) { + query._id = { + $lt: maxId + }; + } + + // Issue query + const channels = await Channel + .find(query, { + limit: limit, + sort: sort + }); + + // Serialize + res(await Promise.all(channels.map(async channel => + await serialize(channel, me)))); +}); diff --git a/src/web/app/ch/tags/channel.tag b/src/web/app/ch/tags/channel.tag index 43a1f851f..12a6b5a3b 100644 --- a/src/web/app/ch/tags/channel.tag +++ b/src/web/app/ch/tags/channel.tag @@ -1,4 +1,6 @@ +
      Misskey Channels
      +

      { channel.title }

      読み込み中

      @@ -21,10 +23,9 @@ diff --git a/src/web/app/ch/tags/index.tag b/src/web/app/ch/tags/index.tag index 1c0a037c2..a64ddb6cc 100644 --- a/src/web/app/ch/tags/index.tag +++ b/src/web/app/ch/tags/index.tag @@ -1,5 +1,9 @@ - + +
      + From c73517b13581b4e5bb04a4b456afe501f4e7a98b Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 1 Nov 2017 13:25:43 +0900 Subject: [PATCH 060/111] v2793 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd8ecb57e..6a86e24c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ChangeLog (Release Notes) ========================= 主に notable な changes を書いていきます +2793 (2017/11/01) +----------------- +* なんか + 2783 (2017/11/01) ----------------- * なんか diff --git a/package.json b/package.json index eaafeb9fd..87db0c8e1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "misskey", "author": "syuilo ", - "version": "0.0.2783", + "version": "0.0.2793", "license": "MIT", "description": "A miniblog-based SNS", "bugs": "https://github.com/syuilo/misskey/issues", From c498d90b1b2216726383564875e758e00ceb4450 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 1 Nov 2017 13:39:05 +0900 Subject: [PATCH 061/111] wip --- src/api/endpoints/posts/create.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/api/endpoints/posts/create.ts b/src/api/endpoints/posts/create.ts index 360b5df0d..b3fbdf6fa 100644 --- a/src/api/endpoints/posts/create.ts +++ b/src/api/endpoints/posts/create.ts @@ -249,8 +249,11 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { } } - // Publish event to myself's stream - event(user._id, 'post', postObj); + // TODO + if (!channel) { + // Publish event to myself's stream + event(user._id, 'post', postObj); + } if (channel) { // Increment channel index(posts count) From b2a39694d5b3bb60cbf8c9331b8be25e6f80b5f8 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 1 Nov 2017 16:42:15 +0900 Subject: [PATCH 062/111] =?UTF-8?q?=E3=81=84=E3=81=84=E6=84=9F=E3=81=98?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/web/app/ch/tags/channel.tag | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/web/app/ch/tags/channel.tag b/src/web/app/ch/tags/channel.tag index 602b80bc1..fdc9ab4ce 100644 --- a/src/web/app/ch/tags/channel.tag +++ b/src/web/app/ch/tags/channel.tag @@ -182,12 +182,13 @@

      >>{ reply.index } ({ reply.user.name }): [x]

      - +
      +
      1. { name }
      @@ -202,6 +203,19 @@ this.mixin('api'); this.channel = this.opts.channel; + this.files = null; + + this.on('mount', () => { + this.refs.uploader.on('uploaded', file => { + this.update({ + files: [file] + }); + }); + }); + + this.upload = file => { + this.refs.uploader.upload(file); + }; this.clearReply = () => { this.update({ @@ -217,7 +231,7 @@ this.refs.text.value = ''; }; - this.post = e => { + this.post = () => { this.update({ wait: true }); @@ -250,5 +264,17 @@ }; window.open(CONFIG.url + '/selectdrive?multiple=true', '_blank'); }; + + this.onkeydown = e => { + if ((e.which == 10 || e.which == 13) && (e.ctrlKey || e.metaKey)) this.post(); + }; + + this.onpaste = e => { + e.clipboardData.items.forEach(item => { + if (item.kind == 'file') { + this.upload(item.getAsFile()); + } + }); + };
      From 56a9d2c35a56877a1f210242469d3733980ee62b Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 1 Nov 2017 16:42:51 +0900 Subject: [PATCH 063/111] v2795 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a86e24c6..9b2f3d7c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ChangeLog (Release Notes) ========================= 主に notable な changes を書いていきます +2795 (2017/11/01) +----------------- +* いい感じに + 2793 (2017/11/01) ----------------- * なんか diff --git a/package.json b/package.json index 87db0c8e1..09e5a6239 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "misskey", "author": "syuilo ", - "version": "0.0.2793", + "version": "0.0.2795", "license": "MIT", "description": "A miniblog-based SNS", "bugs": "https://github.com/syuilo/misskey/issues", From a5fea663f160d49b670e6097398a7adb22b25662 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 1 Nov 2017 16:48:00 +0900 Subject: [PATCH 064/111] Better progress bar --- src/web/app/ch/tags/channel.tag | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/web/app/ch/tags/channel.tag b/src/web/app/ch/tags/channel.tag index fdc9ab4ce..c6921e1a5 100644 --- a/src/web/app/ch/tags/channel.tag +++ b/src/web/app/ch/tags/channel.tag @@ -51,10 +51,18 @@ Progress.start(); + const fetched = false; + + // チャンネル概要読み込み this.api('channels/show', { channel_id: this.id }).then(channel => { - Progress.done(); + if (fetched) { + Progress.done(); + } else { + Progress.set(0.5); + fetched = true; + } this.update({ fetching: false, @@ -64,9 +72,17 @@ document.title = channel.title + ' | Misskey' }); + // 投稿読み込み this.api('channels/posts', { channel_id: this.id }).then(posts => { + if (fetched) { + Progress.done(); + } else { + Progress.set(0.5); + fetched = true; + } + this.update({ postsFetching: false, posts: posts From 1cd75bfeb81261e49fbe6f6f4539615e337e9616 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 1 Nov 2017 17:09:24 +0900 Subject: [PATCH 065/111] Fix bug --- src/web/app/ch/tags/channel.tag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web/app/ch/tags/channel.tag b/src/web/app/ch/tags/channel.tag index c6921e1a5..67b012cb5 100644 --- a/src/web/app/ch/tags/channel.tag +++ b/src/web/app/ch/tags/channel.tag @@ -51,7 +51,7 @@ Progress.start(); - const fetched = false; + let fetched = false; // チャンネル概要読み込み this.api('channels/show', { From 24712b8031f37515982e25b1b0a82422e4f13028 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 1 Nov 2017 17:20:54 +0900 Subject: [PATCH 066/111] :v: --- src/web/app/ch/tags/channel.tag | 54 +++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/src/web/app/ch/tags/channel.tag b/src/web/app/ch/tags/channel.tag index 67b012cb5..ad254c98e 100644 --- a/src/web/app/ch/tags/channel.tag +++ b/src/web/app/ch/tags/channel.tag @@ -3,12 +3,20 @@

      { channel.title }

      -

      読み込み中

      -
      -

      まだ投稿がありません

      - - - + + + +
      +

      読み込み中

      +
      +

      まだ投稿がありません

      + + + +

      @@ -29,6 +37,14 @@ > h1 font-size 1.5em color #f00 + + > .share + > * + margin-right 4px + + > .body + margin 8px 0 0 0 + + + + + + + + + + + From 5631d4bd8f97a80296079aa7b15ee0f87425d3aa Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 1 Nov 2017 17:21:26 +0900 Subject: [PATCH 067/111] v2799 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b2f3d7c0..03282eb3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ChangeLog (Release Notes) ========================= 主に notable な changes を書いていきます +2799 (2017/11/01) +----------------- +* いい感じに + 2795 (2017/11/01) ----------------- * いい感じに diff --git a/package.json b/package.json index 09e5a6239..a45d3b36c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "misskey", "author": "syuilo ", - "version": "0.0.2795", + "version": "0.0.2799", "license": "MIT", "description": "A miniblog-based SNS", "bugs": "https://github.com/syuilo/misskey/issues", From 45945bdb9d5b7b58bddbe0de0736f53d8c7c6cb0 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 1 Nov 2017 19:33:08 +0900 Subject: [PATCH 068/111] Implement Channel Watching --- src/api/endpoints.ts | 8 ++++ src/api/endpoints/channels/create.ts | 11 ++++- src/api/endpoints/channels/unwatch.ts | 60 +++++++++++++++++++++++++++ src/api/endpoints/channels/watch.ts | 58 ++++++++++++++++++++++++++ src/api/endpoints/posts/create.ts | 43 ++++++++++++------- src/api/endpoints/posts/timeline.ts | 39 ++++++++++++----- src/api/models/channel-watching.ts | 3 ++ src/api/serializers/channel.ts | 22 ++++++++++ src/web/app/ch/tags/channel.tag | 27 ++++++++++++ 9 files changed, 244 insertions(+), 27 deletions(-) create mode 100644 src/api/endpoints/channels/unwatch.ts create mode 100644 src/api/endpoints/channels/watch.ts create mode 100644 src/api/models/channel-watching.ts diff --git a/src/api/endpoints.ts b/src/api/endpoints.ts index c4dacad85..afefce39e 100644 --- a/src/api/endpoints.ts +++ b/src/api/endpoints.ts @@ -490,6 +490,14 @@ const endpoints: Endpoint[] = [ { name: 'channels/posts' }, + { + name: 'channels/watch', + withCredential: true + }, + { + name: 'channels/unwatch', + withCredential: true + }, { name: 'channels' }, diff --git a/src/api/endpoints/channels/create.ts b/src/api/endpoints/channels/create.ts index e0c0e0192..a8d7c29dc 100644 --- a/src/api/endpoints/channels/create.ts +++ b/src/api/endpoints/channels/create.ts @@ -3,6 +3,7 @@ */ import $ from 'cafy'; import Channel from '../../models/channel'; +import Watching from '../../models/channel-watching'; import serialize from '../../serializers/channel'; /** @@ -22,9 +23,17 @@ module.exports = async (params, user) => new Promise(async (res, rej) => { created_at: new Date(), user_id: user._id, title: title, - index: 0 + index: 0, + watching_count: 1 }); // Response res(await serialize(channel)); + + // Create Watching + await Watching.insert({ + created_at: new Date(), + user_id: user._id, + channel_id: channel._id + }); }); diff --git a/src/api/endpoints/channels/unwatch.ts b/src/api/endpoints/channels/unwatch.ts new file mode 100644 index 000000000..19d3be118 --- /dev/null +++ b/src/api/endpoints/channels/unwatch.ts @@ -0,0 +1,60 @@ +/** + * Module dependencies + */ +import $ from 'cafy'; +import Channel from '../../models/channel'; +import Watching from '../../models/channel-watching'; + +/** + * Unwatch a channel + * + * @param {any} params + * @param {any} user + * @return {Promise} + */ +module.exports = (params, user) => new Promise(async (res, rej) => { + // Get 'channel_id' parameter + const [channelId, channelIdErr] = $(params.channel_id).id().$; + if (channelIdErr) return rej('invalid channel_id param'); + + //#region Fetch channel + const channel = await Channel.findOne({ + _id: channelId + }); + + if (channel === null) { + return rej('channel not found'); + } + //#endregion + + //#region Check whether not watching + const exist = await Watching.findOne({ + user_id: user._id, + channel_id: channel._id, + deleted_at: { $exists: false } + }); + + if (exist === null) { + return rej('already not watching'); + } + //#endregion + + // Delete watching + await Watching.update({ + _id: exist._id + }, { + $set: { + deleted_at: new Date() + } + }); + + // Send response + res(); + + // Decrement watching count + Channel.update(channel._id, { + $inc: { + watching_count: -1 + } + }); +}); diff --git a/src/api/endpoints/channels/watch.ts b/src/api/endpoints/channels/watch.ts new file mode 100644 index 000000000..030e0dd41 --- /dev/null +++ b/src/api/endpoints/channels/watch.ts @@ -0,0 +1,58 @@ +/** + * Module dependencies + */ +import $ from 'cafy'; +import Channel from '../../models/channel'; +import Watching from '../../models/channel-watching'; + +/** + * Watch a channel + * + * @param {any} params + * @param {any} user + * @return {Promise} + */ +module.exports = (params, user) => new Promise(async (res, rej) => { + // Get 'channel_id' parameter + const [channelId, channelIdErr] = $(params.channel_id).id().$; + if (channelIdErr) return rej('invalid channel_id param'); + + //#region Fetch channel + const channel = await Channel.findOne({ + _id: channelId + }); + + if (channel === null) { + return rej('channel not found'); + } + //#endregion + + //#region Check whether already watching + const exist = await Watching.findOne({ + user_id: user._id, + channel_id: channel._id, + deleted_at: { $exists: false } + }); + + if (exist !== null) { + return rej('already watching'); + } + //#endregion + + // Create Watching + await Watching.insert({ + created_at: new Date(), + user_id: user._id, + channel_id: channel._id + }); + + // Send response + res(); + + // Increment watching count + Channel.update(channel._id, { + $inc: { + watching_count: 1 + } + }); +}); diff --git a/src/api/endpoints/posts/create.ts b/src/api/endpoints/posts/create.ts index b3fbdf6fa..2326f7baf 100644 --- a/src/api/endpoints/posts/create.ts +++ b/src/api/endpoints/posts/create.ts @@ -10,6 +10,7 @@ import { default as Channel, IChannel } from '../../models/channel'; import Following from '../../models/following'; import DriveFile from '../../models/drive-file'; import Watching from '../../models/post-watching'; +import ChannelWatching from '../../models/channel-watching'; import serialize from '../../serializers/post'; import notify from '../../common/notify'; import watch from '../../common/watch-post'; @@ -249,26 +250,11 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { } } - // TODO + // タイムラインへの投稿 if (!channel) { // Publish event to myself's stream event(user._id, 'post', postObj); - } - if (channel) { - // Increment channel index(posts count) - Channel.update({ _id: channel._id }, { - $inc: { - index: 1 - } - }); - - // Publish event to channel - publishChannelStream(channel._id, 'post', postObj); - } - - // TODO - if (!channel) { // Fetch all followers const followers = await Following .find({ @@ -285,6 +271,31 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { event(following.follower_id, 'post', postObj)); } + // チャンネルへの投稿 + if (channel) { + // Increment channel index(posts count) + Channel.update({ _id: channel._id }, { + $inc: { + index: 1 + } + }); + + // Publish event to channel + publishChannelStream(channel._id, 'post', postObj); + + // Get channel watchers + const watches = await ChannelWatching.find({ + channel_id: channel._id, + // 削除されたドキュメントは除く + deleted_at: { $exists: false } + }); + + // チャンネルの視聴者(のタイムライン)に配信 + watches.forEach(w => { + event(w.user_id, 'post', postObj); + }); + } + // Increment my posts count User.update({ _id: user._id }, { $inc: { diff --git a/src/api/endpoints/posts/timeline.ts b/src/api/endpoints/posts/timeline.ts index fe096442b..aa5aff5ba 100644 --- a/src/api/endpoints/posts/timeline.ts +++ b/src/api/endpoints/posts/timeline.ts @@ -3,6 +3,7 @@ */ import $ from 'cafy'; import Post from '../../models/post'; +import ChannelWatching from '../../models/channel-watching'; import getFriends from '../../common/get-friends'; import serialize from '../../serializers/post'; @@ -32,26 +33,43 @@ module.exports = (params, user, app) => new Promise(async (res, rej) => { return rej('cannot set since_id and max_id'); } - // ID list of the user $self and other users who the user follows + // ID list of the user itself and other users who the user follows const followingIds = await getFriends(user._id); - // Construct query + // Watchしているチャンネルを取得 + const watches = await ChannelWatching.find({ + user_id: user._id, + // 削除されたドキュメントは除く + deleted_at: { $exists: false } + }); + + //#region Construct query const sort = { _id: -1 }; + const query = { - user_id: { - $in: followingIds - }, - // TODO $or: [{ - channel_id: { - $exists: false - } + // フォローしている人のタイムラインへの投稿 + user_id: { + $in: followingIds + }, + // 「タイムラインへの」投稿に限定するためにチャンネルが指定されていないもののみに限る + $or: [{ + channel_id: { + $exists: false + } + }, { + channel_id: null + }] }, { - channel_id: null + // Watchしているチャンネルへの投稿 + channel_id: { + $in: watches.map(w => w.channel_id) + } }] } as any; + if (sinceId) { sort._id = 1; query._id = { @@ -62,6 +80,7 @@ module.exports = (params, user, app) => new Promise(async (res, rej) => { $lt: maxId }; } + //#endregion // Issue query const timeline = await Post diff --git a/src/api/models/channel-watching.ts b/src/api/models/channel-watching.ts new file mode 100644 index 000000000..6184ae408 --- /dev/null +++ b/src/api/models/channel-watching.ts @@ -0,0 +1,3 @@ +import db from '../../db/mongodb'; + +export default db.get('channel_watching') as any; // fuck type definition diff --git a/src/api/serializers/channel.ts b/src/api/serializers/channel.ts index d4e16d6be..3cba39aa1 100644 --- a/src/api/serializers/channel.ts +++ b/src/api/serializers/channel.ts @@ -5,6 +5,7 @@ import * as mongo from 'mongodb'; import deepcopy = require('deepcopy'); import { IUser } from '../models/user'; import { default as Channel, IChannel } from '../models/channel'; +import Watching from '../models/channel-watching'; /** * Serialize a channel @@ -40,5 +41,26 @@ export default ( // Remove needless properties delete _channel.user_id; + // Me + const meId: mongo.ObjectID = me + ? mongo.ObjectID.prototype.isPrototypeOf(me) + ? me as mongo.ObjectID + : typeof me === 'string' + ? new mongo.ObjectID(me) + : (me as IUser)._id + : null; + + if (me) { + //#region Watchしているかどうか + const watch = await Watching.findOne({ + user_id: meId, + channel_id: _channel.id, + deleted_at: { $exists: false } + }); + + _channel.is_watching = watch !== null; + //#endregion + } + resolve(_channel); }); diff --git a/src/web/app/ch/tags/channel.tag b/src/web/app/ch/tags/channel.tag index ad254c98e..57cedf10d 100644 --- a/src/web/app/ch/tags/channel.tag +++ b/src/web/app/ch/tags/channel.tag @@ -4,6 +4,11 @@

      { channel.title }

      +
      +

      このチャンネルをウォッチしています ウォッチ解除

      +

      このチャンネルをウォッチする

      +
      +