Merge remote-tracking branch 'refs/remotes/origin/master' into bbs
This commit is contained in:
		
						commit
						0e95cdb04c
					
				
					 19 changed files with 526 additions and 393 deletions
				
			
		|  | @ -2,6 +2,10 @@ ChangeLog (Release Notes) | |||
| ========================= | ||||
| 主に notable な changes を書いていきます | ||||
| 
 | ||||
| 2752 (2017/10/30) | ||||
| ----------------- | ||||
| * New: 未読の通知がある場合アイコンを表示するように | ||||
| 
 | ||||
| 2747 (2017/10/25) | ||||
| ----------------- | ||||
| * Fix: 非ログイン状態ですべてのページが致命的な問題を発生させる (#89) | ||||
|  |  | |||
|  | @ -395,6 +395,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" | ||||
|  |  | |||
|  | @ -395,6 +395,7 @@ mobile: | |||
| 
 | ||||
|     mk-notifications-page: | ||||
|       notifications: "通知" | ||||
|       read-all: "すべての通知を既読にしますか?" | ||||
| 
 | ||||
|     mk-post-page: | ||||
|       title: "投稿" | ||||
|  |  | |||
|  | @ -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", | ||||
|  |  | |||
							
								
								
									
										52
									
								
								src/api/common/read-notification.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/api/common/read-notification.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -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'); | ||||
| 	} | ||||
| }); | ||||
|  | @ -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, | ||||
|  |  | |||
|  | @ -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); | ||||
| 	} | ||||
| }); | ||||
|  |  | |||
							
								
								
									
										23
									
								
								src/api/endpoints/notifications/get_unread_count.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/api/endpoints/notifications/get_unread_count.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||
| 	}); | ||||
| }); | ||||
|  | @ -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); | ||||
| }); | ||||
							
								
								
									
										32
									
								
								src/api/endpoints/notifications/mark_as_read_all.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/api/endpoints/notifications/mark_as_read_all.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -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'); | ||||
| }); | ||||
|  | @ -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; | ||||
| } | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -252,6 +252,12 @@ | |||
| 		}); | ||||
| 
 | ||||
| 		this.onNotification = notification => { | ||||
| 			// TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない | ||||
| 			this.stream.send({ | ||||
| 				type: 'read_notification', | ||||
| 				id: notification.id | ||||
| 			}); | ||||
| 
 | ||||
| 			this.notifications.unshift(notification); | ||||
| 			this.update(); | ||||
| 		}; | ||||
|  |  | |||
|  | @ -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'); | ||||
|  |  | |||
|  | @ -123,6 +123,12 @@ | |||
| 		}); | ||||
| 
 | ||||
| 		this.onNotification = notification => { | ||||
| 			// TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない | ||||
| 			this.stream.send({ | ||||
| 				type: 'read_notification', | ||||
| 				id: notification.id | ||||
| 			}); | ||||
| 
 | ||||
| 			this.notifications.unshift(notification); | ||||
| 			this.update(); | ||||
| 		}; | ||||
|  |  | |||
|  | @ -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> | ||||
|  |  | |||
|  | @ -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> | ||||
|  | @ -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> | ||||
|  | @ -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> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue