From caa47cb38cfc3950539c78ca2e70f2c50e815d2c Mon Sep 17 00:00:00 2001 From: syuilo <syuilotan@yahoo.co.jp> Date: Mon, 30 Oct 2017 22:12:10 +0900 Subject: [PATCH 1/2] =?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 ca41d016c1..bf5c1fcb2c 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 03d5306d3e..020813ddbb 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 b640f0f248..1b3058fe02 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 0000000000..3009cc5d08 --- /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<any>(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 f05762340c..29a97bcb8a 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 5575fb7412..607e0768a4 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 0000000000..9514e78713 --- /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<any>} + */ +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 5cce33e850..0000000000 --- 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<any>} - */ -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 0000000000..3550e344c4 --- /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<any>} + */ +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 1c1f429a0d..1065e8baaa 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 d5fe01c261..7c8f3bfec8 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 1046358ce9..a4f66105a8 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 c5aafd20ba..a79f4f7e7e 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 7370aa84d3..2e95990314 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 06a5be039f..743de04393 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', '<i class="fa fa-bell-o"></i>%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'); + }; </script> </mk-notifications-page> 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 10b44b2153..0000000000 --- a/src/web/app/mobile/tags/ui-header.tag +++ /dev/null @@ -1,156 +0,0 @@ -<mk-ui-header> - <mk-special-message/> - <div class="main"> - <div class="backdrop"></div> - <div class="content"> - <button class="nav" onclick={ parent.toggleDrawer }><i class="fa fa-bars"></i></button> - <i class="fa fa-circle" if={ hasUnreadMessagingMessages }></i> - <h1 ref="title">Misskey</h1> - <button if={ func } onclick={ func }><i class="fa fa-{ funcIcon }"></i></button> - </div> - </div> - <style> - :scope - $height = 48px - - display block - position fixed - top 0 - z-index 1024 - width 100% - box-shadow 0 1px 0 rgba(#000, 0.075) - - > .main - color rgba(#fff, 0.9) - - > .backdrop - position absolute - top 0 - z-index 1023 - width 100% - height $height - -webkit-backdrop-filter blur(12px) - backdrop-filter blur(12px) - background-color rgba(#1b2023, 0.75) - - > .content - z-index 1024 - - > h1 - display block - margin 0 auto - padding 0 - width 100% - max-width calc(100% - 112px) - text-align center - font-size 1.1em - font-weight normal - line-height $height - white-space nowrap - overflow hidden - text-overflow ellipsis - - > i - > .icon - margin-right 8px - - > img - display inline-block - vertical-align bottom - width ($height - 16px) - height ($height - 16px) - margin 8px - border-radius 6px - - > .nav - display block - position absolute - top 0 - left 0 - width $height - font-size 1.4em - line-height $height - border-right solid 1px rgba(#000, 0.1) - - > i - transition all 0.2s ease - - > i - position absolute - top 8px - left 8px - pointer-events none - font-size 10px - color $theme-color - - > button:last-child - display block - position absolute - top 0 - right 0 - width $height - text-align center - font-size 1.4em - color inherit - line-height $height - border-left solid 1px rgba(#000, 0.1) - - </style> - <script> - import ui from '../scripts/ui-event'; - - this.mixin('api'); - this.mixin('stream'); - - this.func = null; - this.funcIcon = null; - - this.on('mount', () => { - this.stream.on('read_all_messaging_messages', this.onReadAllMessagingMessages); - this.stream.on('unread_messaging_message', this.onUnreadMessagingMessage); - - // Fetch count of unread messaging messages - this.api('messaging/unread').then(res => { - if (res.count > 0) { - this.update({ - hasUnreadMessagingMessages: true - }); - } - }); - }); - - this.on('unmount', () => { - this.stream.off('read_all_messaging_messages', this.onReadAllMessagingMessages); - this.stream.off('unread_messaging_message', this.onUnreadMessagingMessage); - - ui.off('title', this.setTitle); - ui.off('func', this.setFunc); - }); - - this.onReadAllMessagingMessages = () => { - this.update({ - hasUnreadMessagingMessages: false - }); - }; - - this.onUnreadMessagingMessage = () => { - this.update({ - hasUnreadMessagingMessages: true - }); - }; - - this.setTitle = title => { - this.refs.title.innerHTML = title; - }; - - this.setFunc = (fn, icon) => { - this.update({ - func: fn, - funcIcon: icon - }); - }; - - ui.on('title', this.setTitle); - ui.on('func', this.setFunc); - </script> -</mk-ui-header> 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 34235ba4f1..0000000000 --- a/src/web/app/mobile/tags/ui-nav.tag +++ /dev/null @@ -1,170 +0,0 @@ -<mk-ui-nav> - <div class="backdrop" onclick={ parent.toggleDrawer }></div> - <div class="body"> - <a class="me" if={ SIGNIN } href={ '/' + I.username }> - <img class="avatar" src={ I.avatar_url + '?thumbnail&size=128' } alt="avatar"/> - <p class="name">{ I.name }</p> - </a> - <div class="links"> - <ul> - <li><a href="/"><i class="fa fa-home"></i>%i18n:mobile.tags.mk-ui-nav.home%<i class="fa fa-angle-right"></i></a></li> - <li><a href="/i/notifications"><i class="fa fa-bell-o"></i>%i18n:mobile.tags.mk-ui-nav.notifications%<i class="fa fa-angle-right"></i></a></li> - <li><a href="/i/messaging"><i class="fa fa-comments-o"></i>%i18n:mobile.tags.mk-ui-nav.messaging%<i class="i fa fa-circle" if={ hasUnreadMessagingMessages }></i><i class="fa fa-angle-right"></i></a></li> - </ul> - <ul> - <li><a onclick={ search }><i class="fa fa-search"></i>%i18n:mobile.tags.mk-ui-nav.search%<i class="fa fa-angle-right"></i></a></li> - </ul> - <ul> - <li><a href="/i/drive"><i class="fa fa-cloud"></i>%i18n:mobile.tags.mk-ui-nav.drive%<i class="fa fa-angle-right"></i></a></li> - </ul> - <ul> - <li><a href="/i/settings"><i class="fa fa-cog"></i>%i18n:mobile.tags.mk-ui-nav.settings%<i class="fa fa-angle-right"></i></a></li> - </ul> - </div> - <a href={ CONFIG.aboutUrl }><p class="about">%i18n:mobile.tags.mk-ui-nav.about%</p></a> - </div> - <style> - :scope - display none - - .backdrop - position fixed - top 0 - left 0 - z-index 1025 - width 100% - height 100% - background rgba(0, 0, 0, 0.2) - - .body - position fixed - top 0 - left 0 - z-index 1026 - width 240px - height 100% - overflow auto - -webkit-overflow-scrolling touch - color #777 - background #fff - - .me - display block - margin 0 - padding 16px - - .avatar - display inline - max-width 64px - border-radius 32px - vertical-align middle - - .name - display block - margin 0 16px - position absolute - top 0 - left 80px - padding 0 - width calc(100% - 112px) - color #777 - line-height 96px - overflow hidden - text-overflow ellipsis - white-space nowrap - - ul - display block - margin 16px 0 - padding 0 - list-style none - - &:first-child - margin-top 0 - - li - display block - font-size 1em - line-height 1em - - a - display block - padding 0 20px - line-height 3rem - line-height calc(1rem + 30px) - color #777 - text-decoration none - - > i:first-child - margin-right 0.5em - - > .i - margin-left 6px - vertical-align super - font-size 10px - color $theme-color - - > i:last-child - position absolute - top 0 - right 0 - padding 0 20px - font-size 1.2em - line-height calc(1rem + 30px) - color #ccc - - .about - margin 0 - padding 1em 0 - text-align center - font-size 0.8em - opacity 0.5 - - a - color #777 - - </style> - <script> - this.mixin('i'); - this.mixin('page'); - this.mixin('api'); - this.mixin('stream'); - - this.on('mount', () => { - this.stream.on('read_all_messaging_messages', this.onReadAllMessagingMessages); - this.stream.on('unread_messaging_message', this.onUnreadMessagingMessage); - - // Fetch count of unread messaging messages - this.api('messaging/unread').then(res => { - if (res.count > 0) { - this.update({ - hasUnreadMessagingMessages: true - }); - } - }); - }); - - this.on('unmount', () => { - this.stream.off('read_all_messaging_messages', this.onReadAllMessagingMessages); - this.stream.off('unread_messaging_message', this.onUnreadMessagingMessage); - }); - - this.onReadAllMessagingMessages = () => { - this.update({ - hasUnreadMessagingMessages: false - }); - }; - - this.onUnreadMessagingMessage = () => { - this.update({ - hasUnreadMessagingMessages: true - }); - }; - - this.search = () => { - const query = window.prompt('%i18n:mobile.tags.mk-ui-nav.search%'); - if (query == null || query == '') return; - this.page('/search:' + query); - }; - </script> -</mk-ui-nav> diff --git a/src/web/app/mobile/tags/ui.tag b/src/web/app/mobile/tags/ui.tag index 9d9cd4d74a..fb8cbcdbd2 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 }); }; </script> </mk-ui> + +<mk-ui-header> + <mk-special-message/> + <div class="main"> + <div class="backdrop"></div> + <div class="content"> + <button class="nav" onclick={ parent.toggleDrawer }><i class="fa fa-bars"></i></button> + <i class="fa fa-circle" if={ hasUnreadNotifications || hasUnreadMessagingMessages }></i> + <h1 ref="title">Misskey</h1> + <button if={ func } onclick={ func }><i class="fa fa-{ funcIcon }"></i></button> + </div> + </div> + <style> + :scope + $height = 48px + + display block + position fixed + top 0 + z-index 1024 + width 100% + box-shadow 0 1px 0 rgba(#000, 0.075) + + > .main + color rgba(#fff, 0.9) + + > .backdrop + position absolute + top 0 + z-index 1023 + width 100% + height $height + -webkit-backdrop-filter blur(12px) + backdrop-filter blur(12px) + background-color rgba(#1b2023, 0.75) + + > .content + z-index 1024 + + > h1 + display block + margin 0 auto + padding 0 + width 100% + max-width calc(100% - 112px) + text-align center + font-size 1.1em + font-weight normal + line-height $height + white-space nowrap + overflow hidden + text-overflow ellipsis + + > i + > .icon + margin-right 8px + + > img + display inline-block + vertical-align bottom + width ($height - 16px) + height ($height - 16px) + margin 8px + border-radius 6px + + > .nav + display block + position absolute + top 0 + left 0 + width $height + font-size 1.4em + line-height $height + border-right solid 1px rgba(#000, 0.1) + + > i + transition all 0.2s ease + + > i + position absolute + top 8px + left 8px + pointer-events none + font-size 10px + color $theme-color + + > button:last-child + display block + position absolute + top 0 + right 0 + width $height + text-align center + font-size 1.4em + color inherit + line-height $height + border-left solid 1px rgba(#000, 0.1) + + </style> + <script> + import ui from '../scripts/ui-event'; + + this.mixin('api'); + this.mixin('stream'); + + this.func = null; + this.funcIcon = null; + + this.on('mount', () => { + this.stream.on('read_all_notifications', this.onReadAllNotifications); + this.stream.on('read_all_messaging_messages', this.onReadAllMessagingMessages); + this.stream.on('unread_messaging_message', this.onUnreadMessagingMessage); + + // Fetch count of unread notifications + this.api('notifications/get_unread_count').then(res => { + if (res.count > 0) { + this.update({ + hasUnreadNotifications: true + }); + } + }); + + // Fetch count of unread messaging messages + this.api('messaging/unread').then(res => { + if (res.count > 0) { + this.update({ + hasUnreadMessagingMessages: true + }); + } + }); + }); + + this.on('unmount', () => { + this.stream.off('read_all_notifications', this.onReadAllNotifications); + this.stream.off('read_all_messaging_messages', this.onReadAllMessagingMessages); + this.stream.off('unread_messaging_message', this.onUnreadMessagingMessage); + + ui.off('title', this.setTitle); + ui.off('func', this.setFunc); + }); + + this.onReadAllNotifications = () => { + this.update({ + hasUnreadNotifications: false + }); + }; + + this.onReadAllMessagingMessages = () => { + this.update({ + hasUnreadMessagingMessages: false + }); + }; + + this.onUnreadMessagingMessage = () => { + this.update({ + hasUnreadMessagingMessages: true + }); + }; + + this.setTitle = title => { + this.refs.title.innerHTML = title; + }; + + this.setFunc = (fn, icon) => { + this.update({ + func: fn, + funcIcon: icon + }); + }; + + ui.on('title', this.setTitle); + ui.on('func', this.setFunc); + </script> +</mk-ui-header> + +<mk-ui-nav> + <div class="backdrop" onclick={ parent.toggleDrawer }></div> + <div class="body"> + <a class="me" if={ SIGNIN } href={ '/' + I.username }> + <img class="avatar" src={ I.avatar_url + '?thumbnail&size=128' } alt="avatar"/> + <p class="name">{ I.name }</p> + </a> + <div class="links"> + <ul> + <li><a href="/"><i class="fa fa-home"></i>%i18n:mobile.tags.mk-ui-nav.home%<i class="fa fa-angle-right"></i></a></li> + <li><a href="/i/notifications"><i class="fa fa-bell-o"></i>%i18n:mobile.tags.mk-ui-nav.notifications%<i class="i fa fa-circle" if={ hasUnreadNotifications }></i><i class="fa fa-angle-right"></i></a></li> + <li><a href="/i/messaging"><i class="fa fa-comments-o"></i>%i18n:mobile.tags.mk-ui-nav.messaging%<i class="i fa fa-circle" if={ hasUnreadMessagingMessages }></i><i class="fa fa-angle-right"></i></a></li> + </ul> + <ul> + <li><a onclick={ search }><i class="fa fa-search"></i>%i18n:mobile.tags.mk-ui-nav.search%<i class="fa fa-angle-right"></i></a></li> + </ul> + <ul> + <li><a href="/i/drive"><i class="fa fa-cloud"></i>%i18n:mobile.tags.mk-ui-nav.drive%<i class="fa fa-angle-right"></i></a></li> + </ul> + <ul> + <li><a href="/i/settings"><i class="fa fa-cog"></i>%i18n:mobile.tags.mk-ui-nav.settings%<i class="fa fa-angle-right"></i></a></li> + </ul> + </div> + <a href={ CONFIG.aboutUrl }><p class="about">%i18n:mobile.tags.mk-ui-nav.about%</p></a> + </div> + <style> + :scope + display none + + .backdrop + position fixed + top 0 + left 0 + z-index 1025 + width 100% + height 100% + background rgba(0, 0, 0, 0.2) + + .body + position fixed + top 0 + left 0 + z-index 1026 + width 240px + height 100% + overflow auto + -webkit-overflow-scrolling touch + color #777 + background #fff + + .me + display block + margin 0 + padding 16px + + .avatar + display inline + max-width 64px + border-radius 32px + vertical-align middle + + .name + display block + margin 0 16px + position absolute + top 0 + left 80px + padding 0 + width calc(100% - 112px) + color #777 + line-height 96px + overflow hidden + text-overflow ellipsis + white-space nowrap + + ul + display block + margin 16px 0 + padding 0 + list-style none + + &:first-child + margin-top 0 + + li + display block + font-size 1em + line-height 1em + + a + display block + padding 0 20px + line-height 3rem + line-height calc(1rem + 30px) + color #777 + text-decoration none + + > i:first-child + margin-right 0.5em + + > .i + margin-left 6px + vertical-align super + font-size 10px + color $theme-color + + > i:last-child + position absolute + top 0 + right 0 + padding 0 20px + font-size 1.2em + line-height calc(1rem + 30px) + color #ccc + + .about + margin 0 + padding 1em 0 + text-align center + font-size 0.8em + opacity 0.5 + + a + color #777 + + </style> + <script> + this.mixin('i'); + this.mixin('page'); + this.mixin('api'); + this.mixin('stream'); + + this.on('mount', () => { + this.stream.on('read_all_notifications', this.onReadAllNotifications); + this.stream.on('read_all_messaging_messages', this.onReadAllMessagingMessages); + this.stream.on('unread_messaging_message', this.onUnreadMessagingMessage); + + // Fetch count of unread notifications + this.api('notifications/get_unread_count').then(res => { + if (res.count > 0) { + this.update({ + hasUnreadNotifications: true + }); + } + }); + + // Fetch count of unread messaging messages + this.api('messaging/unread').then(res => { + if (res.count > 0) { + this.update({ + hasUnreadMessagingMessages: true + }); + } + }); + }); + + this.on('unmount', () => { + this.stream.off('read_all_notifications', this.onReadAllNotifications); + this.stream.off('read_all_messaging_messages', this.onReadAllMessagingMessages); + this.stream.off('unread_messaging_message', this.onUnreadMessagingMessage); + }); + + this.onReadAllNotifications = () => { + this.update({ + hasUnreadNotifications: false + }); + }; + + this.onReadAllMessagingMessages = () => { + this.update({ + hasUnreadMessagingMessages: false + }); + }; + + this.onUnreadMessagingMessage = () => { + this.update({ + hasUnreadMessagingMessages: true + }); + }; + + this.search = () => { + const query = window.prompt('%i18n:mobile.tags.mk-ui-nav.search%'); + if (query == null || query == '') return; + this.page('/search:' + query); + }; + </script> +</mk-ui-nav> From 460c6d448bc98a4006bda810fdb30a59f5955d65 Mon Sep 17 00:00:00 2001 From: syuilo <syuilotan@yahoo.co.jp> Date: Mon, 30 Oct 2017 22:12:52 +0900 Subject: [PATCH 2/2] v2752 --- CHANGELOG.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf5c1fcb2c..2f75462e5f 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 43a0159619..7a81bed7a6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "misskey", "author": "syuilo <i@syuilo.com>", - "version": "0.0.2747", + "version": "0.0.2752", "license": "MIT", "description": "A miniblog-based SNS", "bugs": "https://github.com/syuilo/misskey/issues",