From 346c2959e058fa445ebb82e71eb37ef023ba6bd4 Mon Sep 17 00:00:00 2001 From: syuilo <syuilotan@yahoo.co.jp> Date: Wed, 1 Nov 2017 00:10:30 +0900 Subject: [PATCH] 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 45b83fc9e5..88c01d4e7f 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 0000000000..fa91fb93ee --- /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<any>} + */ +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 0000000000..38e7d91132 --- /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 4fa172f99d..8a3034f40c 100644 --- a/src/web/app/desktop/tags/pages/channel.tag +++ b/src/web/app/desktop/tags/pages/channel.tag @@ -2,6 +2,8 @@ <mk-ui ref="ui"> <main if={ !parent.fetching }> <h1>{ parent.channel.title }</h1> + <mk-channel-post each={ parent.posts } post={ this }/> + <mk-channel-form channel={ parent.channel }/> </main> </mk-ui> <style> @@ -14,12 +16,15 @@ </style> <script> import Progress from '../../../common/scripts/loading'; + import ChannelStream from '../../../common/scripts/channel-stream'; this.mixin('api'); this.id = this.opts.id; this.fetching = true; this.channel = null; + this.posts = null; + this.connection = new ChannelStream(); this.on('mount', () => { document.documentElement.style.background = '#efefef'; @@ -38,6 +43,88 @@ document.title = channel.title + ' | Misskey' }); + + this.api('channels/posts', { + channel_id: this.id + }).then(posts => { + this.update({ + posts: posts + }); + }); }); </script> </mk-channel-page> + +<mk-channel-post> + <header> + <b>{ post.user.name }</b> + </header> + <div> + { post.text } + </div> + <style> + :scope + display block + margin 0 + padding 0 + + > header + > b + color #008000 + + </style> + <script> + this.post = this.opts.post; + </script> +</mk-channel-post> + +<mk-channel-form> + <p if={ reply }>{ reply.user.name }への返信: (or <a onclick={ clearReply }>キャンセル</a>)</p> + <textarea ref="text" disabled={ wait }></textarea> + <button class={ wait: wait } ref="submit" disabled={ wait || (refs.text.value.length == 0) } onclick={ post }> + { wait ? 'やってます' : 'やる' }<mk-ellipsis if={ wait }/> + </button> + + <style> + :scope + display block + + </style> + <script> + this.mixin('api'); + + this.channel = this.opts.channel; + + this.clearReply = () => { + this.update({ + reply: null + }); + }; + + this.clear = () => { + this.clearReply(); + this.refs.text.value = ''; + }; + + this.post = e => { + this.update({ + wait: true + }); + + this.api('posts/create', { + text: this.refs.text.value, + reply_to_id: this.reply ? this.reply.id : undefined, + channel_id: this.channel.id + }).then(data => { + this.clear(); + }).catch(err => { + alert('失敗した'); + }).then(() => { + this.update({ + wait: false + }); + }); + }; + + </script> +</mk-channel-form> 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 0000000000..49741ad40c --- /dev/null +++ b/src/web/app/desktop/tags/pages/drive-chooser.tag @@ -0,0 +1,44 @@ +<mk-drive-chooser> + <mk-drive-browser ref="browser" multiple={ parent.multiple }/> + <div> + <button class="upload" title="PCからドライブにファイルをアップロード" onclick={ upload }><i class="fa fa-upload"></i></button> + <button class="cancel" onclick={ close }>キャンセル</button> + <button class="ok" onclick={ parent.ok }>決定</button> + </div> + + <style> + :scope + display block + height 100% + + </style> + <script> + this.multiple = this.opts.multiple != null ? this.opts.multiple : false; + + this.on('mount', () => { + this.refs.browser.on('selected', file => { + this.files = [file]; + this.ok(); + }); + + this.refs.browser.on('change-selection', files => { + this.update({ + files: files + }); + }); + }); + + this.upload = () => { + this.refs.browser.selectLocalFile(); + }; + + this.close = () => { + window.close(); + }; + + this.ok = () => { + window.opener.cb(this.multiple ? this.files : this.files[0]); + window.close(); + }; + </script> +</mk-drive-chooser>