Add messaging widget
This commit is contained in:
		| @@ -393,6 +393,9 @@ desktop: | |||||||
|     mk-access-log-home-widget: |     mk-access-log-home-widget: | ||||||
|       title: "Access log" |       title: "Access log" | ||||||
|  |  | ||||||
|  |     mk-messaging-home-widget: | ||||||
|  |       title: "Messaging" | ||||||
|  |  | ||||||
|     mk-repost-form: |     mk-repost-form: | ||||||
|       quote: "Quote..." |       quote: "Quote..." | ||||||
|       cancel: "Cancel" |       cancel: "Cancel" | ||||||
|   | |||||||
| @@ -393,6 +393,9 @@ desktop: | |||||||
|     mk-access-log-home-widget: |     mk-access-log-home-widget: | ||||||
|       title: "アクセスログ" |       title: "アクセスログ" | ||||||
|  |  | ||||||
|  |     mk-messaging-home-widget: | ||||||
|  |       title: "メッセージ" | ||||||
|  |  | ||||||
|     mk-repost-form: |     mk-repost-form: | ||||||
|       quote: "引用する..." |       quote: "引用する..." | ||||||
|       cancel: "キャンセル" |       cancel: "キャンセル" | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ import Message from '../models/messaging-message'; | |||||||
| import { IMessagingMessage as IMessage } from '../models/messaging-message'; | import { IMessagingMessage as IMessage } from '../models/messaging-message'; | ||||||
| import publishUserStream from '../event'; | import publishUserStream from '../event'; | ||||||
| import { publishMessagingStream } from '../event'; | import { publishMessagingStream } from '../event'; | ||||||
|  | import { publishMessagingIndexStream } from '../event'; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Mark as read message(s) |  * Mark as read message(s) | ||||||
| @@ -49,6 +50,7 @@ export default ( | |||||||
|  |  | ||||||
| 	// Publish event | 	// Publish event | ||||||
| 	publishMessagingStream(otherpartyId, userId, 'read', ids.map(id => id.toString())); | 	publishMessagingStream(otherpartyId, userId, 'read', ids.map(id => id.toString())); | ||||||
|  | 	publishMessagingIndexStream(userId, 'read', ids.map(id => id.toString())); | ||||||
|  |  | ||||||
| 	// Calc count of my unread messages | 	// Calc count of my unread messages | ||||||
| 	const count = await Message | 	const count = await Message | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import DriveFile from '../../../models/drive-file'; | |||||||
| import serialize from '../../../serializers/messaging-message'; | import serialize from '../../../serializers/messaging-message'; | ||||||
| import publishUserStream from '../../../event'; | import publishUserStream from '../../../event'; | ||||||
| import { publishMessagingStream } from '../../../event'; | import { publishMessagingStream } from '../../../event'; | ||||||
|  | import { publishMessagingIndexStream } from '../../../event'; | ||||||
| import config from '../../../../conf'; | import config from '../../../../conf'; | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -85,10 +86,12 @@ module.exports = (params, user) => new Promise(async (res, rej) => { | |||||||
|  |  | ||||||
| 	// 自分のストリーム | 	// 自分のストリーム | ||||||
| 	publishMessagingStream(message.user_id, message.recipient_id, 'message', messageObj); | 	publishMessagingStream(message.user_id, message.recipient_id, 'message', messageObj); | ||||||
|  | 	publishMessagingIndexStream(message.user_id, 'message', messageObj); | ||||||
| 	publishUserStream(message.user_id, 'messaging_message', messageObj); | 	publishUserStream(message.user_id, 'messaging_message', messageObj); | ||||||
|  |  | ||||||
| 	// 相手のストリーム | 	// 相手のストリーム | ||||||
| 	publishMessagingStream(message.recipient_id, message.user_id, 'message', messageObj); | 	publishMessagingStream(message.recipient_id, message.user_id, 'message', messageObj); | ||||||
|  | 	publishMessagingIndexStream(message.recipient_id, 'message', messageObj); | ||||||
| 	publishUserStream(message.recipient_id, 'messaging_message', messageObj); | 	publishUserStream(message.recipient_id, 'messaging_message', messageObj); | ||||||
|  |  | ||||||
| 	// 3秒経っても(今回作成した)メッセージが既読にならなかったら「未読のメッセージがありますよ」イベントを発行する | 	// 3秒経っても(今回作成した)メッセージが既読にならなかったら「未読のメッセージがありますよ」イベントを発行する | ||||||
|   | |||||||
| @@ -25,6 +25,10 @@ class MisskeyEvent { | |||||||
| 		this.publish(`messaging-stream:${userId}-${otherpartyId}`, type, typeof value === 'undefined' ? null : value); | 		this.publish(`messaging-stream:${userId}-${otherpartyId}`, type, typeof value === 'undefined' ? null : value); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	public publishMessagingIndexStream(userId: ID, type: string, value?: any): void { | ||||||
|  | 		this.publish(`messaging-index-stream:${userId}`, type, typeof value === 'undefined' ? null : value); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	public publishChannelStream(channelId: ID, type: string, value?: any): void { | 	public publishChannelStream(channelId: ID, type: string, value?: any): void { | ||||||
| 		this.publish(`channel-stream:${channelId}`, type, typeof value === 'undefined' ? null : value); | 		this.publish(`channel-stream:${channelId}`, type, typeof value === 'undefined' ? null : value); | ||||||
| 	} | 	} | ||||||
| @@ -46,4 +50,6 @@ export const publishPostStream = ev.publishPostStream.bind(ev); | |||||||
|  |  | ||||||
| export const publishMessagingStream = ev.publishMessagingStream.bind(ev); | export const publishMessagingStream = ev.publishMessagingStream.bind(ev); | ||||||
|  |  | ||||||
|  | export const publishMessagingIndexStream = ev.publishMessagingIndexStream.bind(ev); | ||||||
|  |  | ||||||
| export const publishChannelStream = ev.publishChannelStream.bind(ev); | export const publishChannelStream = ev.publishChannelStream.bind(ev); | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								src/api/stream/messaging-index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/api/stream/messaging-index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | import * as websocket from 'websocket'; | ||||||
|  | import * as redis from 'redis'; | ||||||
|  |  | ||||||
|  | export default function(request: websocket.request, connection: websocket.connection, subscriber: redis.RedisClient, user: any): void { | ||||||
|  | 	// Subscribe messaging index stream | ||||||
|  | 	subscriber.subscribe(`misskey:messaging-index-stream:${user._id}`); | ||||||
|  | 	subscriber.on('message', (_, data) => { | ||||||
|  | 		connection.send(data); | ||||||
|  | 	}); | ||||||
|  | } | ||||||
| @@ -8,6 +8,7 @@ import isNativeToken from './common/is-native-token'; | |||||||
|  |  | ||||||
| import homeStream from './stream/home'; | import homeStream from './stream/home'; | ||||||
| import messagingStream from './stream/messaging'; | import messagingStream from './stream/messaging'; | ||||||
|  | import messagingIndexStream from './stream/messaging-index'; | ||||||
| import serverStream from './stream/server'; | import serverStream from './stream/server'; | ||||||
| import requestsStream from './stream/requests'; | import requestsStream from './stream/requests'; | ||||||
| import channelStream from './stream/channel'; | import channelStream from './stream/channel'; | ||||||
| @@ -58,6 +59,7 @@ module.exports = (server: http.Server) => { | |||||||
| 		const channel = | 		const channel = | ||||||
| 			request.resourceURL.pathname === '/' ? homeStream : | 			request.resourceURL.pathname === '/' ? homeStream : | ||||||
| 			request.resourceURL.pathname === '/messaging' ? messagingStream : | 			request.resourceURL.pathname === '/messaging' ? messagingStream : | ||||||
|  | 			request.resourceURL.pathname === '/messaging-index' ? messagingIndexStream : | ||||||
| 			null; | 			null; | ||||||
|  |  | ||||||
| 		if (channel !== null) { | 		if (channel !== null) { | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ import activateMe from './i'; | |||||||
| import activateApi from './api'; | import activateApi from './api'; | ||||||
| import ServerStreamManager from '../scripts/server-stream-manager'; | import ServerStreamManager from '../scripts/server-stream-manager'; | ||||||
| import RequestsStreamManager from '../scripts/requests-stream-manager'; | import RequestsStreamManager from '../scripts/requests-stream-manager'; | ||||||
|  | import MessagingIndexStream from '../scripts/messaging-index-stream-manager'; | ||||||
|  |  | ||||||
| export default (me, stream) => { | export default (me, stream) => { | ||||||
| 	activateMe(me); | 	activateMe(me); | ||||||
| @@ -13,4 +14,5 @@ export default (me, stream) => { | |||||||
|  |  | ||||||
| 	(riot as any).mixin('server-stream', { serverStream: new ServerStreamManager() }); | 	(riot as any).mixin('server-stream', { serverStream: new ServerStreamManager() }); | ||||||
| 	(riot as any).mixin('requests-stream', { requestsStream: new RequestsStreamManager() }); | 	(riot as any).mixin('requests-stream', { requestsStream: new RequestsStreamManager() }); | ||||||
|  | 	(riot as any).mixin('messaging-index-stream', { messagingIndexStream: new MessagingIndexStream(me) }); | ||||||
| }; | }; | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								src/web/app/common/scripts/messaging-index-stream-manager.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/web/app/common/scripts/messaging-index-stream-manager.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | import StreamManager from './stream-manager'; | ||||||
|  | import Connection from './messaging-index-stream'; | ||||||
|  |  | ||||||
|  | export default class ServerStreamManager extends StreamManager<Connection> { | ||||||
|  | 	private me; | ||||||
|  |  | ||||||
|  | 	constructor(me) { | ||||||
|  | 		super(); | ||||||
|  |  | ||||||
|  | 		this.me = me; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public getConnection() { | ||||||
|  | 		if (this.connection == null) { | ||||||
|  | 			this.connection = new Connection(this.me); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return this.connection; | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								src/web/app/common/scripts/messaging-index-stream.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/web/app/common/scripts/messaging-index-stream.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | import Stream from './stream'; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Messaging index stream connection | ||||||
|  |  */ | ||||||
|  | class Connection extends Stream { | ||||||
|  | 	constructor(me) { | ||||||
|  | 		super('messaging-index', { | ||||||
|  | 			i: me.token | ||||||
|  | 		}); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export default Connection; | ||||||
| @@ -1,5 +1,5 @@ | |||||||
| <mk-messaging> | <mk-messaging data-compact={ opts.compact }> | ||||||
| 	<div class="search"> | 	<div class="search" if={ !opts.compact }> | ||||||
| 		<div class="form"> | 		<div class="form"> | ||||||
| 			<label for="search-input"><i class="fa fa-search"></i></label> | 			<label for="search-input"><i class="fa fa-search"></i></label> | ||||||
| 			<input ref="search" type="search" oninput={ search } onkeydown={ onSearchKeydown } placeholder="%i18n:common.tags.mk-messaging.search-user%"/> | 			<input ref="search" type="search" oninput={ search } onkeydown={ onSearchKeydown } placeholder="%i18n:common.tags.mk-messaging.search-user%"/> | ||||||
| @@ -36,6 +36,31 @@ | |||||||
| 		:scope | 		:scope | ||||||
| 			display block | 			display block | ||||||
|  |  | ||||||
|  | 			&[data-compact] | ||||||
|  | 				font-size 0.8em | ||||||
|  |  | ||||||
|  | 				> .history | ||||||
|  | 					> a | ||||||
|  | 						&:last-child | ||||||
|  | 							border-bottom none | ||||||
|  |  | ||||||
|  | 						&:not([data-is-me]):not([data-is-read]) | ||||||
|  | 							> div | ||||||
|  | 								background-image none | ||||||
|  | 								border-left solid 4px #3aa2dc | ||||||
|  |  | ||||||
|  | 						> div | ||||||
|  | 							padding 16px | ||||||
|  |  | ||||||
|  | 							> header | ||||||
|  | 								> mk-time | ||||||
|  | 									font-size 1em | ||||||
|  |  | ||||||
|  | 							> .avatar | ||||||
|  | 								width 42px | ||||||
|  | 								height 42px | ||||||
|  | 								margin 0 12px 0 0 | ||||||
|  |  | ||||||
| 			> .search | 			> .search | ||||||
| 				display block | 				display block | ||||||
| 				position -webkit-sticky | 				position -webkit-sticky | ||||||
| @@ -75,7 +100,7 @@ | |||||||
|  |  | ||||||
| 					> input | 					> input | ||||||
| 						margin 0 | 						margin 0 | ||||||
| 						padding 0 12px 0 38px | 						padding 0 0 0 38px | ||||||
| 						width 100% | 						width 100% | ||||||
| 						font-size 1em | 						font-size 1em | ||||||
| 						line-height 38px | 						line-height 38px | ||||||
| @@ -299,22 +324,59 @@ | |||||||
| 		this.mixin('i'); | 		this.mixin('i'); | ||||||
| 		this.mixin('api'); | 		this.mixin('api'); | ||||||
|  |  | ||||||
|  | 		this.mixin('messaging-index-stream'); | ||||||
|  | 		this.connection = this.messagingIndexStream.getConnection(); | ||||||
|  | 		this.connectionId = this.messagingIndexStream.use(); | ||||||
|  |  | ||||||
| 		this.searchResult = []; | 		this.searchResult = []; | ||||||
|  |  | ||||||
| 		this.on('mount', () => { | 		this.registerMessage = message => { | ||||||
| 			this.api('messaging/history').then(history => { | 			message.is_me = message.user_id == this.I.id; | ||||||
| 				this.isLoading = false; |  | ||||||
| 				history.forEach(message => { |  | ||||||
| 					message.is_me = message.user_id == this.I.id |  | ||||||
| 			message._click = () => { | 			message._click = () => { | ||||||
| 				this.trigger('navigate-user', message.is_me ? message.recipient : message.user); | 				this.trigger('navigate-user', message.is_me ? message.recipient : message.user); | ||||||
| 			}; | 			}; | ||||||
|  | 		}; | ||||||
|  |  | ||||||
|  | 		this.on('mount', () => { | ||||||
|  | 			this.connection.on('message', this.onMessage); | ||||||
|  | 			this.connection.on('read', this.onRead); | ||||||
|  |  | ||||||
|  | 			this.api('messaging/history').then(history => { | ||||||
|  | 				this.isLoading = false; | ||||||
|  | 				history.forEach(message => { | ||||||
|  | 					this.registerMessage(message); | ||||||
| 				}); | 				}); | ||||||
| 				this.history = history; | 				this.history = history; | ||||||
| 				this.update(); | 				this.update(); | ||||||
| 			}); | 			}); | ||||||
| 		}); | 		}); | ||||||
|  |  | ||||||
|  | 		this.on('unmount', () => { | ||||||
|  | 			this.connection.off('message', this.onMessage); | ||||||
|  | 			this.connection.off('read', this.onRead); | ||||||
|  | 			this.messagingIndexStream.dispose(this.connectionId); | ||||||
|  | 		}); | ||||||
|  |  | ||||||
|  | 		this.onMessage = message => { | ||||||
|  | 			this.history = this.history.filter(m => !( | ||||||
|  | 				(m.recipient_id == message.recipient_id && m.user_id == message.user_id) || | ||||||
|  | 				(m.recipient_id == message.user_id && m.user_id == message.recipient_id))); | ||||||
|  |  | ||||||
|  | 			this.registerMessage(message); | ||||||
|  |  | ||||||
|  | 			this.history.unshift(message); | ||||||
|  | 			this.update(); | ||||||
|  | 		}; | ||||||
|  |  | ||||||
|  | 		this.onRead = ids => { | ||||||
|  | 			ids.forEach(id => { | ||||||
|  | 				const found = this.history.find(m => m.id == id); | ||||||
|  | 				if (found) found.is_read = true; | ||||||
|  | 			}); | ||||||
|  |  | ||||||
|  | 			this.update(); | ||||||
|  | 		}; | ||||||
|  |  | ||||||
| 		this.search = () => { | 		this.search = () => { | ||||||
| 			const q = this.refs.search.value; | 			const q = this.refs.search.value; | ||||||
| 			if (q == '') { | 			if (q == '') { | ||||||
|   | |||||||
							
								
								
									
										50
									
								
								src/web/app/desktop/tags/home-widgets/messaging.tag
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/web/app/desktop/tags/home-widgets/messaging.tag
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | |||||||
|  | <mk-messaging-home-widget> | ||||||
|  | 	<virtual if={ data.design == 0 }> | ||||||
|  | 		<p class="title"><i class="fa fa-comments"></i>%i18n:desktop.tags.mk-messaging-home-widget.title%</p> | ||||||
|  | 	</virtual> | ||||||
|  | 	<mk-messaging ref="index" compact={ true }/> | ||||||
|  | 	<style> | ||||||
|  | 		:scope | ||||||
|  | 			display block | ||||||
|  | 			overflow hidden | ||||||
|  | 			background #fff | ||||||
|  |  | ||||||
|  | 			> .title | ||||||
|  | 				z-index 2 | ||||||
|  | 				margin 0 | ||||||
|  | 				padding 0 16px | ||||||
|  | 				line-height 42px | ||||||
|  | 				font-size 0.9em | ||||||
|  | 				font-weight bold | ||||||
|  | 				color #888 | ||||||
|  | 				box-shadow 0 1px rgba(0, 0, 0, 0.07) | ||||||
|  |  | ||||||
|  | 				> i | ||||||
|  | 					margin-right 4px | ||||||
|  |  | ||||||
|  | 			> mk-messaging | ||||||
|  | 				max-height 250px | ||||||
|  | 				overflow auto | ||||||
|  |  | ||||||
|  | 	</style> | ||||||
|  | 	<script> | ||||||
|  | 		this.data = { | ||||||
|  | 			design: 0 | ||||||
|  | 		}; | ||||||
|  |  | ||||||
|  | 		this.mixin('widget'); | ||||||
|  |  | ||||||
|  | 		this.on('mount', () => { | ||||||
|  | 			this.refs.index.on('navigate-user', user => { | ||||||
|  | 				riot.mount(document.body.appendChild(document.createElement('mk-messaging-room-window')), { | ||||||
|  | 					user: user | ||||||
|  | 				}); | ||||||
|  | 			}); | ||||||
|  | 		}); | ||||||
|  |  | ||||||
|  | 		this.func = () => { | ||||||
|  | 			if (++this.data.design == 2) this.data.design = 0; | ||||||
|  | 			this.save(); | ||||||
|  | 		}; | ||||||
|  | 	</script> | ||||||
|  | </mk-messaging-home-widget> | ||||||
| @@ -19,6 +19,7 @@ | |||||||
| 					<option value="user-recommendation">おすすめユーザー</option> | 					<option value="user-recommendation">おすすめユーザー</option> | ||||||
| 					<option value="recommended-polls">投票</option> | 					<option value="recommended-polls">投票</option> | ||||||
| 					<option value="post-form">投稿フォーム</option> | 					<option value="post-form">投稿フォーム</option> | ||||||
|  | 					<option value="messaging">メッセージ</option> | ||||||
| 					<option value="channel">チャンネル</option> | 					<option value="channel">チャンネル</option> | ||||||
| 					<option value="access-log">アクセスログ</option> | 					<option value="access-log">アクセスログ</option> | ||||||
| 					<option value="server">サーバー情報</option> | 					<option value="server">サーバー情報</option> | ||||||
|   | |||||||
| @@ -44,6 +44,7 @@ require('./home-widgets/channel.tag'); | |||||||
| require('./home-widgets/timemachine.tag'); | require('./home-widgets/timemachine.tag'); | ||||||
| require('./home-widgets/post-form.tag'); | require('./home-widgets/post-form.tag'); | ||||||
| require('./home-widgets/access-log.tag'); | require('./home-widgets/access-log.tag'); | ||||||
|  | require('./home-widgets/messaging.tag'); | ||||||
| require('./timeline.tag'); | require('./timeline.tag'); | ||||||
| require('./messaging/window.tag'); | require('./messaging/window.tag'); | ||||||
| require('./messaging/room-window.tag'); | require('./messaging/room-window.tag'); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 syuilo
					syuilo