From dbc421cb571f69a4c71691c00b094925006b95fe Mon Sep 17 00:00:00 2001 From: syuilo <syuilotan@yahoo.co.jp> Date: Wed, 28 Feb 2018 00:11:28 +0900 Subject: [PATCH] :v: --- src/web/app/auth/script.ts | 25 +++ src/web/app/auth/views/index.vue | 8 +- src/web/app/dev/script.ts | 18 +- src/web/app/dev/tags/index.ts | 5 - src/web/app/dev/tags/new-app-form.tag | 252 ------------------------- src/web/app/dev/tags/pages/app.tag | 32 ---- src/web/app/dev/tags/pages/apps.tag | 33 ---- src/web/app/dev/tags/pages/index.tag | 6 - src/web/app/dev/tags/pages/new-app.tag | 42 ----- src/web/app/dev/views/app.vue | 43 +++++ src/web/app/dev/views/apps.vue | 24 +++ src/web/app/dev/views/index.vue | 10 + src/web/app/dev/views/new-app.vue | 145 ++++++++++++++ src/web/app/init.ts | 2 +- webpack/webpack.config.ts | 4 +- 15 files changed, 272 insertions(+), 377 deletions(-) create mode 100644 src/web/app/auth/script.ts delete mode 100644 src/web/app/dev/tags/index.ts delete mode 100644 src/web/app/dev/tags/new-app-form.tag delete mode 100644 src/web/app/dev/tags/pages/app.tag delete mode 100644 src/web/app/dev/tags/pages/apps.tag delete mode 100644 src/web/app/dev/tags/pages/index.tag delete mode 100644 src/web/app/dev/tags/pages/new-app.tag create mode 100644 src/web/app/dev/views/app.vue create mode 100644 src/web/app/dev/views/apps.vue create mode 100644 src/web/app/dev/views/index.vue create mode 100644 src/web/app/dev/views/new-app.vue diff --git a/src/web/app/auth/script.ts b/src/web/app/auth/script.ts new file mode 100644 index 0000000000..31c758ebc2 --- /dev/null +++ b/src/web/app/auth/script.ts @@ -0,0 +1,25 @@ +/** + * Authorize Form + */ + +// Style +import './style.styl'; + +import init from '../init'; + +import Index from './views/index.vue'; + +/** + * init + */ +init(async (launch) => { + document.title = 'Misskey | アプリの連携'; + + // Launch the app + const [app] = launch(); + + // Routing + app.$router.addRoutes([ + { path: '/:token', component: Index }, + ]); +}); diff --git a/src/web/app/auth/views/index.vue b/src/web/app/auth/views/index.vue index 1e372c0bde..17e5cc6108 100644 --- a/src/web/app/auth/views/index.vue +++ b/src/web/app/auth/views/index.vue @@ -42,10 +42,14 @@ export default Vue.extend({ return { state: null, session: null, - fetching: true, - token: window.location.href.split('/').pop() + fetching: true }; }, + computed: { + token(): string { + return this.$route.params.token; + } + }, mounted() { if (!this.$root.$data.os.isSignedIn) return; diff --git a/src/web/app/dev/script.ts b/src/web/app/dev/script.ts index bb43411195..757bfca490 100644 --- a/src/web/app/dev/script.ts +++ b/src/web/app/dev/script.ts @@ -5,11 +5,25 @@ // Style import './style.styl'; -require('./tags'); import init from '../init'; +import Index from './views/index.vue'; +import Apps from './views/apps.vue'; +import AppNew from './views/new-app.vue'; +import App from './views/app.vue'; + /** * init */ -init(() => { +init(launch => { + // Launch the app + const [app] = launch(); + + // Routing + app.$router.addRoutes([ + { path: '/', component: Index }, + { path: '/app', component: Apps }, + { path: '/app/new', component: AppNew }, + { path: '/app/:id', component: App }, + ]); }); diff --git a/src/web/app/dev/tags/index.ts b/src/web/app/dev/tags/index.ts deleted file mode 100644 index 1e0c73697e..0000000000 --- a/src/web/app/dev/tags/index.ts +++ /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/new-app-form.tag b/src/web/app/dev/tags/new-app-form.tag deleted file mode 100644 index cf3c440079..0000000000 --- a/src/web/app/dev/tags/new-app-form.tag +++ /dev/null @@ -1,252 +0,0 @@ -<mk-new-app-form> - <form onsubmit={ onsubmit } autocomplete="off"> - <section class="name"> - <label> - <p class="caption">アプリケーション名</p> - <input ref="name" type="text" placeholder="ex) Misskey for iOS" autocomplete="off" required="required"/> - </label> - </section> - <section class="nid"> - <label> - <p class="caption">Named ID</p> - <input ref="nid" type="text" pattern="^[a-zA-Z0-9-]{3,30}$" placeholder="ex) misskey-for-ios" autocomplete="off" required="required" onkeyup={ onChangeNid }/> - <p class="info" v-if="nidState == 'wait'" style="color:#999">%fa:spinner .pulse .fw%確認しています...</p> - <p class="info" v-if="nidState == 'ok'" style="color:#3CB7B5">%fa:fw check%利用できます</p> - <p class="info" v-if="nidState == 'unavailable'" style="color:#FF1161">%fa:fw exclamation-triangle%既に利用されています</p> - <p class="info" v-if="nidState == 'error'" style="color:#FF1161">%fa:fw exclamation-triangle%通信エラー</p> - <p class="info" v-if="nidState == 'invalid-format'" style="color:#FF1161">%fa:fw exclamation-triangle%a~z、A~Z、0~9、-(ハイフン)が使えます</p> - <p class="info" v-if="nidState == 'min-range'" style="color:#FF1161">%fa:fw exclamation-triangle%3文字以上でお願いします!</p> - <p class="info" v-if="nidState == 'max-range'" style="color:#FF1161">%fa:fw exclamation-triangle%30文字以内でお願いします</p> - </label> - </section> - <section class="description"> - <label> - <p class="caption">アプリの概要</p> - <textarea ref="description" placeholder="ex) Misskey iOSクライアント。" autocomplete="off" required="required"></textarea> - </label> - </section> - <section class="callback"> - <label> - <p class="caption">コールバックURL (オプション)</p> - <input ref="cb" type="url" placeholder="ex) https://your.app.example.com/callback.php" autocomplete="off"/> - </label> - </section> - <section class="permission"> - <p class="caption">権限</p> - <div ref="permission"> - <label> - <input type="checkbox" value="account-read"/> - <p>アカウントの情報を見る。</p> - </label> - <label> - <input type="checkbox" value="account-write"/> - <p>アカウントの情報を操作する。</p> - </label> - <label> - <input type="checkbox" value="post-write"/> - <p>投稿する。</p> - </label> - <label> - <input type="checkbox" value="reaction-write"/> - <p>リアクションしたりリアクションをキャンセルする。</p> - </label> - <label> - <input type="checkbox" value="following-write"/> - <p>フォローしたりフォロー解除する。</p> - </label> - <label> - <input type="checkbox" value="drive-read"/> - <p>ドライブを見る。</p> - </label> - <label> - <input type="checkbox" value="drive-write"/> - <p>ドライブを操作する。</p> - </label> - <label> - <input type="checkbox" value="notification-read"/> - <p>通知を見る。</p> - </label> - <label> - <input type="checkbox" value="notification-write"/> - <p>通知を操作する。</p> - </label> - </div> - <p>%fa:exclamation-triangle%アプリ作成後も変更できますが、新たな権限を付与する場合、その時点で関連付けられているユーザーキーはすべて無効になります。</p> - </section> - <button @click="onsubmit">アプリ作成</button> - </form> - <style lang="stylus" scoped> - :scope - display block - overflow hidden - - > form - - section - display block - margin 16px 0 - - .caption - margin 0 0 4px 0 - color #616161 - font-size 0.95em - - > [data-fa] - margin-right 0.25em - color #96adac - - .info - display block - margin 4px 0 - font-size 0.8em - - > [data-fa] - margin-right 0.3em - - section.permission - div - padding 8px 0 - max-height 160px - overflow auto - background #fff - border solid 1px #cecece - border-radius 4px - - label - display block - padding 0 12px - line-height 32px - cursor pointer - - &:hover - > p - color #999 - - [type='checkbox']:checked + p - color #000 - - [type='checkbox'] - margin-right 4px - - [type='checkbox']:checked + p - color #111 - - > p - display inline - color #aaa - user-select none - - > p:last-child - margin 6px - font-size 0.8em - color #999 - - > [data-fa] - margin-right 4px - - [type=text] - [type=url] - textarea - user-select text - display inline-block - cursor auto - padding 8px 12px - margin 0 - width 100% - font-size 1em - color #333 - background #fff - outline none - border solid 1px #cecece - border-radius 4px - - &:hover - border-color #bbb - - &:focus - border-color $theme-color - - &:disabled - opacity 0.5 - - > button - margin 20px 0 32px 0 - width 100% - font-size 1em - color #111 - border-radius 3px - - </style> - <script lang="typescript"> - this.mixin('api'); - - this.nidState = null; - - this.onChangeNid = () => { - const nid = this.$refs.nid.value; - - if (nid == '') { - this.update({ - nidState: null - }); - return; - } - - const err = - !nid.match(/^[a-zA-Z0-9\-]+$/) ? 'invalid-format' : - nid.length < 3 ? 'min-range' : - nid.length > 30 ? 'max-range' : - null; - - if (err) { - this.update({ - nidState: err - }); - return; - } - - this.update({ - nidState: 'wait' - }); - - this.$root.$data.os.api('app/name_id/available', { - name_id: nid - }).then(result => { - this.update({ - nidState: result.available ? 'ok' : 'unavailable' - }); - }).catch(err => { - this.update({ - nidState: 'error' - }); - }); - }; - - this.onsubmit = () => { - const name = this.$refs.name.value; - const nid = this.$refs.nid.value; - const description = this.$refs.description.value; - const cb = this.$refs.cb.value; - const permission = []; - - this.$refs.permission.querySelectorAll('input').forEach(el => { - if (el.checked) permission.push(el.value); - }); - - const locker = document.body.appendChild(document.createElement('mk-locker')); - - this.$root.$data.os.api('app/create', { - name: name, - name_id: nid, - description: description, - callback_url: cb, - permission: permission - }).then(() => { - location.href = '/apps'; - }).catch(() => { - alert('アプリの作成に失敗しました。再度お試しください。'); - locker.parentNode.removeChild(locker); - }); - }; - </script> -</mk-new-app-form> diff --git a/src/web/app/dev/tags/pages/app.tag b/src/web/app/dev/tags/pages/app.tag deleted file mode 100644 index 982549ed2b..0000000000 --- a/src/web/app/dev/tags/pages/app.tag +++ /dev/null @@ -1,32 +0,0 @@ -<mk-app-page> - <p v-if="fetching">読み込み中</p> - <main v-if="!fetching"> - <header> - <h1>{ app.name }</h1> - </header> - <div class="body"> - <p>App Secret</p> - <input value={ app.secret } readonly="readonly"/> - </div> - </main> - <style lang="stylus" scoped> - :scope - display block - </style> - <script lang="typescript"> - this.mixin('api'); - - this.fetching = true; - - this.on('mount', () => { - this.$root.$data.os.api('app/show', { - app_id: this.opts.app - }).then(app => { - this.update({ - fetching: false, - app: app - }); - }); - }); - </script> -</mk-app-page> diff --git a/src/web/app/dev/tags/pages/apps.tag b/src/web/app/dev/tags/pages/apps.tag deleted file mode 100644 index 6ae6031e64..0000000000 --- a/src/web/app/dev/tags/pages/apps.tag +++ /dev/null @@ -1,33 +0,0 @@ -<mk-apps-page> - <h1>アプリを管理</h1><a href="/app/new">アプリ作成</a> - <div class="apps"> - <p v-if="fetching">読み込み中</p> - <template v-if="!fetching"> - <p v-if="apps.length == 0">アプリなし</p> - <ul v-if="apps.length > 0"> - <li each={ app in apps }><a href={ '/app/' + app.id }> - <p class="name">{ app.name }</p></a></li> - </ul> - </template> - </div> - <style lang="stylus" scoped> - :scope - display block - </style> - <script lang="typescript"> - this.mixin('api'); - - this.fetching = true; - - this.on('mount', () => { - this.$root.$data.os.api('my/apps').then(apps => { - this.fetching = false - this.apps = apps - this.update({ - fetching: false, - apps: apps - }); - }); - }); - </script> -</mk-apps-page> diff --git a/src/web/app/dev/tags/pages/index.tag b/src/web/app/dev/tags/pages/index.tag deleted file mode 100644 index ca270b3774..0000000000 --- a/src/web/app/dev/tags/pages/index.tag +++ /dev/null @@ -1,6 +0,0 @@ -<mk-index><a href="/apps">アプリ</a> - <style lang="stylus" scoped> - :scope - display block - </style> -</mk-index> diff --git a/src/web/app/dev/tags/pages/new-app.tag b/src/web/app/dev/tags/pages/new-app.tag deleted file mode 100644 index 26185f278b..0000000000 --- a/src/web/app/dev/tags/pages/new-app.tag +++ /dev/null @@ -1,42 +0,0 @@ -<mk-new-app-page> - <main> - <header> - <h1>新しいアプリを作成</h1> - <p>MisskeyのAPIを利用したアプリケーションを作成できます。</p> - </header> - <mk-new-app-form/> - </main> - <style lang="stylus" scoped> - :scope - display block - padding 64px 0 - - > main - width 100% - max-width 700px - margin 0 auto - - > header - margin 0 0 16px 0 - padding 0 0 16px 0 - border-bottom solid 1px #282827 - - > h1 - margin 0 0 12px 0 - padding 0 - line-height 32px - font-size 32px - font-weight normal - color #000 - - > p - margin 0 - line-height 16px - color #9a9894 - - - - - - </style> -</mk-new-app-page> diff --git a/src/web/app/dev/views/app.vue b/src/web/app/dev/views/app.vue new file mode 100644 index 0000000000..9eddabbec2 --- /dev/null +++ b/src/web/app/dev/views/app.vue @@ -0,0 +1,43 @@ +<template> +<div> + <p v-if="fetching">読み込み中</p> + <main v-if="!fetching"> + <header> + <h1>{{ app.name }}</h1> + </header> + <div class="body"> + <p>App Secret</p> + <input :value="app.secret" readonly/> + </div> + </main> +</div> +</template> + +<script lang="ts"> +import Vue from 'vue'; +export default Vue.extend({ + data() { + return { + fetching: true, + app: null + }; + }, + watch: { + $route: 'fetch' + }, + mounted() { + this.fetch(); + }, + methods: { + fetch() { + this.fetching = true; + (this as any).api('app/show', { + app_id: this.$route.params.id + }).then(app => { + this.app = app; + this.fetching = false; + }); + } + } +}); +</script> diff --git a/src/web/app/dev/views/apps.vue b/src/web/app/dev/views/apps.vue new file mode 100644 index 0000000000..e8adbea2da --- /dev/null +++ b/src/web/app/dev/views/apps.vue @@ -0,0 +1,24 @@ +<template> +<div> + <h1>アプリを管理</h1> + <router-link to="/app/new">アプリ作成</router-link> + <div class="apps"> + <p v-if="fetching">読み込み中</p> + <template v-if="!fetching"> + <p v-if="apps.length == 0">アプリなし</p> + <ul v-else> + <li v-for="app in apps" :key="app.id"> + <router-link :to="`/app/${app.id}`"> + <p class="name">{{ app.name }}</p> + </router-link> + </li> + </ul> + </template> + </div> +</div> +</template> + +<script lang="ts"> +import Vue from 'vue'; +export default Vue.extend(); +</script> diff --git a/src/web/app/dev/views/index.vue b/src/web/app/dev/views/index.vue new file mode 100644 index 0000000000..a8429a56dd --- /dev/null +++ b/src/web/app/dev/views/index.vue @@ -0,0 +1,10 @@ +<template> +<div> + <router-link to="/app">アプリ</router-link> +</div> +</template> + +<script lang="ts"> +import Vue from 'vue'; +export default Vue.extend(); +</script> diff --git a/src/web/app/dev/views/new-app.vue b/src/web/app/dev/views/new-app.vue new file mode 100644 index 0000000000..f2e5659a4e --- /dev/null +++ b/src/web/app/dev/views/new-app.vue @@ -0,0 +1,145 @@ +<template> +<div> + <form @submit="onSubmit" autocomplete="off"> + <section class="name"> + <label> + <p class="caption">アプリケーション名</p> + <input v-model="name" type="text" placeholder="ex) Misskey for iOS" autocomplete="off" required/> + </label> + </section> + <section class="nid"> + <label> + <p class="caption">Named ID</p> + <input v-model="nid" type="text" pattern="^[a-zA-Z0-9-]{3,30}$" placeholder="ex) misskey-for-ios" autocomplete="off" required/> + <p class="info" v-if="nidState == 'wait'" style="color:#999">%fa:spinner .pulse .fw%確認しています...</p> + <p class="info" v-if="nidState == 'ok'" style="color:#3CB7B5">%fa:fw check%利用できます</p> + <p class="info" v-if="nidState == 'unavailable'" style="color:#FF1161">%fa:fw exclamation-triangle%既に利用されています</p> + <p class="info" v-if="nidState == 'error'" style="color:#FF1161">%fa:fw exclamation-triangle%通信エラー</p> + <p class="info" v-if="nidState == 'invalid-format'" style="color:#FF1161">%fa:fw exclamation-triangle%a~z、A~Z、0~9、-(ハイフン)が使えます</p> + <p class="info" v-if="nidState == 'min-range'" style="color:#FF1161">%fa:fw exclamation-triangle%3文字以上でお願いします!</p> + <p class="info" v-if="nidState == 'max-range'" style="color:#FF1161">%fa:fw exclamation-triangle%30文字以内でお願いします</p> + </label> + </section> + <section class="description"> + <label> + <p class="caption">アプリの概要</p> + <textarea v-model="description" placeholder="ex) Misskey iOSクライアント。" autocomplete="off" required></textarea> + </label> + </section> + <section class="callback"> + <label> + <p class="caption">コールバックURL (オプション)</p> + <input v-model="cb" type="url" placeholder="ex) https://your.app.example.com/callback.php" autocomplete="off"/> + </label> + </section> + <section class="permission"> + <p class="caption">権限</p> + <div ref="permission"> + <label> + <input type="checkbox" value="account-read"/> + <p>アカウントの情報を見る。</p> + </label> + <label> + <input type="checkbox" value="account-write"/> + <p>アカウントの情報を操作する。</p> + </label> + <label> + <input type="checkbox" value="post-write"/> + <p>投稿する。</p> + </label> + <label> + <input type="checkbox" value="reaction-write"/> + <p>リアクションしたりリアクションをキャンセルする。</p> + </label> + <label> + <input type="checkbox" value="following-write"/> + <p>フォローしたりフォロー解除する。</p> + </label> + <label> + <input type="checkbox" value="drive-read"/> + <p>ドライブを見る。</p> + </label> + <label> + <input type="checkbox" value="drive-write"/> + <p>ドライブを操作する。</p> + </label> + <label> + <input type="checkbox" value="notification-read"/> + <p>通知を見る。</p> + </label> + <label> + <input type="checkbox" value="notification-write"/> + <p>通知を操作する。</p> + </label> + </div> + <p>%fa:exclamation-triangle%アプリ作成後も変更できますが、新たな権限を付与する場合、その時点で関連付けられているユーザーキーはすべて無効になります。</p> + </section> + <button type="submit">アプリ作成</button> + </form> +</div> +</template> + +<script lang="ts"> +import Vue from 'vue'; +export default Vue.extend({ + data() { + return { + name: '', + nid: '', + description: '', + cb: '', + nidState: null + }; + }, + watch: { + nid() { + if (this.nid == null || this.nid == '') { + this.nidState = null; + return; + } + + const err = + !this.nid.match(/^[a-zA-Z0-9\-]+$/) ? 'invalid-format' : + this.nid.length < 3 ? 'min-range' : + this.nid.length > 30 ? 'max-range' : + null; + + if (err) { + this.nidState = err; + return; + } + + this.nidState = 'wait'; + + (this as any).api('app/name_id/available', { + name_id: this.nid + }).then(result => { + this.nidState = result.available ? 'ok' : 'unavailable'; + }).catch(err => { + this.nidState = 'error'; + }); + } + }, + methods: { + onSubmit() { + const permission = []; + + (this.$refs.permission as any).querySelectorAll('input').forEach(el => { + if (el.checked) permission.push(el.value); + }); + + (this as any).api('app/create', { + name: this.name, + name_id: this.nid, + description: this.description, + callback_url: this.cb, + permission: permission + }).then(() => { + location.href = '/apps'; + }).catch(() => { + alert('アプリの作成に失敗しました。再度お試しください。'); + }); + } + } +}); +</script> diff --git a/src/web/app/init.ts b/src/web/app/init.ts index 5c0b4c2d39..2e90d62d79 100644 --- a/src/web/app/init.ts +++ b/src/web/app/init.ts @@ -77,7 +77,7 @@ if (localStorage.getItem('should-refresh') == 'true') { } // MiOSを初期化してコールバックする -export default (callback: (launch: (api: (os: MiOS) => API) => [Vue, MiOS]) => void, sw = false) => { +export default (callback: (launch: (api?: (os: MiOS) => API) => [Vue, MiOS]) => void, sw = false) => { const os = new MiOS(sw); os.init(() => { diff --git a/webpack/webpack.config.ts b/webpack/webpack.config.ts index 69e05dc8c6..775ecbd348 100644 --- a/webpack/webpack.config.ts +++ b/webpack/webpack.config.ts @@ -38,8 +38,8 @@ module.exports = Object.keys(langs).map(lang => { //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', + dev: './src/web/app/dev/script.ts', + auth: './src/web/app/auth/script.ts', sw: './src/web/app/sw.js' };