diff --git a/docs/setup.en.md b/docs/setup.en.md index 3e48935346..dbc0599b5a 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 4f48a08088..602fd9b6a1 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 5c7a1165ba..643649b46c 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 dd76a2b900..9fd7d94f0b 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 183cabf135..34265dcbc3 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 9613a9f7cc..909b0d2556 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 0000000000..d67d77cbf4 --- /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 db600013b9..0e512fb210 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 46a93f5fef..18017e9740 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 0000000000..424158f403 --- /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 0000000000..760d405c52 --- /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 0000000000..2fc3ac3fca --- /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 a14c0648c4..b16844b8bc 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 38e7d91132..17944dbe45 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 75a7abba29..c5015622f0 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 df67bb7b7c..977e3fa9a6 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 0b92d8c236..37fdfe37e4 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 220f1ca50e..0000000000 --- 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 17b2c66dc8..64b64f902f 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 7527358dce..3123c34f4f 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 b26a5cb108..ad18521df6 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 fb8cbcdbd2..b2d96f6b8b 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 5199285d55..066df18157 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',