From bc9a8283c66d7588f931d4b802f7ab1fa7aa3226 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 13 Nov 2017 18:05:35 +0900 Subject: なんかもうめっちゃ変えた MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 7 +- src/web/app/auth/script.js | 23 --- src/web/app/auth/script.ts | 23 +++ src/web/app/auth/tags/index.js | 2 - src/web/app/auth/tags/index.ts | 2 + src/web/app/ch/router.js | 32 ---- src/web/app/ch/router.ts | 32 ++++ src/web/app/ch/script.js | 18 -- src/web/app/ch/script.ts | 18 ++ src/web/app/ch/tags/channel.tag | 4 +- src/web/app/ch/tags/index.js | 3 - src/web/app/ch/tags/index.ts | 3 + src/web/app/common/mixins/api.js | 8 - src/web/app/common/mixins/api.ts | 8 + src/web/app/common/mixins/i.js | 20 --- src/web/app/common/mixins/i.ts | 20 +++ src/web/app/common/mixins/index.js | 13 -- src/web/app/common/mixins/index.ts | 14 ++ src/web/app/common/scripts/api.js | 46 ----- src/web/app/common/scripts/api.ts | 46 +++++ src/web/app/common/scripts/bytes-to-size.js | 6 - src/web/app/common/scripts/bytes-to-size.ts | 6 + src/web/app/common/scripts/channel-stream.js | 16 -- src/web/app/common/scripts/channel-stream.ts | 16 ++ src/web/app/common/scripts/check-for-update.js | 14 -- src/web/app/common/scripts/check-for-update.ts | 16 ++ src/web/app/common/scripts/config.js | 25 --- src/web/app/common/scripts/config.ts | 25 +++ src/web/app/common/scripts/contains.js | 8 - src/web/app/common/scripts/contains.ts | 8 + src/web/app/common/scripts/copy-to-clipboard.js | 13 -- src/web/app/common/scripts/copy-to-clipboard.ts | 13 ++ src/web/app/common/scripts/date-stringify.js | 13 -- src/web/app/common/scripts/date-stringify.ts | 13 ++ src/web/app/common/scripts/gcd.js | 2 - src/web/app/common/scripts/gcd.ts | 2 + src/web/app/common/scripts/get-kao.js | 5 - src/web/app/common/scripts/get-kao.ts | 5 + src/web/app/common/scripts/home-stream.js | 29 --- src/web/app/common/scripts/home-stream.ts | 29 +++ src/web/app/common/scripts/is-promise.js | 1 - src/web/app/common/scripts/is-promise.ts | 1 + src/web/app/common/scripts/loading.js | 21 --- src/web/app/common/scripts/loading.ts | 21 +++ src/web/app/common/scripts/messaging-stream.js | 23 --- src/web/app/common/scripts/messaging-stream.ts | 23 +++ .../app/common/scripts/server-stream-manager.ts | 31 +--- src/web/app/common/scripts/server-stream.js | 14 -- src/web/app/common/scripts/server-stream.ts | 14 ++ src/web/app/common/scripts/signout.js | 7 - src/web/app/common/scripts/signout.ts | 7 + src/web/app/common/scripts/stream-manager.ts | 33 ++++ src/web/app/common/scripts/stream.js | 100 ----------- src/web/app/common/scripts/stream.ts | 99 +++++++++++ src/web/app/common/scripts/text-compiler.js | 47 ----- src/web/app/common/scripts/text-compiler.ts | 47 +++++ src/web/app/common/tags/index.js | 30 ---- src/web/app/common/tags/index.ts | 30 ++++ src/web/app/common/tags/messaging/message.tag | 2 +- src/web/app/desktop/mixins/index.js | 2 - src/web/app/desktop/mixins/index.ts | 2 + src/web/app/desktop/mixins/user-preview.js | 66 ------- src/web/app/desktop/mixins/user-preview.ts | 66 +++++++ src/web/app/desktop/mixins/widget.js | 31 ---- src/web/app/desktop/mixins/widget.ts | 31 ++++ src/web/app/desktop/router.js | 99 ----------- src/web/app/desktop/router.ts | 99 +++++++++++ src/web/app/desktop/script.js | 91 ---------- src/web/app/desktop/script.ts | 91 ++++++++++ src/web/app/desktop/scripts/autocomplete.js | 130 -------------- src/web/app/desktop/scripts/autocomplete.ts | 132 ++++++++++++++ src/web/app/desktop/scripts/dialog.js | 16 -- src/web/app/desktop/scripts/dialog.ts | 16 ++ src/web/app/desktop/scripts/fuck-ad-block.js | 18 -- src/web/app/desktop/scripts/fuck-ad-block.ts | 20 +++ src/web/app/desktop/scripts/input-dialog.js | 12 -- src/web/app/desktop/scripts/input-dialog.ts | 12 ++ .../desktop/scripts/not-implemented-exception.js | 8 - .../desktop/scripts/not-implemented-exception.ts | 8 + src/web/app/desktop/scripts/notify.js | 8 - src/web/app/desktop/scripts/notify.ts | 8 + src/web/app/desktop/scripts/password-dialog.js | 11 -- src/web/app/desktop/scripts/password-dialog.ts | 11 ++ src/web/app/desktop/scripts/update-avatar.js | 87 --------- src/web/app/desktop/scripts/update-avatar.ts | 87 +++++++++ src/web/app/desktop/scripts/update-banner.js | 87 --------- src/web/app/desktop/scripts/update-banner.ts | 87 +++++++++ .../app/desktop/tags/autocomplete-suggestion.tag | 2 +- src/web/app/desktop/tags/drive/browser.tag | 4 +- src/web/app/desktop/tags/drive/folder.tag | 2 +- src/web/app/desktop/tags/drive/nav-folder.tag | 2 +- src/web/app/desktop/tags/index.js | 93 ---------- src/web/app/desktop/tags/index.ts | 93 ++++++++++ src/web/app/desktop/tags/post-detail-sub.tag | 2 +- src/web/app/desktop/tags/post-detail.tag | 2 +- src/web/app/desktop/tags/post-form.tag | 6 +- src/web/app/desktop/tags/sub-post-content.tag | 2 +- src/web/app/desktop/tags/timeline.tag | 2 +- src/web/app/dev/router.js | 42 ----- src/web/app/dev/router.ts | 42 +++++ src/web/app/dev/script.js | 18 -- src/web/app/dev/script.ts | 18 ++ src/web/app/dev/tags/index.js | 5 - src/web/app/dev/tags/index.ts | 5 + src/web/app/init.js | 196 --------------------- src/web/app/init.ts | 178 +++++++++++++++++++ src/web/app/mobile/router.js | 147 ---------------- src/web/app/mobile/router.ts | 147 ++++++++++++++++ src/web/app/mobile/script.js | 21 --- src/web/app/mobile/script.ts | 21 +++ src/web/app/mobile/scripts/open-post-form.js | 15 -- src/web/app/mobile/scripts/open-post-form.ts | 15 ++ src/web/app/mobile/scripts/ui-event.js | 5 - src/web/app/mobile/scripts/ui-event.ts | 5 + src/web/app/mobile/tags/drive.tag | 2 +- src/web/app/mobile/tags/index.js | 51 ------ src/web/app/mobile/tags/index.ts | 51 ++++++ src/web/app/mobile/tags/post-detail.tag | 2 +- src/web/app/mobile/tags/post-form.tag | 4 +- src/web/app/mobile/tags/sub-post-content.tag | 2 +- src/web/app/mobile/tags/timeline.tag | 2 +- src/web/app/stats/script.js | 23 --- src/web/app/stats/script.ts | 23 +++ src/web/app/stats/tags/index.js | 1 - src/web/app/stats/tags/index.ts | 1 + src/web/app/status/script.js | 23 --- src/web/app/status/script.ts | 23 +++ src/web/app/status/tags/index.js | 1 - src/web/app/status/tags/index.ts | 1 + tslint.json | 1 + webpack/webpack.config.ts | 21 ++- 131 files changed, 1910 insertions(+), 1905 deletions(-) delete mode 100644 src/web/app/auth/script.js create mode 100644 src/web/app/auth/script.ts delete mode 100644 src/web/app/auth/tags/index.js create mode 100644 src/web/app/auth/tags/index.ts delete mode 100644 src/web/app/ch/router.js create mode 100644 src/web/app/ch/router.ts delete mode 100644 src/web/app/ch/script.js create mode 100644 src/web/app/ch/script.ts delete mode 100644 src/web/app/ch/tags/index.js create mode 100644 src/web/app/ch/tags/index.ts delete mode 100644 src/web/app/common/mixins/api.js create mode 100644 src/web/app/common/mixins/api.ts delete mode 100644 src/web/app/common/mixins/i.js create mode 100644 src/web/app/common/mixins/i.ts delete mode 100644 src/web/app/common/mixins/index.js create mode 100644 src/web/app/common/mixins/index.ts delete mode 100644 src/web/app/common/scripts/api.js create mode 100644 src/web/app/common/scripts/api.ts delete mode 100644 src/web/app/common/scripts/bytes-to-size.js create mode 100644 src/web/app/common/scripts/bytes-to-size.ts delete mode 100644 src/web/app/common/scripts/channel-stream.js create mode 100644 src/web/app/common/scripts/channel-stream.ts delete mode 100644 src/web/app/common/scripts/check-for-update.js create mode 100644 src/web/app/common/scripts/check-for-update.ts delete mode 100644 src/web/app/common/scripts/config.js create mode 100644 src/web/app/common/scripts/config.ts delete mode 100644 src/web/app/common/scripts/contains.js create mode 100644 src/web/app/common/scripts/contains.ts delete mode 100644 src/web/app/common/scripts/copy-to-clipboard.js create mode 100644 src/web/app/common/scripts/copy-to-clipboard.ts delete mode 100644 src/web/app/common/scripts/date-stringify.js create mode 100644 src/web/app/common/scripts/date-stringify.ts delete mode 100644 src/web/app/common/scripts/gcd.js create mode 100644 src/web/app/common/scripts/gcd.ts delete mode 100644 src/web/app/common/scripts/get-kao.js create mode 100644 src/web/app/common/scripts/get-kao.ts delete mode 100644 src/web/app/common/scripts/home-stream.js create mode 100644 src/web/app/common/scripts/home-stream.ts delete mode 100644 src/web/app/common/scripts/is-promise.js create mode 100644 src/web/app/common/scripts/is-promise.ts delete mode 100644 src/web/app/common/scripts/loading.js create mode 100644 src/web/app/common/scripts/loading.ts delete mode 100644 src/web/app/common/scripts/messaging-stream.js create mode 100644 src/web/app/common/scripts/messaging-stream.ts delete mode 100644 src/web/app/common/scripts/server-stream.js create mode 100644 src/web/app/common/scripts/server-stream.ts delete mode 100644 src/web/app/common/scripts/signout.js create mode 100644 src/web/app/common/scripts/signout.ts create mode 100644 src/web/app/common/scripts/stream-manager.ts delete mode 100644 src/web/app/common/scripts/stream.js create mode 100644 src/web/app/common/scripts/stream.ts delete mode 100644 src/web/app/common/scripts/text-compiler.js create mode 100644 src/web/app/common/scripts/text-compiler.ts delete mode 100644 src/web/app/common/tags/index.js create mode 100644 src/web/app/common/tags/index.ts delete mode 100644 src/web/app/desktop/mixins/index.js create mode 100644 src/web/app/desktop/mixins/index.ts delete mode 100644 src/web/app/desktop/mixins/user-preview.js create mode 100644 src/web/app/desktop/mixins/user-preview.ts delete mode 100644 src/web/app/desktop/mixins/widget.js create mode 100644 src/web/app/desktop/mixins/widget.ts delete mode 100644 src/web/app/desktop/router.js create mode 100644 src/web/app/desktop/router.ts delete mode 100644 src/web/app/desktop/script.js create mode 100644 src/web/app/desktop/script.ts delete mode 100644 src/web/app/desktop/scripts/autocomplete.js create mode 100644 src/web/app/desktop/scripts/autocomplete.ts delete mode 100644 src/web/app/desktop/scripts/dialog.js create mode 100644 src/web/app/desktop/scripts/dialog.ts delete mode 100644 src/web/app/desktop/scripts/fuck-ad-block.js create mode 100644 src/web/app/desktop/scripts/fuck-ad-block.ts delete mode 100644 src/web/app/desktop/scripts/input-dialog.js create mode 100644 src/web/app/desktop/scripts/input-dialog.ts delete mode 100644 src/web/app/desktop/scripts/not-implemented-exception.js create mode 100644 src/web/app/desktop/scripts/not-implemented-exception.ts delete mode 100644 src/web/app/desktop/scripts/notify.js create mode 100644 src/web/app/desktop/scripts/notify.ts delete mode 100644 src/web/app/desktop/scripts/password-dialog.js create mode 100644 src/web/app/desktop/scripts/password-dialog.ts delete mode 100644 src/web/app/desktop/scripts/update-avatar.js create mode 100644 src/web/app/desktop/scripts/update-avatar.ts delete mode 100644 src/web/app/desktop/scripts/update-banner.js create mode 100644 src/web/app/desktop/scripts/update-banner.ts delete mode 100644 src/web/app/desktop/tags/index.js create mode 100644 src/web/app/desktop/tags/index.ts delete mode 100644 src/web/app/dev/router.js create mode 100644 src/web/app/dev/router.ts delete mode 100644 src/web/app/dev/script.js create mode 100644 src/web/app/dev/script.ts delete mode 100644 src/web/app/dev/tags/index.js create mode 100644 src/web/app/dev/tags/index.ts delete mode 100644 src/web/app/init.js create mode 100644 src/web/app/init.ts delete mode 100644 src/web/app/mobile/router.js create mode 100644 src/web/app/mobile/router.ts delete mode 100644 src/web/app/mobile/script.js create mode 100644 src/web/app/mobile/script.ts delete mode 100644 src/web/app/mobile/scripts/open-post-form.js create mode 100644 src/web/app/mobile/scripts/open-post-form.ts delete mode 100644 src/web/app/mobile/scripts/ui-event.js create mode 100644 src/web/app/mobile/scripts/ui-event.ts delete mode 100644 src/web/app/mobile/tags/index.js create mode 100644 src/web/app/mobile/tags/index.ts delete mode 100644 src/web/app/stats/script.js create mode 100644 src/web/app/stats/script.ts delete mode 100644 src/web/app/stats/tags/index.js create mode 100644 src/web/app/stats/tags/index.ts delete mode 100644 src/web/app/status/script.js create mode 100644 src/web/app/status/script.ts delete mode 100644 src/web/app/status/tags/index.js create mode 100644 src/web/app/status/tags/index.ts diff --git a/package.json b/package.json index 2861fd9f04..879f4af92e 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,9 @@ "@types/ms": "0.7.30", "@types/multer": "1.3.5", "@types/node": "8.0.49", + "@types/page": "1.5.32", + "@types/proxy-addr": "2.0.0", + "@types/seedrandom": "2.4.27", "@types/ratelimiter": "2.1.28", "@types/redis": "2.8.1", "@types/request": "2.0.7", @@ -92,8 +95,8 @@ "webpack": "3.8.1" }, "dependencies": { - "@prezzemolo/zip": "0.0.3", "@prezzemolo/rap": "0.1.2", + "@prezzemolo/zip": "0.0.3", "accesses": "2.5.0", "animejs": "2.2.0", "autwh": "0.0.1", @@ -131,6 +134,7 @@ "page": "1.7.1", "pictograph": "2.0.4", "prominence": "0.2.0", + "proxy-addr": "^2.0.2", "pug": "2.0.0-rc.4", "ratelimiter": "3.0.3", "recaptcha-promise": "0.1.3", @@ -141,6 +145,7 @@ "riot": "3.7.4", "rndstr": "1.0.0", "s-age": "1.1.0", + "seedrandom": "^2.4.3", "serve-favicon": "2.4.5", "sortablejs": "1.7.0", "summaly": "2.0.3", diff --git a/src/web/app/auth/script.js b/src/web/app/auth/script.js deleted file mode 100644 index fe7f9befe8..0000000000 --- a/src/web/app/auth/script.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Authorize Form - */ - -// Style -import './style.styl'; - -import * as riot from 'riot'; -require('./tags'); -import init from '../init'; - -document.title = 'Misskey | アプリの連携'; - -/** - * init - */ -init(me => { - mount(document.createElement('mk-index')); -}); - -function mount(content) { - riot.mount(document.getElementById('app').appendChild(content)); -} diff --git a/src/web/app/auth/script.ts b/src/web/app/auth/script.ts new file mode 100644 index 0000000000..fe7f9befe8 --- /dev/null +++ b/src/web/app/auth/script.ts @@ -0,0 +1,23 @@ +/** + * Authorize Form + */ + +// Style +import './style.styl'; + +import * as riot from 'riot'; +require('./tags'); +import init from '../init'; + +document.title = 'Misskey | アプリの連携'; + +/** + * init + */ +init(me => { + mount(document.createElement('mk-index')); +}); + +function mount(content) { + riot.mount(document.getElementById('app').appendChild(content)); +} diff --git a/src/web/app/auth/tags/index.js b/src/web/app/auth/tags/index.js deleted file mode 100644 index 42dffe67d9..0000000000 --- a/src/web/app/auth/tags/index.js +++ /dev/null @@ -1,2 +0,0 @@ -require('./index.tag'); -require('./form.tag'); diff --git a/src/web/app/auth/tags/index.ts b/src/web/app/auth/tags/index.ts new file mode 100644 index 0000000000..42dffe67d9 --- /dev/null +++ b/src/web/app/auth/tags/index.ts @@ -0,0 +1,2 @@ +require('./index.tag'); +require('./form.tag'); diff --git a/src/web/app/ch/router.js b/src/web/app/ch/router.js deleted file mode 100644 index 424158f403..0000000000 --- a/src/web/app/ch/router.js +++ /dev/null @@ -1,32 +0,0 @@ -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/router.ts b/src/web/app/ch/router.ts new file mode 100644 index 0000000000..fe014d4e31 --- /dev/null +++ b/src/web/app/ch/router.ts @@ -0,0 +1,32 @@ +import * as riot from 'riot'; +import * as route from '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 as any)(); +}; + +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 deleted file mode 100644 index 760d405c52..0000000000 --- a/src/web/app/ch/script.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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/script.ts b/src/web/app/ch/script.ts new file mode 100644 index 0000000000..760d405c52 --- /dev/null +++ b/src/web/app/ch/script.ts @@ -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/tags/channel.tag b/src/web/app/ch/tags/channel.tag index 4ae62e7b39..48c5c705d3 100644 --- a/src/web/app/ch/tags/channel.tag +++ b/src/web/app/ch/tags/channel.tag @@ -343,7 +343,7 @@ }; this.changeFile = () => { - this.refs.file.files.forEach(this.upload); + Array.from(this.refs.file.files).forEach(this.upload); }; this.selectFile = () => { @@ -367,7 +367,7 @@ }; this.onpaste = e => { - e.clipboardData.items.forEach(item => { + Array.from(e.clipboardData.items).forEach(item => { if (item.kind == 'file') { this.upload(item.getAsFile()); } diff --git a/src/web/app/ch/tags/index.js b/src/web/app/ch/tags/index.js deleted file mode 100644 index 12ffdaeb84..0000000000 --- a/src/web/app/ch/tags/index.js +++ /dev/null @@ -1,3 +0,0 @@ -require('./index.tag'); -require('./channel.tag'); -require('./header.tag'); diff --git a/src/web/app/ch/tags/index.ts b/src/web/app/ch/tags/index.ts new file mode 100644 index 0000000000..12ffdaeb84 --- /dev/null +++ b/src/web/app/ch/tags/index.ts @@ -0,0 +1,3 @@ +require('./index.tag'); +require('./channel.tag'); +require('./header.tag'); diff --git a/src/web/app/common/mixins/api.js b/src/web/app/common/mixins/api.js deleted file mode 100644 index 42d96db559..0000000000 --- a/src/web/app/common/mixins/api.js +++ /dev/null @@ -1,8 +0,0 @@ -import * as riot from 'riot'; -import api from '../scripts/api'; - -export default me => { - riot.mixin('api', { - api: api.bind(null, me ? me.token : null) - }); -}; diff --git a/src/web/app/common/mixins/api.ts b/src/web/app/common/mixins/api.ts new file mode 100644 index 0000000000..9726caf510 --- /dev/null +++ b/src/web/app/common/mixins/api.ts @@ -0,0 +1,8 @@ +import * as riot from 'riot'; +import api from '../scripts/api'; + +export default me => { + (riot as any).mixin('api', { + api: api.bind(null, me ? me.token : null) + }); +}; diff --git a/src/web/app/common/mixins/i.js b/src/web/app/common/mixins/i.js deleted file mode 100644 index 5225147766..0000000000 --- a/src/web/app/common/mixins/i.js +++ /dev/null @@ -1,20 +0,0 @@ -import * as riot from 'riot'; - -export default me => { - riot.mixin('i', { - init: function() { - this.I = me; - this.SIGNIN = me != null; - - if (this.SIGNIN) { - this.on('mount', () => { - me.on('updated', this.update); - }); - this.on('unmount', () => { - me.off('updated', this.update); - }); - } - }, - me: me - }); -}; diff --git a/src/web/app/common/mixins/i.ts b/src/web/app/common/mixins/i.ts new file mode 100644 index 0000000000..0879d02d3d --- /dev/null +++ b/src/web/app/common/mixins/i.ts @@ -0,0 +1,20 @@ +import * as riot from 'riot'; + +export default me => { + (riot as any).mixin('i', { + init: function() { + this.I = me; + this.SIGNIN = me != null; + + if (this.SIGNIN) { + this.on('mount', () => { + me.on('updated', this.update); + }); + this.on('unmount', () => { + me.off('updated', this.update); + }); + } + }, + me: me + }); +}; diff --git a/src/web/app/common/mixins/index.js b/src/web/app/common/mixins/index.js deleted file mode 100644 index 19e0690d72..0000000000 --- a/src/web/app/common/mixins/index.js +++ /dev/null @@ -1,13 +0,0 @@ -import * as riot from 'riot'; - -import activateMe from './i'; -import activateApi from './api'; - -export default (me, stream, serverStreamManager) => { - activateMe(me); - activateApi(me); - - riot.mixin('stream', { stream }); - - riot.mixin('server-stream', { serverStream: serverStreamManager }); -}; diff --git a/src/web/app/common/mixins/index.ts b/src/web/app/common/mixins/index.ts new file mode 100644 index 0000000000..45427fb9d3 --- /dev/null +++ b/src/web/app/common/mixins/index.ts @@ -0,0 +1,14 @@ +import * as riot from 'riot'; + +import activateMe from './i'; +import activateApi from './api'; +import ServerStreamManager from '../scripts/server-stream-manager'; + +export default (me, stream) => { + activateMe(me); + activateApi(me); + + (riot as any).mixin('stream', { stream }); + + (riot as any).mixin('server-stream', { serverStream: new ServerStreamManager() }); +}; diff --git a/src/web/app/common/scripts/api.js b/src/web/app/common/scripts/api.js deleted file mode 100644 index 4855f736c7..0000000000 --- a/src/web/app/common/scripts/api.js +++ /dev/null @@ -1,46 +0,0 @@ -/** - * API Request - */ - -import CONFIG from './config'; - -let spinner = null; -let pending = 0; - -/** - * Send a request to API - * @param {string|Object} i Credential - * @param {string} endpoint Endpoint - * @param {any} [data={}] Data - * @return {Promise} Response - */ -export default (i, endpoint, data = {}) => { - if (++pending === 1) { - spinner = document.createElement('div'); - spinner.setAttribute('id', 'wait'); - document.body.appendChild(spinner); - } - - // Append the credential - if (i != null) data.i = typeof i === 'object' ? i.token : i; - - return new Promise((resolve, reject) => { - // Send request - fetch(endpoint.indexOf('://') > -1 ? endpoint : `${CONFIG.apiUrl}/${endpoint}`, { - method: 'POST', - body: JSON.stringify(data), - credentials: endpoint === 'signin' ? 'include' : 'omit' - }).then(res => { - if (--pending === 0) spinner.parentNode.removeChild(spinner); - if (res.status === 200) { - res.json().then(resolve); - } else if (res.status === 204) { - resolve(); - } else { - res.json().then(err => { - reject(err.error); - }); - } - }).catch(reject); - }); -}; diff --git a/src/web/app/common/scripts/api.ts b/src/web/app/common/scripts/api.ts new file mode 100644 index 0000000000..2a9d78e87d --- /dev/null +++ b/src/web/app/common/scripts/api.ts @@ -0,0 +1,46 @@ +/** + * API Request + */ + +import CONFIG from './config'; + +let spinner = null; +let pending = 0; + +/** + * Send a request to API + * @param {string|Object} i Credential + * @param {string} endpoint Endpoint + * @param {any} [data={}] Data + * @return {Promise} Response + */ +export default (i, endpoint, data = {}): Promise => { + if (++pending === 1) { + spinner = document.createElement('div'); + spinner.setAttribute('id', 'wait'); + document.body.appendChild(spinner); + } + + // Append the credential + if (i != null) (data as any).i = typeof i === 'object' ? i.token : i; + + return new Promise((resolve, reject) => { + // Send request + fetch(endpoint.indexOf('://') > -1 ? endpoint : `${CONFIG.apiUrl}/${endpoint}`, { + method: 'POST', + body: JSON.stringify(data), + credentials: endpoint === 'signin' ? 'include' : 'omit' + }).then(res => { + if (--pending === 0) spinner.parentNode.removeChild(spinner); + if (res.status === 200) { + res.json().then(resolve); + } else if (res.status === 204) { + resolve(); + } else { + res.json().then(err => { + reject(err.error); + }); + } + }).catch(reject); + }); +}; diff --git a/src/web/app/common/scripts/bytes-to-size.js b/src/web/app/common/scripts/bytes-to-size.js deleted file mode 100644 index af0268dbd0..0000000000 --- a/src/web/app/common/scripts/bytes-to-size.js +++ /dev/null @@ -1,6 +0,0 @@ -export default (bytes, digits = 0) => { - var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; - if (bytes == 0) return '0Byte'; - var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); - return (bytes / Math.pow(1024, i)).toFixed(digits).replace(/\.0+$/, '') + sizes[i]; -}; diff --git a/src/web/app/common/scripts/bytes-to-size.ts b/src/web/app/common/scripts/bytes-to-size.ts new file mode 100644 index 0000000000..1d2b1e7ce3 --- /dev/null +++ b/src/web/app/common/scripts/bytes-to-size.ts @@ -0,0 +1,6 @@ +export default (bytes, digits = 0) => { + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; + if (bytes == 0) return '0Byte'; + const i = Math.floor(Math.log(bytes) / Math.log(1024)); + return (bytes / Math.pow(1024, i)).toFixed(digits).replace(/\.0+$/, '') + sizes[i]; +}; diff --git a/src/web/app/common/scripts/channel-stream.js b/src/web/app/common/scripts/channel-stream.js deleted file mode 100644 index 17944dbe45..0000000000 --- a/src/web/app/common/scripts/channel-stream.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; - -import Stream from './stream'; - -/** - * Channel stream connection - */ -class Connection extends Stream { - constructor(channelId) { - super('channel', { - channel: channelId - }); - } -} - -export default Connection; diff --git a/src/web/app/common/scripts/channel-stream.ts b/src/web/app/common/scripts/channel-stream.ts new file mode 100644 index 0000000000..17944dbe45 --- /dev/null +++ b/src/web/app/common/scripts/channel-stream.ts @@ -0,0 +1,16 @@ +'use strict'; + +import Stream from './stream'; + +/** + * Channel stream connection + */ +class Connection extends Stream { + constructor(channelId) { + super('channel', { + channel: channelId + }); + } +} + +export default Connection; diff --git a/src/web/app/common/scripts/check-for-update.js b/src/web/app/common/scripts/check-for-update.js deleted file mode 100644 index 7cb7839d29..0000000000 --- a/src/web/app/common/scripts/check-for-update.js +++ /dev/null @@ -1,14 +0,0 @@ -import CONFIG from './config'; - -export default function() { - fetch(CONFIG.apiUrl + '/meta', { - method: 'POST' - }).then(res => { - res.json().then(meta => { - if (meta.version != VERSION) { - localStorage.setItem('should-refresh', 'true'); - alert('%i18n:common.update-available%'.replace('{newer}', meta.version).replace('{current}', VERSION)); - } - }); - }); -}; diff --git a/src/web/app/common/scripts/check-for-update.ts b/src/web/app/common/scripts/check-for-update.ts new file mode 100644 index 0000000000..99d8b5d059 --- /dev/null +++ b/src/web/app/common/scripts/check-for-update.ts @@ -0,0 +1,16 @@ +import CONFIG from './config'; + +declare var VERSION: string; + +export default function() { + fetch(CONFIG.apiUrl + '/meta', { + method: 'POST' + }).then(res => { + res.json().then(meta => { + if (meta.version != VERSION) { + localStorage.setItem('should-refresh', 'true'); + alert('%i18n:common.update-available%'.replace('{newer}', meta.version).replace('{current}', VERSION)); + } + }); + }); +} diff --git a/src/web/app/common/scripts/config.js b/src/web/app/common/scripts/config.js deleted file mode 100644 index c5015622f0..0000000000 --- a/src/web/app/common/scripts/config.js +++ /dev/null @@ -1,25 +0,0 @@ -const Url = new URL(location.href); - -const isRoot = Url.host.split('.')[0] == 'misskey'; - -const host = isRoot ? Url.host : Url.host.substring(Url.host.indexOf('.') + 1, Url.host.length); -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}`; -const statusUrl = `${scheme}//status.${host}`; - -export default { - host, - scheme, - url, - apiUrl, - chUrl, - devUrl, - aboutUrl, - statsUrl, - statusUrl -}; diff --git a/src/web/app/common/scripts/config.ts b/src/web/app/common/scripts/config.ts new file mode 100644 index 0000000000..c5015622f0 --- /dev/null +++ b/src/web/app/common/scripts/config.ts @@ -0,0 +1,25 @@ +const Url = new URL(location.href); + +const isRoot = Url.host.split('.')[0] == 'misskey'; + +const host = isRoot ? Url.host : Url.host.substring(Url.host.indexOf('.') + 1, Url.host.length); +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}`; +const statusUrl = `${scheme}//status.${host}`; + +export default { + host, + scheme, + url, + apiUrl, + chUrl, + devUrl, + aboutUrl, + statsUrl, + statusUrl +}; diff --git a/src/web/app/common/scripts/contains.js b/src/web/app/common/scripts/contains.js deleted file mode 100644 index a5071b3f25..0000000000 --- a/src/web/app/common/scripts/contains.js +++ /dev/null @@ -1,8 +0,0 @@ -export default (parent, child) => { - let node = child.parentNode; - while (node) { - if (node == parent) return true; - node = node.parentNode; - } - return false; -}; diff --git a/src/web/app/common/scripts/contains.ts b/src/web/app/common/scripts/contains.ts new file mode 100644 index 0000000000..a5071b3f25 --- /dev/null +++ b/src/web/app/common/scripts/contains.ts @@ -0,0 +1,8 @@ +export default (parent, child) => { + let node = child.parentNode; + while (node) { + if (node == parent) return true; + node = node.parentNode; + } + return false; +}; diff --git a/src/web/app/common/scripts/copy-to-clipboard.js b/src/web/app/common/scripts/copy-to-clipboard.js deleted file mode 100644 index 3d2741f8d7..0000000000 --- a/src/web/app/common/scripts/copy-to-clipboard.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Clipboardに値をコピー(TODO: 文字列以外も対応) - */ -export default val => { - const form = document.createElement('textarea'); - form.textContent = val; - document.body.appendChild(form); - form.select(); - const result = document.execCommand('copy'); - document.body.removeChild(form); - - return result; -}; diff --git a/src/web/app/common/scripts/copy-to-clipboard.ts b/src/web/app/common/scripts/copy-to-clipboard.ts new file mode 100644 index 0000000000..3d2741f8d7 --- /dev/null +++ b/src/web/app/common/scripts/copy-to-clipboard.ts @@ -0,0 +1,13 @@ +/** + * Clipboardに値をコピー(TODO: 文字列以外も対応) + */ +export default val => { + const form = document.createElement('textarea'); + form.textContent = val; + document.body.appendChild(form); + form.select(); + const result = document.execCommand('copy'); + document.body.removeChild(form); + + return result; +}; diff --git a/src/web/app/common/scripts/date-stringify.js b/src/web/app/common/scripts/date-stringify.js deleted file mode 100644 index e51de8833d..0000000000 --- a/src/web/app/common/scripts/date-stringify.js +++ /dev/null @@ -1,13 +0,0 @@ -export default date => { - if (typeof date == 'string') date = new Date(date); - return ( - date.getFullYear() + '年' + - (date.getMonth() + 1) + '月' + - date.getDate() + '日' + - ' ' + - date.getHours() + '時' + - date.getMinutes() + '分' + - ' ' + - `(${['日', '月', '火', '水', '木', '金', '土'][date.getDay()]})` - ); -}; diff --git a/src/web/app/common/scripts/date-stringify.ts b/src/web/app/common/scripts/date-stringify.ts new file mode 100644 index 0000000000..e51de8833d --- /dev/null +++ b/src/web/app/common/scripts/date-stringify.ts @@ -0,0 +1,13 @@ +export default date => { + if (typeof date == 'string') date = new Date(date); + return ( + date.getFullYear() + '年' + + (date.getMonth() + 1) + '月' + + date.getDate() + '日' + + ' ' + + date.getHours() + '時' + + date.getMinutes() + '分' + + ' ' + + `(${['日', '月', '火', '水', '木', '金', '土'][date.getDay()]})` + ); +}; diff --git a/src/web/app/common/scripts/gcd.js b/src/web/app/common/scripts/gcd.js deleted file mode 100644 index 9a19f9da66..0000000000 --- a/src/web/app/common/scripts/gcd.js +++ /dev/null @@ -1,2 +0,0 @@ -const gcd = (a, b) => !b ? a : gcd(b, a % b); -export default gcd; diff --git a/src/web/app/common/scripts/gcd.ts b/src/web/app/common/scripts/gcd.ts new file mode 100644 index 0000000000..9a19f9da66 --- /dev/null +++ b/src/web/app/common/scripts/gcd.ts @@ -0,0 +1,2 @@ +const gcd = (a, b) => !b ? a : gcd(b, a % b); +export default gcd; diff --git a/src/web/app/common/scripts/get-kao.js b/src/web/app/common/scripts/get-kao.js deleted file mode 100644 index 0b77ee285a..0000000000 --- a/src/web/app/common/scripts/get-kao.js +++ /dev/null @@ -1,5 +0,0 @@ -export default () => [ - '(=^・・^=)', - 'v(‘ω’)v', - '🐡( '-' 🐡 )フグパンチ!!!!' -][Math.floor(Math.random() * 3)]; diff --git a/src/web/app/common/scripts/get-kao.ts b/src/web/app/common/scripts/get-kao.ts new file mode 100644 index 0000000000..2168c5be88 --- /dev/null +++ b/src/web/app/common/scripts/get-kao.ts @@ -0,0 +1,5 @@ +export default () => [ + '(=^・・^=)', + 'v(‘ω’)v', + '🐡( \'-\' 🐡 )フグパンチ!!!!' +][Math.floor(Math.random() * 3)]; diff --git a/src/web/app/common/scripts/home-stream.js b/src/web/app/common/scripts/home-stream.js deleted file mode 100644 index de9ceb3b51..0000000000 --- a/src/web/app/common/scripts/home-stream.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -import Stream from './stream'; -import signout from './signout'; - -/** - * Home stream connection - */ -class Connection extends Stream { - constructor(me) { - super('', { - i: me.token - }); - - // 最終利用日時を更新するため定期的にaliveメッセージを送信 - setInterval(() => { - this.send({ type: 'alive' }); - }, 1000 * 60); - - this.on('i_updated', me.update); - - this.on('my_token_regenerated', () => { - alert('%i18n:common.my-token-regenerated%'); - signout(); - }); - } -} - -export default Connection; diff --git a/src/web/app/common/scripts/home-stream.ts b/src/web/app/common/scripts/home-stream.ts new file mode 100644 index 0000000000..c549f2b936 --- /dev/null +++ b/src/web/app/common/scripts/home-stream.ts @@ -0,0 +1,29 @@ +'use strict'; + +import Stream from './stream'; +import signout from './signout'; + +/** + * Home stream connection + */ +class Connection extends Stream { + constructor(me) { + super('', { + i: me.token + }); + + // 最終利用日時を更新するため定期的にaliveメッセージを送信 + setInterval(() => { + this.send({ type: 'alive' }); + }, 1000 * 60); + + (this as any).on('i_updated', me.update); + + (this as any).on('my_token_regenerated', () => { + alert('%i18n:common.my-token-regenerated%'); + signout(); + }); + } +} + +export default Connection; diff --git a/src/web/app/common/scripts/is-promise.js b/src/web/app/common/scripts/is-promise.js deleted file mode 100644 index 3b4cd70b49..0000000000 --- a/src/web/app/common/scripts/is-promise.js +++ /dev/null @@ -1 +0,0 @@ -export default x => typeof x.then == 'function'; diff --git a/src/web/app/common/scripts/is-promise.ts b/src/web/app/common/scripts/is-promise.ts new file mode 100644 index 0000000000..3b4cd70b49 --- /dev/null +++ b/src/web/app/common/scripts/is-promise.ts @@ -0,0 +1 @@ +export default x => typeof x.then == 'function'; diff --git a/src/web/app/common/scripts/loading.js b/src/web/app/common/scripts/loading.js deleted file mode 100644 index c48e626648..0000000000 --- a/src/web/app/common/scripts/loading.js +++ /dev/null @@ -1,21 +0,0 @@ -const NProgress = require('nprogress'); -NProgress.configure({ - trickleSpeed: 500, - showSpinner: false -}); - -const root = document.getElementsByTagName('html')[0]; - -export default { - start: () => { - root.classList.add('progress'); - NProgress.start(); - }, - done: () => { - root.classList.remove('progress'); - NProgress.done(); - }, - set: val => { - NProgress.set(val); - } -}; diff --git a/src/web/app/common/scripts/loading.ts b/src/web/app/common/scripts/loading.ts new file mode 100644 index 0000000000..c48e626648 --- /dev/null +++ b/src/web/app/common/scripts/loading.ts @@ -0,0 +1,21 @@ +const NProgress = require('nprogress'); +NProgress.configure({ + trickleSpeed: 500, + showSpinner: false +}); + +const root = document.getElementsByTagName('html')[0]; + +export default { + start: () => { + root.classList.add('progress'); + NProgress.start(); + }, + done: () => { + root.classList.remove('progress'); + NProgress.done(); + }, + set: val => { + NProgress.set(val); + } +}; diff --git a/src/web/app/common/scripts/messaging-stream.js b/src/web/app/common/scripts/messaging-stream.js deleted file mode 100644 index 261525d5f6..0000000000 --- a/src/web/app/common/scripts/messaging-stream.js +++ /dev/null @@ -1,23 +0,0 @@ -'use strict'; - -import Stream from './stream'; - -/** - * Messaging stream connection - */ -class Connection extends Stream { - constructor(me, otherparty) { - super('messaging', { - i: me.token, - otherparty - }); - - this.on('_connected_', () => { - this.send({ - i: me.token - }); - }); - } -} - -export default Connection; diff --git a/src/web/app/common/scripts/messaging-stream.ts b/src/web/app/common/scripts/messaging-stream.ts new file mode 100644 index 0000000000..63830f7b17 --- /dev/null +++ b/src/web/app/common/scripts/messaging-stream.ts @@ -0,0 +1,23 @@ +'use strict'; + +import Stream from './stream'; + +/** + * Messaging stream connection + */ +class Connection extends Stream { + constructor(me, otherparty) { + super('messaging', { + i: me.token, + otherparty + }); + + (this as any).on('_connected_', () => { + this.send({ + i: me.token + }); + }); + } +} + +export default Connection; diff --git a/src/web/app/common/scripts/server-stream-manager.ts b/src/web/app/common/scripts/server-stream-manager.ts index 54333c8cf5..a170daebb9 100644 --- a/src/web/app/common/scripts/server-stream-manager.ts +++ b/src/web/app/common/scripts/server-stream-manager.ts @@ -1,14 +1,7 @@ +import StreamManager from './stream-manager'; import Connection from './server-stream'; -import * as uuid from 'uuid'; - -export default class ServerStreamManager { - private connection = null; - - /** - * コネクションを必要としているユーザー - */ - private users = []; +export default class ServerStreamManager extends StreamManager { public getConnection() { if (this.connection == null) { this.connection = new Connection(); @@ -16,24 +9,4 @@ export default class ServerStreamManager { return this.connection; } - - public use() { - // ユーザーID生成 - const userId = uuid(); - - this.users.push(userId); - - return userId; - } - - public dispose(userId) { - this.users = this.users.filter(id => id != userId); - - // 誰もコネクションの利用者がいなくなったら - if (this.users.length == 0) { - // コネクションを切断する - this.connection.close(); - this.connection = null; - } - } } diff --git a/src/web/app/common/scripts/server-stream.js b/src/web/app/common/scripts/server-stream.js deleted file mode 100644 index a1c466b35d..0000000000 --- a/src/web/app/common/scripts/server-stream.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -import Stream from './stream'; - -/** - * Server stream connection - */ -class Connection extends Stream { - constructor() { - super('server'); - } -} - -export default Connection; diff --git a/src/web/app/common/scripts/server-stream.ts b/src/web/app/common/scripts/server-stream.ts new file mode 100644 index 0000000000..a1c466b35d --- /dev/null +++ b/src/web/app/common/scripts/server-stream.ts @@ -0,0 +1,14 @@ +'use strict'; + +import Stream from './stream'; + +/** + * Server stream connection + */ +class Connection extends Stream { + constructor() { + super('server'); + } +} + +export default Connection; diff --git a/src/web/app/common/scripts/signout.js b/src/web/app/common/scripts/signout.js deleted file mode 100644 index 6c95cfbc9c..0000000000 --- a/src/web/app/common/scripts/signout.js +++ /dev/null @@ -1,7 +0,0 @@ -import CONFIG from './config'; - -export default () => { - localStorage.removeItem('me'); - document.cookie = `i=; domain=.${CONFIG.host}; expires=Thu, 01 Jan 1970 00:00:01 GMT;`; - location.href = '/'; -}; diff --git a/src/web/app/common/scripts/signout.ts b/src/web/app/common/scripts/signout.ts new file mode 100644 index 0000000000..6c95cfbc9c --- /dev/null +++ b/src/web/app/common/scripts/signout.ts @@ -0,0 +1,7 @@ +import CONFIG from './config'; + +export default () => { + localStorage.removeItem('me'); + document.cookie = `i=; domain=.${CONFIG.host}; expires=Thu, 01 Jan 1970 00:00:01 GMT;`; + location.href = '/'; +}; diff --git a/src/web/app/common/scripts/stream-manager.ts b/src/web/app/common/scripts/stream-manager.ts new file mode 100644 index 0000000000..4eaf0f9a45 --- /dev/null +++ b/src/web/app/common/scripts/stream-manager.ts @@ -0,0 +1,33 @@ +import * as uuid from 'uuid'; +import Connection from './stream'; + +export default abstract class StreamManager { + protected connection: T = null; + + /** + * コネクションを必要としているユーザー + */ + private users = []; + + public abstract getConnection(): T; + + public use() { + // ユーザーID生成 + const userId = uuid(); + + this.users.push(userId); + + return userId; + } + + public dispose(userId) { + this.users = this.users.filter(id => id != userId); + + // 誰もコネクションの利用者がいなくなったら + if (this.users.length == 0) { + // コネクションを切断する + this.connection.close(); + this.connection = null; + } + } +} diff --git a/src/web/app/common/scripts/stream.js b/src/web/app/common/scripts/stream.js deleted file mode 100644 index a03b7bf200..0000000000 --- a/src/web/app/common/scripts/stream.js +++ /dev/null @@ -1,100 +0,0 @@ -'use strict'; - -import * as ReconnectingWebsocket from 'reconnecting-websocket'; -import * as riot from 'riot'; -import CONFIG from './config'; - -/** - * Misskey stream connection - */ -class Connection { - constructor(endpoint, params) { - // BIND ----------------------------------- - this.onOpen = this.onOpen.bind(this); - this.onClose = this.onClose.bind(this); - this.onMessage = this.onMessage.bind(this); - this.send = this.send.bind(this); - this.close = this.close.bind(this); - // ---------------------------------------- - - riot.observable(this); - - this.state = 'initializing'; - this.buffer = []; - - const host = CONFIG.apiUrl.replace('http', 'ws'); - const query = params - ? Object.keys(params) - .map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k])) - .join('&') - : null; - - this.socket = new ReconnectingWebsocket(`${host}/${endpoint}${query ? '?' + query : ''}`); - this.socket.addEventListener('open', this.onOpen); - this.socket.addEventListener('close', this.onClose); - this.socket.addEventListener('message', this.onMessage); - } - - /** - * Callback of when open connection - * @private - */ - onOpen() { - this.state = 'connected'; - this.trigger('_connected_'); - - // バッファーを処理 - const _buffer = [].concat(this.buffer); // Shallow copy - this.buffer = []; // Clear buffer - _buffer.forEach(message => { - this.send(message); // Resend each buffered messages - }); - } - - /** - * Callback of when close connection - * @private - */ - onClose() { - this.state = 'reconnecting'; - this.trigger('_closed_'); - } - - /** - * Callback of when received a message from connection - * @private - */ - onMessage(message) { - try { - const msg = JSON.parse(message.data); - if (msg.type) this.trigger(msg.type, msg.body); - } catch(e) { - // noop - } - } - - /** - * Send a message to connection - * @public - */ - send(message) { - // まだ接続が確立されていなかったらバッファリングして次に接続した時に送信する - if (this.state != 'connected') { - this.buffer.push(message); - return; - }; - - this.socket.send(JSON.stringify(message)); - } - - /** - * Close this connection - * @public - */ - close() { - this.socket.removeEventListener('open', this.onOpen); - this.socket.removeEventListener('message', this.onMessage); - } -} - -export default Connection; diff --git a/src/web/app/common/scripts/stream.ts b/src/web/app/common/scripts/stream.ts new file mode 100644 index 0000000000..9595246879 --- /dev/null +++ b/src/web/app/common/scripts/stream.ts @@ -0,0 +1,99 @@ +'use strict'; + +import * as ReconnectingWebsocket from 'reconnecting-websocket'; +import * as riot from 'riot'; +import CONFIG from './config'; + +/** + * Misskey stream connection + */ +class Connection { + private state: string; + private buffer: any[]; + private socket: ReconnectingWebsocket; + + constructor(endpoint, params?) { + // BIND ----------------------------------- + this.onOpen = this.onOpen.bind(this); + this.onClose = this.onClose.bind(this); + this.onMessage = this.onMessage.bind(this); + this.send = this.send.bind(this); + this.close = this.close.bind(this); + // ---------------------------------------- + + riot.observable(this); + + this.state = 'initializing'; + this.buffer = []; + + const host = CONFIG.apiUrl.replace('http', 'ws'); + const query = params + ? Object.keys(params) + .map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k])) + .join('&') + : null; + + this.socket = new ReconnectingWebsocket(`${host}/${endpoint}${query ? '?' + query : ''}`); + this.socket.addEventListener('open', this.onOpen); + this.socket.addEventListener('close', this.onClose); + this.socket.addEventListener('message', this.onMessage); + } + + /** + * Callback of when open connection + */ + private onOpen() { + this.state = 'connected'; + (this as any).trigger('_connected_'); + + // バッファーを処理 + const _buffer = [].concat(this.buffer); // Shallow copy + this.buffer = []; // Clear buffer + _buffer.forEach(message => { + this.send(message); // Resend each buffered messages + }); + } + + /** + * Callback of when close connection + */ + private onClose() { + this.state = 'reconnecting'; + (this as any).trigger('_closed_'); + } + + /** + * Callback of when received a message from connection + */ + private onMessage(message) { + try { + const msg = JSON.parse(message.data); + if (msg.type) (this as any).trigger(msg.type, msg.body); + } catch (e) { + // noop + } + } + + /** + * Send a message to connection + */ + public send(message) { + // まだ接続が確立されていなかったらバッファリングして次に接続した時に送信する + if (this.state != 'connected') { + this.buffer.push(message); + return; + } + + this.socket.send(JSON.stringify(message)); + } + + /** + * Close this connection + */ + public close() { + this.socket.removeEventListener('open', this.onOpen); + this.socket.removeEventListener('message', this.onMessage); + } +} + +export default Connection; diff --git a/src/web/app/common/scripts/text-compiler.js b/src/web/app/common/scripts/text-compiler.js deleted file mode 100644 index 0a9b8022df..0000000000 --- a/src/web/app/common/scripts/text-compiler.js +++ /dev/null @@ -1,47 +0,0 @@ -import * as riot from 'riot'; -const pictograph = require('pictograph'); -import CONFIG from './config'; - -const escape = text => - text - .replace(/>/g, '>') - .replace(/ { - if (shouldBreak == null) { - shouldBreak = true; - } - - const me = riot.mixin('i').me; - - let text = tokens.map(token => { - switch (token.type) { - case 'text': - return escape(token.content) - .replace(/(\r\n|\n|\r)/g, shouldBreak ? '
' : ' '); - case 'bold': - return `${escape(token.bold)}`; - case 'url': - return ``; - case 'link': - return `${escape(token.title)}`; - case 'mention': - return `${token.content}`; - case 'hashtag': // TODO - return `${escape(token.content)}`; - case 'code': - return `
${token.html}
`; - case 'inline-code': - return `${token.html}`; - case 'emoji': - return pictograph.dic[token.emoji] || token.content; - } - }).join(''); - - // Remove needless whitespaces - text = text - .replace(/ /g, '').replace(/<\/code> /g, '') - .replace(/
/g, '
').replace(/<\/code><\/pre>
/g, '
'); - - return text; -}; diff --git a/src/web/app/common/scripts/text-compiler.ts b/src/web/app/common/scripts/text-compiler.ts new file mode 100644 index 0000000000..8c65d6a068 --- /dev/null +++ b/src/web/app/common/scripts/text-compiler.ts @@ -0,0 +1,47 @@ +import * as riot from 'riot'; +import * as pictograph from 'pictograph'; +import CONFIG from './config'; + +const escape = text => + text + .replace(/>/g, '>') + .replace(/ { + if (shouldBreak == null) { + shouldBreak = true; + } + + const me = (riot as any).mixin('i').me; + + let text = tokens.map(token => { + switch (token.type) { + case 'text': + return escape(token.content) + .replace(/(\r\n|\n|\r)/g, shouldBreak ? '
' : ' '); + case 'bold': + return `${escape(token.bold)}`; + case 'url': + return ``; + case 'link': + return `${escape(token.title)}`; + case 'mention': + return `${token.content}`; + case 'hashtag': // TODO + return `${escape(token.content)}`; + case 'code': + return `
${token.html}
`; + case 'inline-code': + return `${token.html}`; + case 'emoji': + return pictograph.dic[token.emoji] || token.content; + } + }).join(''); + + // Remove needless whitespaces + text = text + .replace(/ /g, '').replace(/<\/code> /g, '') + .replace(/
/g, '
').replace(/<\/code><\/pre>
/g, '
'); + + return text; +}; diff --git a/src/web/app/common/tags/index.js b/src/web/app/common/tags/index.js deleted file mode 100644 index 35a9f4586e..0000000000 --- a/src/web/app/common/tags/index.js +++ /dev/null @@ -1,30 +0,0 @@ -require('./error.tag'); -require('./url.tag'); -require('./url-preview.tag'); -require('./time.tag'); -require('./file-type-icon.tag'); -require('./uploader.tag'); -require('./ellipsis.tag'); -require('./raw.tag'); -require('./number.tag'); -require('./special-message.tag'); -require('./signin.tag'); -require('./signup.tag'); -require('./forkit.tag'); -require('./introduction.tag'); -require('./copyright.tag'); -require('./signin-history.tag'); -require('./twitter-setting.tag'); -require('./authorized-apps.tag'); -require('./poll.tag'); -require('./poll-editor.tag'); -require('./messaging/room.tag'); -require('./messaging/message.tag'); -require('./messaging/index.tag'); -require('./messaging/form.tag'); -require('./stream-indicator.tag'); -require('./activity-table.tag'); -require('./reaction-picker.tag'); -require('./reactions-viewer.tag'); -require('./reaction-icon.tag'); -require('./post-menu.tag'); diff --git a/src/web/app/common/tags/index.ts b/src/web/app/common/tags/index.ts new file mode 100644 index 0000000000..35a9f4586e --- /dev/null +++ b/src/web/app/common/tags/index.ts @@ -0,0 +1,30 @@ +require('./error.tag'); +require('./url.tag'); +require('./url-preview.tag'); +require('./time.tag'); +require('./file-type-icon.tag'); +require('./uploader.tag'); +require('./ellipsis.tag'); +require('./raw.tag'); +require('./number.tag'); +require('./special-message.tag'); +require('./signin.tag'); +require('./signup.tag'); +require('./forkit.tag'); +require('./introduction.tag'); +require('./copyright.tag'); +require('./signin-history.tag'); +require('./twitter-setting.tag'); +require('./authorized-apps.tag'); +require('./poll.tag'); +require('./poll-editor.tag'); +require('./messaging/room.tag'); +require('./messaging/message.tag'); +require('./messaging/index.tag'); +require('./messaging/form.tag'); +require('./stream-indicator.tag'); +require('./activity-table.tag'); +require('./reaction-picker.tag'); +require('./reactions-viewer.tag'); +require('./reaction-icon.tag'); +require('./post-menu.tag'); diff --git a/src/web/app/common/tags/messaging/message.tag b/src/web/app/common/tags/messaging/message.tag index d6db9070e2..ea1ea2310b 100644 --- a/src/web/app/common/tags/messaging/message.tag +++ b/src/web/app/common/tags/messaging/message.tag @@ -219,7 +219,7 @@ this.refs.text.innerHTML = compile(tokens); - this.refs.text.children.forEach(e => { + Array.from(this.refs.text.children).forEach(e => { if (e.tagName == 'MK-URL') riot.mount(e); }); diff --git a/src/web/app/desktop/mixins/index.js b/src/web/app/desktop/mixins/index.js deleted file mode 100644 index e0c94ec5ee..0000000000 --- a/src/web/app/desktop/mixins/index.js +++ /dev/null @@ -1,2 +0,0 @@ -require('./user-preview'); -require('./widget'); diff --git a/src/web/app/desktop/mixins/index.ts b/src/web/app/desktop/mixins/index.ts new file mode 100644 index 0000000000..e0c94ec5ee --- /dev/null +++ b/src/web/app/desktop/mixins/index.ts @@ -0,0 +1,2 @@ +require('./user-preview'); +require('./widget'); diff --git a/src/web/app/desktop/mixins/user-preview.js b/src/web/app/desktop/mixins/user-preview.js deleted file mode 100644 index 3f483beb3a..0000000000 --- a/src/web/app/desktop/mixins/user-preview.js +++ /dev/null @@ -1,66 +0,0 @@ -import * as riot from 'riot'; - -riot.mixin('user-preview', { - init: function() { - const scan = () => { - this.root.querySelectorAll('[data-user-preview]:not([data-user-preview-attached])') - .forEach(attach.bind(this)); - }; - this.on('mount', scan); - this.on('updated', scan); - } -}); - -function attach(el) { - el.setAttribute('data-user-preview-attached', true); - - const user = el.getAttribute('data-user-preview'); - let tag = null; - let showTimer = null; - let hideTimer = null; - - el.addEventListener('mouseover', () => { - clearTimeout(showTimer); - clearTimeout(hideTimer); - showTimer = setTimeout(show, 500); - }); - - el.addEventListener('mouseleave', () => { - clearTimeout(showTimer); - clearTimeout(hideTimer); - hideTimer = setTimeout(close, 500); - }); - - this.on('unmount', () => { - clearTimeout(showTimer); - clearTimeout(hideTimer); - close(); - }); - - const show = () => { - if (tag) return; - const preview = document.createElement('mk-user-preview'); - const rect = el.getBoundingClientRect(); - const x = rect.left + el.offsetWidth + window.pageXOffset; - const y = rect.top + window.pageYOffset; - preview.style.top = y + 'px'; - preview.style.left = x + 'px'; - preview.addEventListener('mouseover', () => { - clearTimeout(hideTimer); - }); - preview.addEventListener('mouseleave', () => { - clearTimeout(showTimer); - hideTimer = setTimeout(close, 500); - }); - tag = riot.mount(document.body.appendChild(preview), { - user: user - })[0]; - }; - - const close = () => { - if (tag) { - tag.close(); - tag = null; - } - }; -} diff --git a/src/web/app/desktop/mixins/user-preview.ts b/src/web/app/desktop/mixins/user-preview.ts new file mode 100644 index 0000000000..614de72bea --- /dev/null +++ b/src/web/app/desktop/mixins/user-preview.ts @@ -0,0 +1,66 @@ +import * as riot from 'riot'; + +riot.mixin('user-preview', { + init: function() { + const scan = () => { + this.root.querySelectorAll('[data-user-preview]:not([data-user-preview-attached])') + .forEach(attach.bind(this)); + }; + this.on('mount', scan); + this.on('updated', scan); + } +}); + +function attach(el) { + el.setAttribute('data-user-preview-attached', true); + + const user = el.getAttribute('data-user-preview'); + let tag = null; + let showTimer = null; + let hideTimer = null; + + el.addEventListener('mouseover', () => { + clearTimeout(showTimer); + clearTimeout(hideTimer); + showTimer = setTimeout(show, 500); + }); + + el.addEventListener('mouseleave', () => { + clearTimeout(showTimer); + clearTimeout(hideTimer); + hideTimer = setTimeout(close, 500); + }); + + this.on('unmount', () => { + clearTimeout(showTimer); + clearTimeout(hideTimer); + close(); + }); + + const show = () => { + if (tag) return; + const preview = document.createElement('mk-user-preview'); + const rect = el.getBoundingClientRect(); + const x = rect.left + el.offsetWidth + window.pageXOffset; + const y = rect.top + window.pageYOffset; + preview.style.top = y + 'px'; + preview.style.left = x + 'px'; + preview.addEventListener('mouseover', () => { + clearTimeout(hideTimer); + }); + preview.addEventListener('mouseleave', () => { + clearTimeout(showTimer); + hideTimer = setTimeout(close, 500); + }); + tag = (riot as any).mount(document.body.appendChild(preview), { + user: user + })[0]; + }; + + const close = () => { + if (tag) { + tag.close(); + tag = null; + } + }; +} diff --git a/src/web/app/desktop/mixins/widget.js b/src/web/app/desktop/mixins/widget.js deleted file mode 100644 index cb04295fc5..0000000000 --- a/src/web/app/desktop/mixins/widget.js +++ /dev/null @@ -1,31 +0,0 @@ -import * as riot from 'riot'; - -// ミックスインにオプションを渡せないのアレ -// SEE: https://github.com/riot/riot/issues/2434 - -riot.mixin('widget', { - init: function() { - this.mixin('i'); - this.mixin('api'); - - this.id = this.opts.id; - this.place = this.opts.place; - - if (this.data) { - Object.keys(this.data).forEach(prop => { - this.data[prop] = this.opts.data.hasOwnProperty(prop) ? this.opts.data[prop] : this.data[prop]; - }); - } - }, - - save: function() { - this.update(); - this.api('i/update_home', { - id: this.id, - data: this.data - }).then(() => { - this.I.client_settings.home.find(w => w.id == this.id).data = this.data; - this.I.update(); - }); - } -}); diff --git a/src/web/app/desktop/mixins/widget.ts b/src/web/app/desktop/mixins/widget.ts new file mode 100644 index 0000000000..04131cd8f0 --- /dev/null +++ b/src/web/app/desktop/mixins/widget.ts @@ -0,0 +1,31 @@ +import * as riot from 'riot'; + +// ミックスインにオプションを渡せないのアレ +// SEE: https://github.com/riot/riot/issues/2434 + +(riot as any).mixin('widget', { + init: function() { + this.mixin('i'); + this.mixin('api'); + + this.id = this.opts.id; + this.place = this.opts.place; + + if (this.data) { + Object.keys(this.data).forEach(prop => { + this.data[prop] = this.opts.data.hasOwnProperty(prop) ? this.opts.data[prop] : this.data[prop]; + }); + } + }, + + save: function() { + this.update(); + this.api('i/update_home', { + id: this.id, + data: this.data + }).then(() => { + this.I.client_settings.home.find(w => w.id == this.id).data = this.data; + this.I.update(); + }); + } +}); diff --git a/src/web/app/desktop/router.js b/src/web/app/desktop/router.js deleted file mode 100644 index 4675b967d6..0000000000 --- a/src/web/app/desktop/router.js +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Desktop App Router - */ - -import * as riot from 'riot'; -const route = require('page'); -let page = null; - -export default me => { - route('/', index); - route('/selectdrive', selectDrive); - route('/i/customize-home', customizeHome); - route('/i/drive', drive); - route('/i/drive/folder/:folder', drive); - route('/i/messaging/:user', messaging); - route('/i/mentions', mentions); - route('/post::post', post); - route('/search::query', search); - route('/:user', user.bind(null, 'home')); - route('/:user/graphs', user.bind(null, 'graphs')); - route('/:user/:post', post); - route('*', notFound); - - function index() { - me ? home() : entrance(); - } - - function home() { - mount(document.createElement('mk-home-page')); - } - - function customizeHome() { - mount(document.createElement('mk-home-customize-page')); - } - - function entrance() { - mount(document.createElement('mk-entrance')); - document.documentElement.setAttribute('data-page', 'entrance'); - } - - function mentions() { - const el = document.createElement('mk-home-page'); - el.setAttribute('mode', 'mentions'); - mount(el); - } - - function search(ctx) { - const el = document.createElement('mk-search-page'); - el.setAttribute('query', ctx.params.query); - mount(el); - } - - function user(page, ctx) { - const el = document.createElement('mk-user-page'); - el.setAttribute('user', ctx.params.user); - el.setAttribute('page', page); - mount(el); - } - - function post(ctx) { - const el = document.createElement('mk-post-page'); - el.setAttribute('post', ctx.params.post); - mount(el); - } - - function selectDrive() { - mount(document.createElement('mk-selectdrive-page')); - } - - function drive(ctx) { - const el = document.createElement('mk-drive-page'); - if (ctx.params.folder) el.setAttribute('folder', ctx.params.folder); - mount(el); - } - - function messaging(ctx) { - const el = document.createElement('mk-messaging-room-page'); - el.setAttribute('user', ctx.params.user); - mount(el); - } - - function notFound() { - mount(document.createElement('mk-not-found')); - } - - riot.mixin('page', { - page: route - }); - - // EXEC - route(); -}; - -function mount(content) { - document.documentElement.removeAttribute('data-page'); - if (page) page.unmount(); - const body = document.getElementById('app'); - page = riot.mount(body.appendChild(content))[0]; -} diff --git a/src/web/app/desktop/router.ts b/src/web/app/desktop/router.ts new file mode 100644 index 0000000000..a74299b281 --- /dev/null +++ b/src/web/app/desktop/router.ts @@ -0,0 +1,99 @@ +/** + * Desktop App Router + */ + +import * as riot from 'riot'; +import * as route from 'page'; +let page = null; + +export default me => { + route('/', index); + route('/selectdrive', selectDrive); + route('/i/customize-home', customizeHome); + route('/i/drive', drive); + route('/i/drive/folder/:folder', drive); + route('/i/messaging/:user', messaging); + route('/i/mentions', mentions); + route('/post::post', post); + route('/search::query', search); + route('/:user', user.bind(null, 'home')); + route('/:user/graphs', user.bind(null, 'graphs')); + route('/:user/:post', post); + route('*', notFound); + + function index() { + me ? home() : entrance(); + } + + function home() { + mount(document.createElement('mk-home-page')); + } + + function customizeHome() { + mount(document.createElement('mk-home-customize-page')); + } + + function entrance() { + mount(document.createElement('mk-entrance')); + document.documentElement.setAttribute('data-page', 'entrance'); + } + + function mentions() { + const el = document.createElement('mk-home-page'); + el.setAttribute('mode', 'mentions'); + mount(el); + } + + function search(ctx) { + const el = document.createElement('mk-search-page'); + el.setAttribute('query', ctx.params.query); + mount(el); + } + + function user(page, ctx) { + const el = document.createElement('mk-user-page'); + el.setAttribute('user', ctx.params.user); + el.setAttribute('page', page); + mount(el); + } + + function post(ctx) { + const el = document.createElement('mk-post-page'); + el.setAttribute('post', ctx.params.post); + mount(el); + } + + function selectDrive() { + mount(document.createElement('mk-selectdrive-page')); + } + + function drive(ctx) { + const el = document.createElement('mk-drive-page'); + if (ctx.params.folder) el.setAttribute('folder', ctx.params.folder); + mount(el); + } + + function messaging(ctx) { + const el = document.createElement('mk-messaging-room-page'); + el.setAttribute('user', ctx.params.user); + mount(el); + } + + function notFound() { + mount(document.createElement('mk-not-found')); + } + + (riot as any).mixin('page', { + page: route + }); + + // EXEC + (route as any)(); +}; + +function mount(content) { + document.documentElement.removeAttribute('data-page'); + if (page) page.unmount(); + const body = document.getElementById('app'); + page = riot.mount(body.appendChild(content))[0]; +} diff --git a/src/web/app/desktop/script.js b/src/web/app/desktop/script.js deleted file mode 100644 index 46a7fce700..0000000000 --- a/src/web/app/desktop/script.js +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Desktop Client - */ - -// Style -import './style.styl'; - -require('./tags'); -require('./mixins'); -import * as riot from 'riot'; -import init from '../init'; -import route from './router'; -import fuckAdBlock from './scripts/fuck-ad-block'; -import getPostSummary from '../../../common/get-post-summary.ts'; - -/** - * init - */ -init(async (me, stream) => { - /** - * Fuck AD Block - */ - fuckAdBlock(); - - /** - * Init Notification - */ - if ('Notification' in window) { - // 許可を得ていなかったらリクエスト - if (Notification.permission == 'default') { - await Notification.requestPermission(); - } - - if (Notification.permission == 'granted') { - registerNotifications(stream); - } - } - - // Start routing - route(me); -}); - -function registerNotifications(stream) { - if (stream == null) return; - - stream.on('drive_file_created', file => { - const n = new Notification('ファイルがアップロードされました', { - body: file.name, - icon: file.url + '?thumbnail&size=64' - }); - setTimeout(n.close.bind(n), 5000); - }); - - stream.on('mention', post => { - const n = new Notification(`${post.user.name}さんから:`, { - body: getPostSummary(post), - icon: post.user.avatar_url + '?thumbnail&size=64' - }); - setTimeout(n.close.bind(n), 6000); - }); - - stream.on('reply', post => { - const n = new Notification(`${post.user.name}さんから返信:`, { - body: getPostSummary(post), - icon: post.user.avatar_url + '?thumbnail&size=64' - }); - setTimeout(n.close.bind(n), 6000); - }); - - stream.on('quote', post => { - const n = new Notification(`${post.user.name}さんが引用:`, { - body: getPostSummary(post), - icon: post.user.avatar_url + '?thumbnail&size=64' - }); - setTimeout(n.close.bind(n), 6000); - }); - - stream.on('unread_messaging_message', message => { - const n = new Notification(`${message.user.name}さんからメッセージ:`, { - body: message.text, // TODO: getMessagingMessageSummary(message), - icon: message.user.avatar_url + '?thumbnail&size=64' - }); - n.onclick = () => { - n.close(); - riot.mount(document.body.appendChild(document.createElement('mk-messaging-room-window')), { - user: message.user - }); - }; - setTimeout(n.close.bind(n), 7000); - }); -} diff --git a/src/web/app/desktop/script.ts b/src/web/app/desktop/script.ts new file mode 100644 index 0000000000..a0453865ec --- /dev/null +++ b/src/web/app/desktop/script.ts @@ -0,0 +1,91 @@ +/** + * Desktop Client + */ + +// Style +import './style.styl'; + +require('./tags'); +require('./mixins'); +import * as riot from 'riot'; +import init from '../init'; +import route from './router'; +import fuckAdBlock from './scripts/fuck-ad-block'; +import getPostSummary from '../../../common/get-post-summary'; + +/** + * init + */ +init(async (me, stream) => { + /** + * Fuck AD Block + */ + fuckAdBlock(); + + /** + * Init Notification + */ + if ('Notification' in window) { + // 許可を得ていなかったらリクエスト + if ((Notification as any).permission == 'default') { + await Notification.requestPermission(); + } + + if ((Notification as any).permission == 'granted') { + registerNotifications(stream); + } + } + + // Start routing + route(me); +}); + +function registerNotifications(stream) { + if (stream == null) return; + + stream.on('drive_file_created', file => { + const n = new Notification('ファイルがアップロードされました', { + body: file.name, + icon: file.url + '?thumbnail&size=64' + }); + setTimeout(n.close.bind(n), 5000); + }); + + stream.on('mention', post => { + const n = new Notification(`${post.user.name}さんから:`, { + body: getPostSummary(post), + icon: post.user.avatar_url + '?thumbnail&size=64' + }); + setTimeout(n.close.bind(n), 6000); + }); + + stream.on('reply', post => { + const n = new Notification(`${post.user.name}さんから返信:`, { + body: getPostSummary(post), + icon: post.user.avatar_url + '?thumbnail&size=64' + }); + setTimeout(n.close.bind(n), 6000); + }); + + stream.on('quote', post => { + const n = new Notification(`${post.user.name}さんが引用:`, { + body: getPostSummary(post), + icon: post.user.avatar_url + '?thumbnail&size=64' + }); + setTimeout(n.close.bind(n), 6000); + }); + + stream.on('unread_messaging_message', message => { + const n = new Notification(`${message.user.name}さんからメッセージ:`, { + body: message.text, // TODO: getMessagingMessageSummary(message), + icon: message.user.avatar_url + '?thumbnail&size=64' + }); + n.onclick = () => { + n.close(); + (riot as any).mount(document.body.appendChild(document.createElement('mk-messaging-room-window')), { + user: message.user + }); + }; + setTimeout(n.close.bind(n), 7000); + }); +} diff --git a/src/web/app/desktop/scripts/autocomplete.js b/src/web/app/desktop/scripts/autocomplete.js deleted file mode 100644 index 8ca516e2a9..0000000000 --- a/src/web/app/desktop/scripts/autocomplete.js +++ /dev/null @@ -1,130 +0,0 @@ -const getCaretCoordinates = require('textarea-caret'); -import * as riot from 'riot'; - -/** - * オートコンプリートを管理するクラス。 - */ -class Autocomplete { - - /** - * 対象のテキストエリアを与えてインスタンスを初期化します。 - */ - constructor(textarea) { - // BIND --------------------------------- - this.onInput = this.onInput.bind(this); - this.complete = this.complete.bind(this); - this.close = this.close.bind(this); - // -------------------------------------- - - this.suggestion = null; - this.textarea = textarea; - } - - /** - * このインスタンスにあるテキストエリアの入力のキャプチャを開始します。 - */ - attach() { - this.textarea.addEventListener('input', this.onInput); - } - - /** - * このインスタンスにあるテキストエリアの入力のキャプチャを解除します。 - */ - detach() { - this.textarea.removeEventListener('input', this.onInput); - this.close(); - } - - /** - * [Private] テキスト入力時 - */ - onInput() { - this.close(); - - const caret = this.textarea.selectionStart; - const text = this.textarea.value.substr(0, caret); - - const mentionIndex = text.lastIndexOf('@'); - - if (mentionIndex == -1) return; - - const username = text.substr(mentionIndex + 1); - - if (!username.match(/^[a-zA-Z0-9-]+$/)) return; - - this.open('user', username); - } - - /** - * [Private] サジェストを提示します。 - */ - open(type, q) { - // 既に開いているサジェストは閉じる - this.close(); - - // サジェスト要素作成 - const tag = document.createElement('mk-autocomplete-suggestion'); - - // ~ サジェストを表示すべき位置を計算 ~ - - const caretPosition = getCaretCoordinates(this.textarea, this.textarea.selectionStart); - - const rect = this.textarea.getBoundingClientRect(); - - const x = rect.left + window.pageXOffset + caretPosition.left; - const y = rect.top + window.pageYOffset + caretPosition.top; - - tag.style.left = x + 'px'; - tag.style.top = y + 'px'; - - // 要素追加 - const el = document.body.appendChild(tag); - - // マウント - this.suggestion = riot.mount(el, { - textarea: this.textarea, - complete: this.complete, - close: this.close, - type: type, - q: q - })[0]; - } - - /** - * [Private] サジェストを閉じます。 - */ - close() { - if (this.suggestion == null) return; - - this.suggestion.unmount(); - this.suggestion = null; - - this.textarea.focus(); - } - - /** - * [Private] オートコンプリートする - */ - complete(user) { - this.close(); - - const value = user.username; - - const caret = this.textarea.selectionStart; - const source = this.textarea.value; - - const before = source.substr(0, caret); - const trimmedBefore = before.substring(0, before.lastIndexOf('@')); - const after = source.substr(caret); - - // 結果を挿入する - this.textarea.value = trimmedBefore + '@' + value + ' ' + after; - - // キャレットを戻す - this.textarea.focus(); - const pos = caret + value.length; - this.textarea.setSelectionRange(pos, pos); - } -} - -export default Autocomplete; diff --git a/src/web/app/desktop/scripts/autocomplete.ts b/src/web/app/desktop/scripts/autocomplete.ts new file mode 100644 index 0000000000..9df7aae08d --- /dev/null +++ b/src/web/app/desktop/scripts/autocomplete.ts @@ -0,0 +1,132 @@ +import getCaretCoordinates = require('textarea-caret'); +import * as riot from 'riot'; + +/** + * オートコンプリートを管理するクラス。 + */ +class Autocomplete { + private suggestion: any; + private textarea: any; + + /** + * 対象のテキストエリアを与えてインスタンスを初期化します。 + */ + constructor(textarea) { + // BIND --------------------------------- + this.onInput = this.onInput.bind(this); + this.complete = this.complete.bind(this); + this.close = this.close.bind(this); + // -------------------------------------- + + this.suggestion = null; + this.textarea = textarea; + } + + /** + * このインスタンスにあるテキストエリアの入力のキャプチャを開始します。 + */ + public attach() { + this.textarea.addEventListener('input', this.onInput); + } + + /** + * このインスタンスにあるテキストエリアの入力のキャプチャを解除します。 + */ + public detach() { + this.textarea.removeEventListener('input', this.onInput); + this.close(); + } + + /** + * テキスト入力時 + */ + private onInput() { + this.close(); + + const caret = this.textarea.selectionStart; + const text = this.textarea.value.substr(0, caret); + + const mentionIndex = text.lastIndexOf('@'); + + if (mentionIndex == -1) return; + + const username = text.substr(mentionIndex + 1); + + if (!username.match(/^[a-zA-Z0-9-]+$/)) return; + + this.open('user', username); + } + + /** + * サジェストを提示します。 + */ + private open(type, q) { + // 既に開いているサジェストは閉じる + this.close(); + + // サジェスト要素作成 + const tag = document.createElement('mk-autocomplete-suggestion'); + + // ~ サジェストを表示すべき位置を計算 ~ + + const caretPosition = getCaretCoordinates(this.textarea, this.textarea.selectionStart); + + const rect = this.textarea.getBoundingClientRect(); + + const x = rect.left + window.pageXOffset + caretPosition.left; + const y = rect.top + window.pageYOffset + caretPosition.top; + + tag.style.left = x + 'px'; + tag.style.top = y + 'px'; + + // 要素追加 + const el = document.body.appendChild(tag); + + // マウント + this.suggestion = (riot as any).mount(el, { + textarea: this.textarea, + complete: this.complete, + close: this.close, + type: type, + q: q + })[0]; + } + + /** + * サジェストを閉じます。 + */ + private close() { + if (this.suggestion == null) return; + + this.suggestion.unmount(); + this.suggestion = null; + + this.textarea.focus(); + } + + /** + * オートコンプリートする + */ + private complete(user) { + this.close(); + + const value = user.username; + + const caret = this.textarea.selectionStart; + const source = this.textarea.value; + + const before = source.substr(0, caret); + const trimmedBefore = before.substring(0, before.lastIndexOf('@')); + const after = source.substr(caret); + + // 結果を挿入する + this.textarea.value = trimmedBefore + '@' + value + ' ' + after; + + // キャレットを戻す + this.textarea.focus(); + const pos = caret + value.length; + this.textarea.setSelectionRange(pos, pos); + } +} + +export default Autocomplete; diff --git a/src/web/app/desktop/scripts/dialog.js b/src/web/app/desktop/scripts/dialog.js deleted file mode 100644 index c502d3fcb8..0000000000 --- a/src/web/app/desktop/scripts/dialog.js +++ /dev/null @@ -1,16 +0,0 @@ -import * as riot from 'riot'; - -export default (title, text, buttons, canThrough, onThrough) => { - const dialog = document.body.appendChild(document.createElement('mk-dialog')); - const controller = riot.observable(); - riot.mount(dialog, { - controller: controller, - title: title, - text: text, - buttons: buttons, - canThrough: canThrough, - onThrough: onThrough - }); - controller.trigger('open'); - return controller; -}; diff --git a/src/web/app/desktop/scripts/dialog.ts b/src/web/app/desktop/scripts/dialog.ts new file mode 100644 index 0000000000..816ba4b5f5 --- /dev/null +++ b/src/web/app/desktop/scripts/dialog.ts @@ -0,0 +1,16 @@ +import * as riot from 'riot'; + +export default (title, text, buttons, canThrough?, onThrough?) => { + const dialog = document.body.appendChild(document.createElement('mk-dialog')); + const controller = riot.observable(); + (riot as any).mount(dialog, { + controller: controller, + title: title, + text: text, + buttons: buttons, + canThrough: canThrough, + onThrough: onThrough + }); + controller.trigger('open'); + return controller; +}; diff --git a/src/web/app/desktop/scripts/fuck-ad-block.js b/src/web/app/desktop/scripts/fuck-ad-block.js deleted file mode 100644 index ccfc43ce6e..0000000000 --- a/src/web/app/desktop/scripts/fuck-ad-block.js +++ /dev/null @@ -1,18 +0,0 @@ -require('fuckadblock'); -import dialog from './dialog'; - -export default () => { - if (fuckAdBlock === undefined) { - adBlockDetected(); - } else { - fuckAdBlock.onDetected(adBlockDetected); - } -}; - -function adBlockDetected() { - dialog('広告ブロッカーを無効にしてください', - 'Misskeyは広告を掲載していませんが、広告をブロックする機能が有効だと一部の機能が利用できなかったり、不具合が発生する場合があります。', - [{ - text: 'OK' - }]); -} diff --git a/src/web/app/desktop/scripts/fuck-ad-block.ts b/src/web/app/desktop/scripts/fuck-ad-block.ts new file mode 100644 index 0000000000..3307ba2f30 --- /dev/null +++ b/src/web/app/desktop/scripts/fuck-ad-block.ts @@ -0,0 +1,20 @@ +require('fuckadblock'); +import dialog from './dialog'; + +declare var fuckAdBlock: any; + +export default () => { + if (fuckAdBlock === undefined) { + adBlockDetected(); + } else { + fuckAdBlock.onDetected(adBlockDetected); + } +}; + +function adBlockDetected() { + dialog('広告ブロッカーを無効にしてください', + 'Misskeyは広告を掲載していませんが、広告をブロックする機能が有効だと一部の機能が利用できなかったり、不具合が発生する場合があります。', + [{ + text: 'OK' + }]); +} diff --git a/src/web/app/desktop/scripts/input-dialog.js b/src/web/app/desktop/scripts/input-dialog.js deleted file mode 100644 index 954fabfb67..0000000000 --- a/src/web/app/desktop/scripts/input-dialog.js +++ /dev/null @@ -1,12 +0,0 @@ -import * as riot from 'riot'; - -export default (title, placeholder, defaultValue, onOk, onCancel) => { - const dialog = document.body.appendChild(document.createElement('mk-input-dialog')); - return riot.mount(dialog, { - title: title, - placeholder: placeholder, - 'default': defaultValue, - onOk: onOk, - onCancel: onCancel - }); -}; diff --git a/src/web/app/desktop/scripts/input-dialog.ts b/src/web/app/desktop/scripts/input-dialog.ts new file mode 100644 index 0000000000..b06d011c6b --- /dev/null +++ b/src/web/app/desktop/scripts/input-dialog.ts @@ -0,0 +1,12 @@ +import * as riot from 'riot'; + +export default (title, placeholder, defaultValue, onOk, onCancel) => { + const dialog = document.body.appendChild(document.createElement('mk-input-dialog')); + return (riot as any).mount(dialog, { + title: title, + placeholder: placeholder, + 'default': defaultValue, + onOk: onOk, + onCancel: onCancel + }); +}; diff --git a/src/web/app/desktop/scripts/not-implemented-exception.js b/src/web/app/desktop/scripts/not-implemented-exception.js deleted file mode 100644 index dd00c7662f..0000000000 --- a/src/web/app/desktop/scripts/not-implemented-exception.js +++ /dev/null @@ -1,8 +0,0 @@ -import dialog from './dialog'; - -export default () => { - dialog('Not implemented yet', - '要求された操作は実装されていません。
Misskeyの開発に参加する', [{ - text: 'OK' - }]); -}; diff --git a/src/web/app/desktop/scripts/not-implemented-exception.ts b/src/web/app/desktop/scripts/not-implemented-exception.ts new file mode 100644 index 0000000000..dd00c7662f --- /dev/null +++ b/src/web/app/desktop/scripts/not-implemented-exception.ts @@ -0,0 +1,8 @@ +import dialog from './dialog'; + +export default () => { + dialog('Not implemented yet', + '要求された操作は実装されていません。
Misskeyの開発に参加する', [{ + text: 'OK' + }]); +}; diff --git a/src/web/app/desktop/scripts/notify.js b/src/web/app/desktop/scripts/notify.js deleted file mode 100644 index e58a8e4d36..0000000000 --- a/src/web/app/desktop/scripts/notify.js +++ /dev/null @@ -1,8 +0,0 @@ -import * as riot from 'riot'; - -export default message => { - const notification = document.body.appendChild(document.createElement('mk-ui-notification')); - riot.mount(notification, { - message: message - }); -}; diff --git a/src/web/app/desktop/scripts/notify.ts b/src/web/app/desktop/scripts/notify.ts new file mode 100644 index 0000000000..2e6cbdeed8 --- /dev/null +++ b/src/web/app/desktop/scripts/notify.ts @@ -0,0 +1,8 @@ +import * as riot from 'riot'; + +export default message => { + const notification = document.body.appendChild(document.createElement('mk-ui-notification')); + (riot as any).mount(notification, { + message: message + }); +}; diff --git a/src/web/app/desktop/scripts/password-dialog.js b/src/web/app/desktop/scripts/password-dialog.js deleted file mode 100644 index 2bdc93e421..0000000000 --- a/src/web/app/desktop/scripts/password-dialog.js +++ /dev/null @@ -1,11 +0,0 @@ -import * as riot from 'riot'; - -export default (title, onOk, onCancel) => { - const dialog = document.body.appendChild(document.createElement('mk-input-dialog')); - return riot.mount(dialog, { - title: title, - type: 'password', - onOk: onOk, - onCancel: onCancel - }); -}; diff --git a/src/web/app/desktop/scripts/password-dialog.ts b/src/web/app/desktop/scripts/password-dialog.ts new file mode 100644 index 0000000000..39d7f3db7a --- /dev/null +++ b/src/web/app/desktop/scripts/password-dialog.ts @@ -0,0 +1,11 @@ +import * as riot from 'riot'; + +export default (title, onOk, onCancel) => { + const dialog = document.body.appendChild(document.createElement('mk-input-dialog')); + return (riot as any).mount(dialog, { + title: title, + type: 'password', + onOk: onOk, + onCancel: onCancel + }); +}; diff --git a/src/web/app/desktop/scripts/update-avatar.js b/src/web/app/desktop/scripts/update-avatar.js deleted file mode 100644 index 165c90567c..0000000000 --- a/src/web/app/desktop/scripts/update-avatar.js +++ /dev/null @@ -1,87 +0,0 @@ -import * as riot from 'riot'; -import CONFIG from '../../common/scripts/config'; -import dialog from './dialog'; -import api from '../../common/scripts/api'; - -export default (I, cb, file = null) => { - const fileSelected = file => { - const cropper = riot.mount(document.body.appendChild(document.createElement('mk-crop-window')), { - file: file, - title: 'アバターとして表示する部分を選択', - aspectRatio: 1 / 1 - })[0]; - - cropper.on('cropped', blob => { - const data = new FormData(); - data.append('i', I.token); - data.append('file', blob, file.name + '.cropped.png'); - - api(I, 'drive/folders/find', { - name: 'アイコン' - }).then(iconFolder => { - if (iconFolder.length === 0) { - api(I, 'drive/folders/create', { - name: 'アイコン' - }).then(iconFolder => { - upload(data, iconFolder); - }); - } else { - upload(data, iconFolder[0]); - } - }); - }); - - cropper.on('skipped', () => { - set(file); - }); - }; - - const upload = (data, folder) => { - const progress = riot.mount(document.body.appendChild(document.createElement('mk-progress-dialog')), { - title: '新しいアバターをアップロードしています' - })[0]; - - if (folder) data.append('folder_id', folder.id); - - const xhr = new XMLHttpRequest(); - xhr.open('POST', CONFIG.apiUrl + '/drive/files/create', true); - xhr.onload = e => { - const file = JSON.parse(e.target.response); - progress.close(); - set(file); - }; - - xhr.upload.onprogress = e => { - if (e.lengthComputable) progress.updateProgress(e.loaded, e.total); - }; - - xhr.send(data); - }; - - const set = file => { - api(I, 'i/update', { - avatar_id: file.id - }).then(i => { - dialog('アバターを更新しました', - '新しいアバターが反映されるまで時間がかかる場合があります。', - [{ - text: 'わかった' - }]); - - if (cb) cb(i); - }); - }; - - if (file) { - fileSelected(file); - } else { - const browser = riot.mount(document.body.appendChild(document.createElement('mk-select-file-from-drive-window')), { - multiple: false, - title: 'アバターにする画像を選択' - })[0]; - - browser.one('selected', file => { - fileSelected(file); - }); - } -}; diff --git a/src/web/app/desktop/scripts/update-avatar.ts b/src/web/app/desktop/scripts/update-avatar.ts new file mode 100644 index 0000000000..5fd7f2d3d1 --- /dev/null +++ b/src/web/app/desktop/scripts/update-avatar.ts @@ -0,0 +1,87 @@ +import * as riot from 'riot'; +import CONFIG from '../../common/scripts/config'; +import dialog from './dialog'; +import api from '../../common/scripts/api'; + +export default (I, cb, file = null) => { + const fileSelected = file => { + const cropper = (riot as any).mount(document.body.appendChild(document.createElement('mk-crop-window')), { + file: file, + title: 'アバターとして表示する部分を選択', + aspectRatio: 1 / 1 + })[0]; + + cropper.on('cropped', blob => { + const data = new FormData(); + data.append('i', I.token); + data.append('file', blob, file.name + '.cropped.png'); + + api(I, 'drive/folders/find', { + name: 'アイコン' + }).then(iconFolder => { + if (iconFolder.length === 0) { + api(I, 'drive/folders/create', { + name: 'アイコン' + }).then(iconFolder => { + upload(data, iconFolder); + }); + } else { + upload(data, iconFolder[0]); + } + }); + }); + + cropper.on('skipped', () => { + set(file); + }); + }; + + const upload = (data, folder) => { + const progress = (riot as any).mount(document.body.appendChild(document.createElement('mk-progress-dialog')), { + title: '新しいアバターをアップロードしています' + })[0]; + + if (folder) data.append('folder_id', folder.id); + + const xhr = new XMLHttpRequest(); + xhr.open('POST', CONFIG.apiUrl + '/drive/files/create', true); + xhr.onload = e => { + const file = JSON.parse((e.target as any).response); + progress.close(); + set(file); + }; + + xhr.upload.onprogress = e => { + if (e.lengthComputable) progress.updateProgress(e.loaded, e.total); + }; + + xhr.send(data); + }; + + const set = file => { + api(I, 'i/update', { + avatar_id: file.id + }).then(i => { + dialog('アバターを更新しました', + '新しいアバターが反映されるまで時間がかかる場合があります。', + [{ + text: 'わかった' + }]); + + if (cb) cb(i); + }); + }; + + if (file) { + fileSelected(file); + } else { + const browser = (riot as any).mount(document.body.appendChild(document.createElement('mk-select-file-from-drive-window')), { + multiple: false, + title: 'アバターにする画像を選択' + })[0]; + + browser.one('selected', file => { + fileSelected(file); + }); + } +}; diff --git a/src/web/app/desktop/scripts/update-banner.js b/src/web/app/desktop/scripts/update-banner.js deleted file mode 100644 index d83b2bf1b1..0000000000 --- a/src/web/app/desktop/scripts/update-banner.js +++ /dev/null @@ -1,87 +0,0 @@ -import * as riot from 'riot'; -import CONFIG from '../../common/scripts/config'; -import dialog from './dialog'; -import api from '../../common/scripts/api'; - -export default (I, cb, file = null) => { - const fileSelected = file => { - const cropper = riot.mount(document.body.appendChild(document.createElement('mk-crop-window')), { - file: file, - title: 'バナーとして表示する部分を選択', - aspectRatio: 16 / 9 - })[0]; - - cropper.on('cropped', blob => { - const data = new FormData(); - data.append('i', I.token); - data.append('file', blob, file.name + '.cropped.png'); - - api(I, 'drive/folders/find', { - name: 'バナー' - }).then(iconFolder => { - if (iconFolder.length === 0) { - api(I, 'drive/folders/create', { - name: 'バナー' - }).then(iconFolder => { - upload(data, iconFolder); - }); - } else { - upload(data, iconFolder[0]); - } - }); - }); - - cropper.on('skipped', () => { - set(file); - }); - }; - - const upload = (data, folder) => { - const progress = riot.mount(document.body.appendChild(document.createElement('mk-progress-dialog')), { - title: '新しいバナーをアップロードしています' - })[0]; - - if (folder) data.append('folder_id', folder.id); - - const xhr = new XMLHttpRequest(); - xhr.open('POST', CONFIG.apiUrl + '/drive/files/create', true); - xhr.onload = e => { - const file = JSON.parse(e.target.response); - progress.close(); - set(file); - }; - - xhr.upload.onprogress = e => { - if (e.lengthComputable) progress.updateProgress(e.loaded, e.total); - }; - - xhr.send(data); - }; - - const set = file => { - api(I, 'i/update', { - banner_id: file.id - }).then(i => { - dialog('バナーを更新しました', - '新しいバナーが反映されるまで時間がかかる場合があります。', - [{ - text: 'わかりました。' - }]); - - if (cb) cb(i); - }); - }; - - if (file) { - fileSelected(file); - } else { - const browser = riot.mount(document.body.appendChild(document.createElement('mk-select-file-from-drive-window')), { - multiple: false, - title: 'バナーにする画像を選択' - })[0]; - - browser.one('selected', file => { - fileSelected(file); - }); - } -}; diff --git a/src/web/app/desktop/scripts/update-banner.ts b/src/web/app/desktop/scripts/update-banner.ts new file mode 100644 index 0000000000..23a671c44d --- /dev/null +++ b/src/web/app/desktop/scripts/update-banner.ts @@ -0,0 +1,87 @@ +import * as riot from 'riot'; +import CONFIG from '../../common/scripts/config'; +import dialog from './dialog'; +import api from '../../common/scripts/api'; + +export default (I, cb, file = null) => { + const fileSelected = file => { + const cropper = (riot as any).mount(document.body.appendChild(document.createElement('mk-crop-window')), { + file: file, + title: 'バナーとして表示する部分を選択', + aspectRatio: 16 / 9 + })[0]; + + cropper.on('cropped', blob => { + const data = new FormData(); + data.append('i', I.token); + data.append('file', blob, file.name + '.cropped.png'); + + api(I, 'drive/folders/find', { + name: 'バナー' + }).then(iconFolder => { + if (iconFolder.length === 0) { + api(I, 'drive/folders/create', { + name: 'バナー' + }).then(iconFolder => { + upload(data, iconFolder); + }); + } else { + upload(data, iconFolder[0]); + } + }); + }); + + cropper.on('skipped', () => { + set(file); + }); + }; + + const upload = (data, folder) => { + const progress = (riot as any).mount(document.body.appendChild(document.createElement('mk-progress-dialog')), { + title: '新しいバナーをアップロードしています' + })[0]; + + if (folder) data.append('folder_id', folder.id); + + const xhr = new XMLHttpRequest(); + xhr.open('POST', CONFIG.apiUrl + '/drive/files/create', true); + xhr.onload = e => { + const file = JSON.parse((e.target as any).response); + progress.close(); + set(file); + }; + + xhr.upload.onprogress = e => { + if (e.lengthComputable) progress.updateProgress(e.loaded, e.total); + }; + + xhr.send(data); + }; + + const set = file => { + api(I, 'i/update', { + banner_id: file.id + }).then(i => { + dialog('バナーを更新しました', + '新しいバナーが反映されるまで時間がかかる場合があります。', + [{ + text: 'わかりました。' + }]); + + if (cb) cb(i); + }); + }; + + if (file) { + fileSelected(file); + } else { + const browser = (riot as any).mount(document.body.appendChild(document.createElement('mk-select-file-from-drive-window')), { + multiple: false, + title: 'バナーにする画像を選択' + })[0]; + + browser.one('selected', file => { + fileSelected(file); + }); + } +}; diff --git a/src/web/app/desktop/tags/autocomplete-suggestion.tag b/src/web/app/desktop/tags/autocomplete-suggestion.tag index b936360402..7311606694 100644 --- a/src/web/app/desktop/tags/autocomplete-suggestion.tag +++ b/src/web/app/desktop/tags/autocomplete-suggestion.tag @@ -177,7 +177,7 @@ }; this.applySelect = () => { - this.refs.users.children.forEach(el => { + Array.from(this.refs.users.children).forEach(el => { el.removeAttribute('data-selected'); }); diff --git a/src/web/app/desktop/tags/drive/browser.tag b/src/web/app/desktop/tags/drive/browser.tag index be16a782d7..18e27f24bc 100644 --- a/src/web/app/desktop/tags/drive/browser.tag +++ b/src/web/app/desktop/tags/drive/browser.tag @@ -408,7 +408,7 @@ // ドロップされてきたものがファイルだったら if (e.dataTransfer.files.length > 0) { - e.dataTransfer.files.forEach(file => { + Array.from(e.dataTransfer.files).forEach(file => { this.upload(file, this.folder); }); return false; @@ -510,7 +510,7 @@ }; this.changeFileInput = () => { - this.refs.fileInput.files.forEach(file => { + Array.from(this.refs.fileInput.files).forEach(file => { this.upload(file, this.folder); }); }; diff --git a/src/web/app/desktop/tags/drive/folder.tag b/src/web/app/desktop/tags/drive/folder.tag index e03c4e3534..1c361c8e4d 100644 --- a/src/web/app/desktop/tags/drive/folder.tag +++ b/src/web/app/desktop/tags/drive/folder.tag @@ -109,7 +109,7 @@ // ファイルだったら if (e.dataTransfer.files.length > 0) { - e.dataTransfer.files.forEach(file => { + Array.from(e.dataTransfer.files).forEach(file => { this.browser.upload(file, this.folder); }); return false; diff --git a/src/web/app/desktop/tags/drive/nav-folder.tag b/src/web/app/desktop/tags/drive/nav-folder.tag index c89d9edc1c..0a9421353c 100644 --- a/src/web/app/desktop/tags/drive/nav-folder.tag +++ b/src/web/app/desktop/tags/drive/nav-folder.tag @@ -55,7 +55,7 @@ // ファイルだったら if (e.dataTransfer.files.length > 0) { - e.dataTransfer.files.forEach(file => { + Array.from(e.dataTransfer.files).forEach(file => { this.browser.upload(file, this.folder); }); return false; diff --git a/src/web/app/desktop/tags/index.js b/src/web/app/desktop/tags/index.js deleted file mode 100644 index 15677471c3..0000000000 --- a/src/web/app/desktop/tags/index.js +++ /dev/null @@ -1,93 +0,0 @@ -require('./contextmenu.tag'); -require('./dialog.tag'); -require('./window.tag'); -require('./input-dialog.tag'); -require('./follow-button.tag'); -require('./drive/base-contextmenu.tag'); -require('./drive/file-contextmenu.tag'); -require('./drive/folder-contextmenu.tag'); -require('./drive/file.tag'); -require('./drive/folder.tag'); -require('./drive/nav-folder.tag'); -require('./drive/browser-window.tag'); -require('./drive/browser.tag'); -require('./select-file-from-drive-window.tag'); -require('./select-folder-from-drive-window.tag'); -require('./crop-window.tag'); -require('./settings.tag'); -require('./settings-window.tag'); -require('./analog-clock.tag'); -require('./notifications.tag'); -require('./post-form-window.tag'); -require('./post-form.tag'); -require('./post-preview.tag'); -require('./repost-form-window.tag'); -require('./home-widgets/user-recommendation.tag'); -require('./home-widgets/timeline.tag'); -require('./home-widgets/mentions.tag'); -require('./home-widgets/calendar.tag'); -require('./home-widgets/donation.tag'); -require('./home-widgets/tips.tag'); -require('./home-widgets/nav.tag'); -require('./home-widgets/profile.tag'); -require('./home-widgets/notifications.tag'); -require('./home-widgets/rss-reader.tag'); -require('./home-widgets/photo-stream.tag'); -require('./home-widgets/broadcast.tag'); -require('./home-widgets/version.tag'); -require('./home-widgets/recommended-polls.tag'); -require('./home-widgets/trends.tag'); -require('./home-widgets/activity.tag'); -require('./home-widgets/server.tag'); -require('./home-widgets/slideshow.tag'); -require('./home-widgets/channel.tag'); -require('./home-widgets/timemachine.tag'); -require('./home-widgets/post-form.tag'); -require('./timeline.tag'); -require('./messaging/window.tag'); -require('./messaging/room-window.tag'); -require('./following-setuper.tag'); -require('./ellipsis-icon.tag'); -require('./ui.tag'); -require('./home.tag'); -require('./user-header.tag'); -require('./user-profile.tag'); -require('./user-timeline.tag'); -require('./user.tag'); -require('./user-home.tag'); -require('./user-graphs.tag'); -require('./user-photos.tag'); -require('./big-follow-button.tag'); -require('./pages/entrance.tag'); -require('./pages/entrance/signin.tag'); -require('./pages/entrance/signup.tag'); -require('./pages/home.tag'); -require('./pages/home-customize.tag'); -require('./pages/user.tag'); -require('./pages/post.tag'); -require('./pages/search.tag'); -require('./pages/not-found.tag'); -require('./pages/selectdrive.tag'); -require('./pages/drive.tag'); -require('./pages/messaging-room.tag'); -require('./autocomplete-suggestion.tag'); -require('./progress-dialog.tag'); -require('./user-preview.tag'); -require('./post-detail.tag'); -require('./post-detail-sub.tag'); -require('./search.tag'); -require('./search-posts.tag'); -require('./set-avatar-suggestion.tag'); -require('./set-banner-suggestion.tag'); -require('./repost-form.tag'); -require('./sub-post-content.tag'); -require('./images-viewer.tag'); -require('./image-dialog.tag'); -require('./donation.tag'); -require('./users-list.tag'); -require('./user-following.tag'); -require('./user-followers.tag'); -require('./user-following-window.tag'); -require('./user-followers-window.tag'); -require('./list-user.tag'); -require('./detailed-post-window.tag'); diff --git a/src/web/app/desktop/tags/index.ts b/src/web/app/desktop/tags/index.ts new file mode 100644 index 0000000000..15677471c3 --- /dev/null +++ b/src/web/app/desktop/tags/index.ts @@ -0,0 +1,93 @@ +require('./contextmenu.tag'); +require('./dialog.tag'); +require('./window.tag'); +require('./input-dialog.tag'); +require('./follow-button.tag'); +require('./drive/base-contextmenu.tag'); +require('./drive/file-contextmenu.tag'); +require('./drive/folder-contextmenu.tag'); +require('./drive/file.tag'); +require('./drive/folder.tag'); +require('./drive/nav-folder.tag'); +require('./drive/browser-window.tag'); +require('./drive/browser.tag'); +require('./select-file-from-drive-window.tag'); +require('./select-folder-from-drive-window.tag'); +require('./crop-window.tag'); +require('./settings.tag'); +require('./settings-window.tag'); +require('./analog-clock.tag'); +require('./notifications.tag'); +require('./post-form-window.tag'); +require('./post-form.tag'); +require('./post-preview.tag'); +require('./repost-form-window.tag'); +require('./home-widgets/user-recommendation.tag'); +require('./home-widgets/timeline.tag'); +require('./home-widgets/mentions.tag'); +require('./home-widgets/calendar.tag'); +require('./home-widgets/donation.tag'); +require('./home-widgets/tips.tag'); +require('./home-widgets/nav.tag'); +require('./home-widgets/profile.tag'); +require('./home-widgets/notifications.tag'); +require('./home-widgets/rss-reader.tag'); +require('./home-widgets/photo-stream.tag'); +require('./home-widgets/broadcast.tag'); +require('./home-widgets/version.tag'); +require('./home-widgets/recommended-polls.tag'); +require('./home-widgets/trends.tag'); +require('./home-widgets/activity.tag'); +require('./home-widgets/server.tag'); +require('./home-widgets/slideshow.tag'); +require('./home-widgets/channel.tag'); +require('./home-widgets/timemachine.tag'); +require('./home-widgets/post-form.tag'); +require('./timeline.tag'); +require('./messaging/window.tag'); +require('./messaging/room-window.tag'); +require('./following-setuper.tag'); +require('./ellipsis-icon.tag'); +require('./ui.tag'); +require('./home.tag'); +require('./user-header.tag'); +require('./user-profile.tag'); +require('./user-timeline.tag'); +require('./user.tag'); +require('./user-home.tag'); +require('./user-graphs.tag'); +require('./user-photos.tag'); +require('./big-follow-button.tag'); +require('./pages/entrance.tag'); +require('./pages/entrance/signin.tag'); +require('./pages/entrance/signup.tag'); +require('./pages/home.tag'); +require('./pages/home-customize.tag'); +require('./pages/user.tag'); +require('./pages/post.tag'); +require('./pages/search.tag'); +require('./pages/not-found.tag'); +require('./pages/selectdrive.tag'); +require('./pages/drive.tag'); +require('./pages/messaging-room.tag'); +require('./autocomplete-suggestion.tag'); +require('./progress-dialog.tag'); +require('./user-preview.tag'); +require('./post-detail.tag'); +require('./post-detail-sub.tag'); +require('./search.tag'); +require('./search-posts.tag'); +require('./set-avatar-suggestion.tag'); +require('./set-banner-suggestion.tag'); +require('./repost-form.tag'); +require('./sub-post-content.tag'); +require('./images-viewer.tag'); +require('./image-dialog.tag'); +require('./donation.tag'); +require('./users-list.tag'); +require('./user-following.tag'); +require('./user-followers.tag'); +require('./user-following-window.tag'); +require('./user-followers-window.tag'); +require('./list-user.tag'); +require('./detailed-post-window.tag'); diff --git a/src/web/app/desktop/tags/post-detail-sub.tag b/src/web/app/desktop/tags/post-detail-sub.tag index 8a0ada5f2a..e22386df91 100644 --- a/src/web/app/desktop/tags/post-detail-sub.tag +++ b/src/web/app/desktop/tags/post-detail-sub.tag @@ -129,7 +129,7 @@ this.refs.text.innerHTML = compile(tokens); - this.refs.text.children.forEach(e => { + Array.from(this.refs.text.children).forEach(e => { if (e.tagName == 'MK-URL') riot.mount(e); }); } diff --git a/src/web/app/desktop/tags/post-detail.tag b/src/web/app/desktop/tags/post-detail.tag index ce7f81e32c..1a0eefe13e 100644 --- a/src/web/app/desktop/tags/post-detail.tag +++ b/src/web/app/desktop/tags/post-detail.tag @@ -273,7 +273,7 @@ this.refs.text.innerHTML = compile(tokens); - this.refs.text.children.forEach(e => { + Array.from(this.refs.text.children).forEach(e => { if (e.tagName == 'MK-URL') riot.mount(e); }); diff --git a/src/web/app/desktop/tags/post-form.tag b/src/web/app/desktop/tags/post-form.tag index 5041078bee..e49beeedbc 100644 --- a/src/web/app/desktop/tags/post-form.tag +++ b/src/web/app/desktop/tags/post-form.tag @@ -405,7 +405,7 @@ // ファイルだったら if (e.dataTransfer.files.length > 0) { - e.dataTransfer.files.forEach(this.upload); + Array.from(e.dataTransfer.files).forEach(this.upload); } }; @@ -414,7 +414,7 @@ }; this.onpaste = e => { - e.clipboardData.items.forEach(item => { + Array.from(e.clipboardData.items).forEach(item => { if (item.kind == 'file') { this.upload(item.getAsFile()); } @@ -435,7 +435,7 @@ }; this.changeFile = () => { - this.refs.file.files.forEach(this.upload); + Array.from(this.refs.file.files).forEach(this.upload); }; this.upload = file => { diff --git a/src/web/app/desktop/tags/sub-post-content.tag b/src/web/app/desktop/tags/sub-post-content.tag index c75ae2911c..86269fdbe9 100644 --- a/src/web/app/desktop/tags/sub-post-content.tag +++ b/src/web/app/desktop/tags/sub-post-content.tag @@ -45,7 +45,7 @@ const tokens = this.post.ast; this.refs.text.innerHTML = compile(tokens, false); - this.refs.text.children.forEach(e => { + Array.from(this.refs.text.children).forEach(e => { if (e.tagName == 'MK-URL') riot.mount(e); }); } diff --git a/src/web/app/desktop/tags/timeline.tag b/src/web/app/desktop/tags/timeline.tag index 44f3d5d8ec..5e3b883b47 100644 --- a/src/web/app/desktop/tags/timeline.tag +++ b/src/web/app/desktop/tags/timeline.tag @@ -498,7 +498,7 @@ this.refs.text.innerHTML = this.refs.text.innerHTML.replace('

', compile(tokens)); - this.refs.text.children.forEach(e => { + Array.from(this.refs.text.children).forEach(e => { if (e.tagName == 'MK-URL') riot.mount(e); }); diff --git a/src/web/app/dev/router.js b/src/web/app/dev/router.js deleted file mode 100644 index 7fde30fa5c..0000000000 --- a/src/web/app/dev/router.js +++ /dev/null @@ -1,42 +0,0 @@ -import * as riot from 'riot'; -const route = require('page'); -let page = null; - -export default me => { - route('/', index); - route('/apps', apps); - route('/app/new', newApp); - route('/app/:app', app); - route('*', notFound); - - function index() { - mount(document.createElement('mk-index')); - } - - function apps() { - mount(document.createElement('mk-apps-page')); - } - - function newApp() { - mount(document.createElement('mk-new-app-page')); - } - - function app(ctx) { - const el = document.createElement('mk-app-page'); - el.setAttribute('app', ctx.params.app); - 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/dev/router.ts b/src/web/app/dev/router.ts new file mode 100644 index 0000000000..532ec23c73 --- /dev/null +++ b/src/web/app/dev/router.ts @@ -0,0 +1,42 @@ +import * as riot from 'riot'; +import * as route from 'page'; +let page = null; + +export default me => { + route('/', index); + route('/apps', apps); + route('/app/new', newApp); + route('/app/:app', app); + route('*', notFound); + + function index() { + mount(document.createElement('mk-index')); + } + + function apps() { + mount(document.createElement('mk-apps-page')); + } + + function newApp() { + mount(document.createElement('mk-new-app-page')); + } + + function app(ctx) { + const el = document.createElement('mk-app-page'); + el.setAttribute('app', ctx.params.app); + mount(el); + } + + function notFound() { + mount(document.createElement('mk-not-found')); + } + + // EXEC + (route as any)(); +}; + +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/dev/script.js b/src/web/app/dev/script.js deleted file mode 100644 index 39d7fc891e..0000000000 --- a/src/web/app/dev/script.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Developer Center - */ - -// 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/dev/script.ts b/src/web/app/dev/script.ts new file mode 100644 index 0000000000..39d7fc891e --- /dev/null +++ b/src/web/app/dev/script.ts @@ -0,0 +1,18 @@ +/** + * Developer Center + */ + +// 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/dev/tags/index.js b/src/web/app/dev/tags/index.js deleted file mode 100644 index 1e0c73697e..0000000000 --- a/src/web/app/dev/tags/index.js +++ /dev/null @@ -1,5 +0,0 @@ -require('./pages/index.tag'); -require('./pages/apps.tag'); -require('./pages/app.tag'); -require('./pages/new-app.tag'); -require('./new-app-form.tag'); diff --git a/src/web/app/dev/tags/index.ts b/src/web/app/dev/tags/index.ts new file mode 100644 index 0000000000..1e0c73697e --- /dev/null +++ b/src/web/app/dev/tags/index.ts @@ -0,0 +1,5 @@ +require('./pages/index.tag'); +require('./pages/apps.tag'); +require('./pages/app.tag'); +require('./pages/new-app.tag'); +require('./new-app-form.tag'); diff --git a/src/web/app/init.js b/src/web/app/init.js deleted file mode 100644 index d3817fe971..0000000000 --- a/src/web/app/init.js +++ /dev/null @@ -1,196 +0,0 @@ -/** - * App initializer - */ - -'use strict'; - -import * as riot from 'riot'; -import api from './common/scripts/api'; -import signout from './common/scripts/signout'; -import checkForUpdate from './common/scripts/check-for-update'; -import Connection from './common/scripts/home-stream'; -import ServerStreamManager from './common/scripts/server-stream-manager.ts'; -import Progress from './common/scripts/loading'; -import mixin from './common/mixins'; -import CONFIG from './common/scripts/config'; -require('./common/tags'); - -/** - * APP ENTRY POINT! - */ - -console.info(`Misskey v${VERSION} (葵 aoi)`); - -{ // Set lang attr - const html = document.documentElement; - html.setAttribute('lang', LANG); -} - -{ // Set description meta tag - const head = document.getElementsByTagName('head')[0]; - const meta = document.createElement('meta'); - meta.setAttribute('name', 'description'); - meta.setAttribute('content', '%i18n:common.misskey%'); - head.appendChild(meta); -} - -document.domain = CONFIG.host; - -// Set global configuration -riot.mixin({ CONFIG }); - -// ↓ NodeList、HTMLCollection、FileList、DataTransferItemListで forEach を使えるようにする -if (NodeList.prototype.forEach === undefined) { - NodeList.prototype.forEach = Array.prototype.forEach; -} -if (HTMLCollection.prototype.forEach === undefined) { - HTMLCollection.prototype.forEach = Array.prototype.forEach; -} -if (FileList.prototype.forEach === undefined) { - FileList.prototype.forEach = Array.prototype.forEach; -} -if (window.DataTransferItemList && DataTransferItemList.prototype.forEach === undefined) { - DataTransferItemList.prototype.forEach = Array.prototype.forEach; -} - -// iOSでプライベートモードだとlocalStorageが使えないので既存のメソッドを上書きする -try { - localStorage.setItem('kyoppie', 'yuppie'); -} catch (e) { - Storage.prototype.setItem = () => { }; // noop -} - -// クライアントを更新すべきならする -if (localStorage.getItem('should-refresh') == 'true') { - localStorage.removeItem('should-refresh'); - location.reload(true); -} - -// 更新チェック -setTimeout(checkForUpdate, 3000); - -// ユーザーをフェッチしてコールバックする -export default callback => { - // Get cached account data - let cachedMe = JSON.parse(localStorage.getItem('me')); - - if (cachedMe) { - fetched(cachedMe); - - // 後から新鮮なデータをフェッチ - fetchme(cachedMe.token, freshData => { - Object.assign(cachedMe, freshData); - cachedMe.trigger('updated'); - }); - } else { - // Get token from cookie - const i = (document.cookie.match(/i=(!\w+)/) || [null, null])[1]; - - fetchme(i, fetched); - } - - // フェッチが完了したとき - function fetched(me) { - if (me) { - riot.observable(me); - - // この me オブジェクトを更新するメソッド - me.update = data => { - if (data) Object.assign(me, data); - me.trigger('updated'); - }; - - // ローカルストレージにキャッシュ - localStorage.setItem('me', JSON.stringify(me)); - - me.on('updated', () => { - // キャッシュ更新 - localStorage.setItem('me', JSON.stringify(me)); - }); - } - - // Init home stream connection - const stream = me ? new Connection(me) : null; - - // Init server stream connection manager - const serverStreamManager = new ServerStreamManager(); - - // ミックスイン初期化 - mixin(me, stream, serverStreamManager); - - // ローディング画面クリア - const ini = document.getElementById('ini'); - ini.parentNode.removeChild(ini); - - // アプリ基底要素マウント - const app = document.createElement('div'); - app.setAttribute('id', 'app'); - document.body.appendChild(app); - - try { - callback(me, stream); - } catch (e) { - panic(e); - } - } -}; - -// ユーザーをフェッチしてコールバックする -function fetchme(token, cb) { - let me = null; - - // Return when not signed in - if (token == null) { - return done(); - } - - // Fetch user - fetch(`${CONFIG.apiUrl}/i`, { - method: 'POST', - body: JSON.stringify({ - i: token - }) - }).then(res => { // When success - // When failed to authenticate user - if (res.status !== 200) { - return signout(); - } - - res.json().then(i => { - me = i; - me.token = token; - done(); - }); - }, () => { // When failure - // Render the error screen - document.body.innerHTML = ''; - riot.mount('*'); - Progress.done(); - }); - - function done() { - if (cb) cb(me); - } -} - -// BSoD -function panic(e) { - console.error(e); - - // Display blue screen - document.documentElement.style.background = '#1269e2'; - document.body.innerHTML = - '
' - + '

:( 致命的な問題が発生しました。

' - + '

お使いのブラウザ(またはOS)のバージョンを更新すると解決する可能性があります。

' - + '
' - + `

エラーコード: ${e.toString()}

` - + `

ブラウザ バージョン: ${navigator.userAgent}

` - + `

クライアント バージョン: ${VERSION}

` - + '
' - + '

問題が解決しない場合は、上記の情報をお書き添えの上 syuilotan@yahoo.co.jp までご連絡ください。

' - + '

Thank you for using Misskey.

' - + '
'; - - // TODO: Report the bug -} diff --git a/src/web/app/init.ts b/src/web/app/init.ts new file mode 100644 index 0000000000..e68a7c9152 --- /dev/null +++ b/src/web/app/init.ts @@ -0,0 +1,178 @@ +/** + * App initializer + */ + +declare var VERSION: string; +declare var LANG: string; + +import * as riot from 'riot'; +import signout from './common/scripts/signout'; +import checkForUpdate from './common/scripts/check-for-update'; +import Connection from './common/scripts/home-stream'; +import Progress from './common/scripts/loading'; +import mixin from './common/mixins'; +import CONFIG from './common/scripts/config'; +require('./common/tags'); + +/** + * APP ENTRY POINT! + */ + +console.info(`Misskey v${VERSION} (葵 aoi)`); + +{ // Set lang attr + const html = document.documentElement; + html.setAttribute('lang', LANG); +} + +{ // Set description meta tag + const head = document.getElementsByTagName('head')[0]; + const meta = document.createElement('meta'); + meta.setAttribute('name', 'description'); + meta.setAttribute('content', '%i18n:common.misskey%'); + head.appendChild(meta); +} + +document.domain = CONFIG.host; + +// Set global configuration +(riot as any).mixin({ CONFIG }); + +// iOSでプライベートモードだとlocalStorageが使えないので既存のメソッドを上書きする +try { + localStorage.setItem('kyoppie', 'yuppie'); +} catch (e) { + Storage.prototype.setItem = () => { }; // noop +} + +// クライアントを更新すべきならする +if (localStorage.getItem('should-refresh') == 'true') { + localStorage.removeItem('should-refresh'); + location.reload(true); +} + +// 更新チェック +setTimeout(checkForUpdate, 3000); + +// ユーザーをフェッチしてコールバックする +export default callback => { + // Get cached account data + const cachedMe = JSON.parse(localStorage.getItem('me')); + + if (cachedMe) { + fetched(cachedMe); + + // 後から新鮮なデータをフェッチ + fetchme(cachedMe.token, freshData => { + Object.assign(cachedMe, freshData); + cachedMe.trigger('updated'); + }); + } else { + // Get token from cookie + const i = (document.cookie.match(/i=(!\w+)/) || [null, null])[1]; + + fetchme(i, fetched); + } + + // フェッチが完了したとき + function fetched(me) { + if (me) { + riot.observable(me); + + // この me オブジェクトを更新するメソッド + me.update = data => { + if (data) Object.assign(me, data); + me.trigger('updated'); + }; + + // ローカルストレージにキャッシュ + localStorage.setItem('me', JSON.stringify(me)); + + me.on('updated', () => { + // キャッシュ更新 + localStorage.setItem('me', JSON.stringify(me)); + }); + } + + // Init home stream connection + const stream = me ? new Connection(me) : null; + + // ミックスイン初期化 + mixin(me, stream); + + // ローディング画面クリア + const ini = document.getElementById('ini'); + ini.parentNode.removeChild(ini); + + // アプリ基底要素マウント + const app = document.createElement('div'); + app.setAttribute('id', 'app'); + document.body.appendChild(app); + + try { + callback(me, stream); + } catch (e) { + panic(e); + } + } +}; + +// ユーザーをフェッチしてコールバックする +function fetchme(token, cb) { + let me = null; + + // Return when not signed in + if (token == null) { + return done(); + } + + // Fetch user + fetch(`${CONFIG.apiUrl}/i`, { + method: 'POST', + body: JSON.stringify({ + i: token + }) + }).then(res => { // When success + // When failed to authenticate user + if (res.status !== 200) { + return signout(); + } + + res.json().then(i => { + me = i; + me.token = token; + done(); + }); + }, () => { // When failure + // Render the error screen + document.body.innerHTML = ''; + riot.mount('*'); + Progress.done(); + }); + + function done() { + if (cb) cb(me); + } +} + +// BSoD +function panic(e) { + console.error(e); + + // Display blue screen + document.documentElement.style.background = '#1269e2'; + document.body.innerHTML = + '
' + + '

:( 致命的な問題が発生しました。

' + + '

お使いのブラウザ(またはOS)のバージョンを更新すると解決する可能性があります。

' + + '
' + + `

エラーコード: ${e.toString()}

` + + `

ブラウザ バージョン: ${navigator.userAgent}

` + + `

クライアント バージョン: ${VERSION}

` + + '
' + + '

問題が解決しない場合は、上記の情報をお書き添えの上 syuilotan@yahoo.co.jp までご連絡ください。

' + + '

Thank you for using Misskey.

' + + '
'; + + // TODO: Report the bug +} diff --git a/src/web/app/mobile/router.js b/src/web/app/mobile/router.js deleted file mode 100644 index 01eb3c8145..0000000000 --- a/src/web/app/mobile/router.js +++ /dev/null @@ -1,147 +0,0 @@ -/** - * Mobile App Router - */ - -import * as riot from 'riot'; -const route = require('page'); -let page = null; - -export default me => { - route('/', index); - route('/selectdrive', selectDrive); - route('/i/notifications', notifications); - route('/i/messaging', messaging); - route('/i/messaging/:username', messaging); - route('/i/drive', drive); - route('/i/drive/folder/:folder', drive); - route('/i/drive/file/:file', drive); - route('/i/settings', settings); - route('/i/settings/profile', settingsProfile); - route('/i/settings/signin-history', settingsSignin); - route('/i/settings/api', settingsApi); - route('/i/settings/twitter', settingsTwitter); - route('/i/settings/authorized-apps', settingsAuthorizedApps); - route('/post/new', newPost); - route('/post::post', post); - route('/search::query', search); - route('/:user', user.bind(null, 'overview')); - route('/:user/graphs', user.bind(null, 'graphs')); - route('/:user/followers', userFollowers); - route('/:user/following', userFollowing); - route('/:user/:post', post); - route('*', notFound); - - function index() { - me ? home() : entrance(); - } - - function home() { - mount(document.createElement('mk-home-page')); - } - - function entrance() { - mount(document.createElement('mk-entrance')); - } - - function notifications() { - mount(document.createElement('mk-notifications-page')); - } - - function messaging(ctx) { - if (ctx.params.username) { - const el = document.createElement('mk-messaging-room-page'); - el.setAttribute('username', ctx.params.username); - mount(el); - } else { - mount(document.createElement('mk-messaging-page')); - } - } - - function newPost() { - mount(document.createElement('mk-new-post-page')); - } - - function settings() { - mount(document.createElement('mk-settings-page')); - } - - function settingsProfile() { - mount(document.createElement('mk-profile-setting-page')); - } - - function settingsSignin() { - mount(document.createElement('mk-signin-history-page')); - } - - function settingsApi() { - mount(document.createElement('mk-api-info-page')); - } - - function settingsTwitter() { - mount(document.createElement('mk-twitter-setting-page')); - } - - function settingsAuthorizedApps() { - mount(document.createElement('mk-authorized-apps-page')); - } - - function search(ctx) { - const el = document.createElement('mk-search-page'); - el.setAttribute('query', ctx.params.query); - mount(el); - } - - function user(page, ctx) { - const el = document.createElement('mk-user-page'); - el.setAttribute('user', ctx.params.user); - el.setAttribute('page', page); - mount(el); - } - - function userFollowing(ctx) { - const el = document.createElement('mk-user-following-page'); - el.setAttribute('user', ctx.params.user); - mount(el); - } - - function userFollowers(ctx) { - const el = document.createElement('mk-user-followers-page'); - el.setAttribute('user', ctx.params.user); - mount(el); - } - - function post(ctx) { - const el = document.createElement('mk-post-page'); - el.setAttribute('post', ctx.params.post); - mount(el); - } - - function drive(ctx) { - const el = document.createElement('mk-drive-page'); - if (ctx.params.folder) el.setAttribute('folder', ctx.params.folder); - if (ctx.params.file) el.setAttribute('file', ctx.params.file); - mount(el); - } - - function selectDrive() { - mount(document.createElement('mk-selectdrive-page')); - } - - function notFound() { - mount(document.createElement('mk-not-found')); - } - - riot.mixin('page', { - page: route - }); - - // EXEC - route(); -}; - -function mount(content) { - document.documentElement.style.background = '#fff'; - if (page) page.unmount(); - const body = document.getElementById('app'); - page = riot.mount(body.appendChild(content))[0]; -} diff --git a/src/web/app/mobile/router.ts b/src/web/app/mobile/router.ts new file mode 100644 index 0000000000..7fae9db547 --- /dev/null +++ b/src/web/app/mobile/router.ts @@ -0,0 +1,147 @@ +/** + * Mobile App Router + */ + +import * as riot from 'riot'; +import * as route from 'page'; +let page = null; + +export default me => { + route('/', index); + route('/selectdrive', selectDrive); + route('/i/notifications', notifications); + route('/i/messaging', messaging); + route('/i/messaging/:username', messaging); + route('/i/drive', drive); + route('/i/drive/folder/:folder', drive); + route('/i/drive/file/:file', drive); + route('/i/settings', settings); + route('/i/settings/profile', settingsProfile); + route('/i/settings/signin-history', settingsSignin); + route('/i/settings/api', settingsApi); + route('/i/settings/twitter', settingsTwitter); + route('/i/settings/authorized-apps', settingsAuthorizedApps); + route('/post/new', newPost); + route('/post::post', post); + route('/search::query', search); + route('/:user', user.bind(null, 'overview')); + route('/:user/graphs', user.bind(null, 'graphs')); + route('/:user/followers', userFollowers); + route('/:user/following', userFollowing); + route('/:user/:post', post); + route('*', notFound); + + function index() { + me ? home() : entrance(); + } + + function home() { + mount(document.createElement('mk-home-page')); + } + + function entrance() { + mount(document.createElement('mk-entrance')); + } + + function notifications() { + mount(document.createElement('mk-notifications-page')); + } + + function messaging(ctx) { + if (ctx.params.username) { + const el = document.createElement('mk-messaging-room-page'); + el.setAttribute('username', ctx.params.username); + mount(el); + } else { + mount(document.createElement('mk-messaging-page')); + } + } + + function newPost() { + mount(document.createElement('mk-new-post-page')); + } + + function settings() { + mount(document.createElement('mk-settings-page')); + } + + function settingsProfile() { + mount(document.createElement('mk-profile-setting-page')); + } + + function settingsSignin() { + mount(document.createElement('mk-signin-history-page')); + } + + function settingsApi() { + mount(document.createElement('mk-api-info-page')); + } + + function settingsTwitter() { + mount(document.createElement('mk-twitter-setting-page')); + } + + function settingsAuthorizedApps() { + mount(document.createElement('mk-authorized-apps-page')); + } + + function search(ctx) { + const el = document.createElement('mk-search-page'); + el.setAttribute('query', ctx.params.query); + mount(el); + } + + function user(page, ctx) { + const el = document.createElement('mk-user-page'); + el.setAttribute('user', ctx.params.user); + el.setAttribute('page', page); + mount(el); + } + + function userFollowing(ctx) { + const el = document.createElement('mk-user-following-page'); + el.setAttribute('user', ctx.params.user); + mount(el); + } + + function userFollowers(ctx) { + const el = document.createElement('mk-user-followers-page'); + el.setAttribute('user', ctx.params.user); + mount(el); + } + + function post(ctx) { + const el = document.createElement('mk-post-page'); + el.setAttribute('post', ctx.params.post); + mount(el); + } + + function drive(ctx) { + const el = document.createElement('mk-drive-page'); + if (ctx.params.folder) el.setAttribute('folder', ctx.params.folder); + if (ctx.params.file) el.setAttribute('file', ctx.params.file); + mount(el); + } + + function selectDrive() { + mount(document.createElement('mk-selectdrive-page')); + } + + function notFound() { + mount(document.createElement('mk-not-found')); + } + + (riot as any).mixin('page', { + page: route + }); + + // EXEC + (route as any)(); +}; + +function mount(content) { + document.documentElement.style.background = '#fff'; + if (page) page.unmount(); + const body = document.getElementById('app'); + page = riot.mount(body.appendChild(content))[0]; +} diff --git a/src/web/app/mobile/script.js b/src/web/app/mobile/script.js deleted file mode 100644 index 503e0fd673..0000000000 --- a/src/web/app/mobile/script.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Mobile Client - */ - -// Style -import './style.styl'; - -require('./tags'); -import init from '../init'; -import route from './router'; - -/** - * init - */ -init(me => { - // http://qiita.com/junya/items/3ff380878f26ca447f85 - document.body.setAttribute('ontouchstart', ''); - - // Start routing - route(me); -}); diff --git a/src/web/app/mobile/script.ts b/src/web/app/mobile/script.ts new file mode 100644 index 0000000000..503e0fd673 --- /dev/null +++ b/src/web/app/mobile/script.ts @@ -0,0 +1,21 @@ +/** + * Mobile Client + */ + +// Style +import './style.styl'; + +require('./tags'); +import init from '../init'; +import route from './router'; + +/** + * init + */ +init(me => { + // http://qiita.com/junya/items/3ff380878f26ca447f85 + document.body.setAttribute('ontouchstart', ''); + + // Start routing + route(me); +}); diff --git a/src/web/app/mobile/scripts/open-post-form.js b/src/web/app/mobile/scripts/open-post-form.js deleted file mode 100644 index e0fae4d8ca..0000000000 --- a/src/web/app/mobile/scripts/open-post-form.js +++ /dev/null @@ -1,15 +0,0 @@ -import * as riot from 'riot'; - -export default opts => { - const app = document.getElementById('app'); - app.style.display = 'none'; - - function recover() { - app.style.display = 'block'; - } - - const form = riot.mount(document.body.appendChild(document.createElement('mk-post-form')), opts)[0]; - form - .on('cancel', recover) - .on('post', recover); -}; diff --git a/src/web/app/mobile/scripts/open-post-form.ts b/src/web/app/mobile/scripts/open-post-form.ts new file mode 100644 index 0000000000..e0fae4d8ca --- /dev/null +++ b/src/web/app/mobile/scripts/open-post-form.ts @@ -0,0 +1,15 @@ +import * as riot from 'riot'; + +export default opts => { + const app = document.getElementById('app'); + app.style.display = 'none'; + + function recover() { + app.style.display = 'block'; + } + + const form = riot.mount(document.body.appendChild(document.createElement('mk-post-form')), opts)[0]; + form + .on('cancel', recover) + .on('post', recover); +}; diff --git a/src/web/app/mobile/scripts/ui-event.js b/src/web/app/mobile/scripts/ui-event.js deleted file mode 100644 index 2e406549a4..0000000000 --- a/src/web/app/mobile/scripts/ui-event.js +++ /dev/null @@ -1,5 +0,0 @@ -import * as riot from 'riot'; - -const ev = riot.observable(); - -export default ev; diff --git a/src/web/app/mobile/scripts/ui-event.ts b/src/web/app/mobile/scripts/ui-event.ts new file mode 100644 index 0000000000..2e406549a4 --- /dev/null +++ b/src/web/app/mobile/scripts/ui-event.ts @@ -0,0 +1,5 @@ +import * as riot from 'riot'; + +const ev = riot.observable(); + +export default ev; diff --git a/src/web/app/mobile/tags/drive.tag b/src/web/app/mobile/tags/drive.tag index 6929c50ab1..870a451acb 100644 --- a/src/web/app/mobile/tags/drive.tag +++ b/src/web/app/mobile/tags/drive.tag @@ -561,7 +561,7 @@ }; this.changeLocalFile = () => { - this.refs.file.files.forEach(f => this.refs.uploader.upload(f, this.folder)); + Array.from(this.refs.file.files).forEach(f => this.refs.uploader.upload(f, this.folder)); }; diff --git a/src/web/app/mobile/tags/index.js b/src/web/app/mobile/tags/index.js deleted file mode 100644 index 19952c20cd..0000000000 --- a/src/web/app/mobile/tags/index.js +++ /dev/null @@ -1,51 +0,0 @@ -require('./ui.tag'); -require('./page/entrance.tag'); -require('./page/entrance/signin.tag'); -require('./page/entrance/signup.tag'); -require('./page/home.tag'); -require('./page/drive.tag'); -require('./page/notifications.tag'); -require('./page/user.tag'); -require('./page/user-followers.tag'); -require('./page/user-following.tag'); -require('./page/post.tag'); -require('./page/new-post.tag'); -require('./page/search.tag'); -require('./page/settings.tag'); -require('./page/settings/profile.tag'); -require('./page/settings/signin.tag'); -require('./page/settings/api.tag'); -require('./page/settings/authorized-apps.tag'); -require('./page/settings/twitter.tag'); -require('./page/messaging.tag'); -require('./page/messaging-room.tag'); -require('./page/selectdrive.tag'); -require('./home.tag'); -require('./home-timeline.tag'); -require('./timeline.tag'); -require('./post-preview.tag'); -require('./sub-post-content.tag'); -require('./images-viewer.tag'); -require('./drive.tag'); -require('./drive-selector.tag'); -require('./drive-folder-selector.tag'); -require('./drive/file.tag'); -require('./drive/folder.tag'); -require('./drive/file-viewer.tag'); -require('./post-form.tag'); -require('./notification.tag'); -require('./notifications.tag'); -require('./notify.tag'); -require('./notification-preview.tag'); -require('./search.tag'); -require('./search-posts.tag'); -require('./post-detail.tag'); -require('./user.tag'); -require('./user-timeline.tag'); -require('./follow-button.tag'); -require('./user-preview.tag'); -require('./users-list.tag'); -require('./user-following.tag'); -require('./user-followers.tag'); -require('./init-following.tag'); -require('./user-card.tag'); diff --git a/src/web/app/mobile/tags/index.ts b/src/web/app/mobile/tags/index.ts new file mode 100644 index 0000000000..19952c20cd --- /dev/null +++ b/src/web/app/mobile/tags/index.ts @@ -0,0 +1,51 @@ +require('./ui.tag'); +require('./page/entrance.tag'); +require('./page/entrance/signin.tag'); +require('./page/entrance/signup.tag'); +require('./page/home.tag'); +require('./page/drive.tag'); +require('./page/notifications.tag'); +require('./page/user.tag'); +require('./page/user-followers.tag'); +require('./page/user-following.tag'); +require('./page/post.tag'); +require('./page/new-post.tag'); +require('./page/search.tag'); +require('./page/settings.tag'); +require('./page/settings/profile.tag'); +require('./page/settings/signin.tag'); +require('./page/settings/api.tag'); +require('./page/settings/authorized-apps.tag'); +require('./page/settings/twitter.tag'); +require('./page/messaging.tag'); +require('./page/messaging-room.tag'); +require('./page/selectdrive.tag'); +require('./home.tag'); +require('./home-timeline.tag'); +require('./timeline.tag'); +require('./post-preview.tag'); +require('./sub-post-content.tag'); +require('./images-viewer.tag'); +require('./drive.tag'); +require('./drive-selector.tag'); +require('./drive-folder-selector.tag'); +require('./drive/file.tag'); +require('./drive/folder.tag'); +require('./drive/file-viewer.tag'); +require('./post-form.tag'); +require('./notification.tag'); +require('./notifications.tag'); +require('./notify.tag'); +require('./notification-preview.tag'); +require('./search.tag'); +require('./search-posts.tag'); +require('./post-detail.tag'); +require('./user.tag'); +require('./user-timeline.tag'); +require('./follow-button.tag'); +require('./user-preview.tag'); +require('./users-list.tag'); +require('./user-following.tag'); +require('./user-followers.tag'); +require('./init-following.tag'); +require('./user-card.tag'); diff --git a/src/web/app/mobile/tags/post-detail.tag b/src/web/app/mobile/tags/post-detail.tag index 8a32101036..28071a5cac 100644 --- a/src/web/app/mobile/tags/post-detail.tag +++ b/src/web/app/mobile/tags/post-detail.tag @@ -285,7 +285,7 @@ this.refs.text.innerHTML = compile(tokens); - this.refs.text.children.forEach(e => { + Array.from(this.refs.text.children).forEach(e => { if (e.tagName == 'MK-URL') riot.mount(e); }); diff --git a/src/web/app/mobile/tags/post-form.tag b/src/web/app/mobile/tags/post-form.tag index d7d382c9e2..2912bfdfa2 100644 --- a/src/web/app/mobile/tags/post-form.tag +++ b/src/web/app/mobile/tags/post-form.tag @@ -207,7 +207,7 @@ }; this.onpaste = e => { - e.clipboardData.items.forEach(item => { + Array.from(e.clipboardData.items).forEach(item => { if (item.kind == 'file') { this.upload(item.getAsFile()); } @@ -228,7 +228,7 @@ }; this.changeFile = () => { - this.refs.file.files.forEach(this.upload); + Array.from(this.refs.file.files).forEach(this.upload); }; this.upload = file => { diff --git a/src/web/app/mobile/tags/sub-post-content.tag b/src/web/app/mobile/tags/sub-post-content.tag index e32e245185..c14233d3b7 100644 --- a/src/web/app/mobile/tags/sub-post-content.tag +++ b/src/web/app/mobile/tags/sub-post-content.tag @@ -37,7 +37,7 @@ const tokens = this.post.ast; this.refs.text.innerHTML = compile(tokens, false); - this.refs.text.children.forEach(e => { + Array.from(this.refs.text.children).forEach(e => { if (e.tagName == 'MK-URL') riot.mount(e); }); } diff --git a/src/web/app/mobile/tags/timeline.tag b/src/web/app/mobile/tags/timeline.tag index f9ec2cca60..52f6f27ea8 100644 --- a/src/web/app/mobile/tags/timeline.tag +++ b/src/web/app/mobile/tags/timeline.tag @@ -538,7 +538,7 @@ this.refs.text.innerHTML = this.refs.text.innerHTML.replace('

', compile(tokens)); - this.refs.text.children.forEach(e => { + Array.from(this.refs.text.children).forEach(e => { if (e.tagName == 'MK-URL') riot.mount(e); }); diff --git a/src/web/app/stats/script.js b/src/web/app/stats/script.js deleted file mode 100644 index 75063501bb..0000000000 --- a/src/web/app/stats/script.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Stats - */ - -// Style -import './style.styl'; - -import * as riot from 'riot'; -require('./tags'); -import init from '../init'; - -document.title = 'Misskey Statistics'; - -/** - * init - */ -init(me => { - mount(document.createElement('mk-index')); -}); - -function mount(content) { - riot.mount(document.getElementById('app').appendChild(content)); -} diff --git a/src/web/app/stats/script.ts b/src/web/app/stats/script.ts new file mode 100644 index 0000000000..75063501bb --- /dev/null +++ b/src/web/app/stats/script.ts @@ -0,0 +1,23 @@ +/** + * Stats + */ + +// Style +import './style.styl'; + +import * as riot from 'riot'; +require('./tags'); +import init from '../init'; + +document.title = 'Misskey Statistics'; + +/** + * init + */ +init(me => { + mount(document.createElement('mk-index')); +}); + +function mount(content) { + riot.mount(document.getElementById('app').appendChild(content)); +} diff --git a/src/web/app/stats/tags/index.js b/src/web/app/stats/tags/index.js deleted file mode 100644 index f41151949f..0000000000 --- a/src/web/app/stats/tags/index.js +++ /dev/null @@ -1 +0,0 @@ -require('./index.tag'); diff --git a/src/web/app/stats/tags/index.ts b/src/web/app/stats/tags/index.ts new file mode 100644 index 0000000000..f41151949f --- /dev/null +++ b/src/web/app/stats/tags/index.ts @@ -0,0 +1 @@ +require('./index.tag'); diff --git a/src/web/app/status/script.js b/src/web/app/status/script.js deleted file mode 100644 index 06d4d9a7a4..0000000000 --- a/src/web/app/status/script.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Status - */ - -// Style -import './style.styl'; - -import * as riot from 'riot'; -require('./tags'); -import init from '../init'; - -document.title = 'Misskey System Status'; - -/** - * init - */ -init(me => { - mount(document.createElement('mk-index')); -}); - -function mount(content) { - riot.mount(document.getElementById('app').appendChild(content)); -} diff --git a/src/web/app/status/script.ts b/src/web/app/status/script.ts new file mode 100644 index 0000000000..06d4d9a7a4 --- /dev/null +++ b/src/web/app/status/script.ts @@ -0,0 +1,23 @@ +/** + * Status + */ + +// Style +import './style.styl'; + +import * as riot from 'riot'; +require('./tags'); +import init from '../init'; + +document.title = 'Misskey System Status'; + +/** + * init + */ +init(me => { + mount(document.createElement('mk-index')); +}); + +function mount(content) { + riot.mount(document.getElementById('app').appendChild(content)); +} diff --git a/src/web/app/status/tags/index.js b/src/web/app/status/tags/index.js deleted file mode 100644 index f41151949f..0000000000 --- a/src/web/app/status/tags/index.js +++ /dev/null @@ -1 +0,0 @@ -require('./index.tag'); diff --git a/src/web/app/status/tags/index.ts b/src/web/app/status/tags/index.ts new file mode 100644 index 0000000000..f41151949f --- /dev/null +++ b/src/web/app/status/tags/index.ts @@ -0,0 +1 @@ +require('./index.tag'); diff --git a/tslint.json b/tslint.json index 1c44579512..d3f96000b9 100644 --- a/tslint.json +++ b/tslint.json @@ -13,6 +13,7 @@ "object-literal-sort-keys": false, "curly": false, "no-console": [false], + "no-empty":false, "ordered-imports": [false], "arrow-parens": false, "object-literal-shorthand": false, diff --git a/webpack/webpack.config.ts b/webpack/webpack.config.ts index 97782a4102..f2bcf48f31 100644 --- a/webpack/webpack.config.ts +++ b/webpack/webpack.config.ts @@ -14,13 +14,13 @@ module.exports = langs.map(([lang, locale]) => { // Entries 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', - auth: './src/web/app/auth/script.js' + desktop: './src/web/app/desktop/script.ts', + mobile: './src/web/app/mobile/script.ts', + ch: './src/web/app/ch/script.ts', + stats: './src/web/app/stats/script.ts', + status: './src/web/app/status/script.ts', + dev: './src/web/app/dev/script.ts', + auth: './src/web/app/auth/script.ts' }; const output = { @@ -33,6 +33,11 @@ module.exports = langs.map(([lang, locale]) => { entry, module: module_(lang, locale), plugins: plugins(version, lang), - output + output, + resolve: { + extensions: [ + '.js', '.ts' + ] + } }; }); -- cgit v1.2.3-freya