diff options
| author | tamaina <tamaina@hotmail.co.jp> | 2018-04-11 20:27:09 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-04-11 20:27:09 +0900 |
| commit | d43fe853c3605696e2e57e240845d0fc9c284f61 (patch) | |
| tree | 838914e262c0fca5737588a7bba64e2b9f3d8e5f /src/web | |
| parent | Update README.md (diff) | |
| parent | wip #1443 (diff) | |
| download | misskey-d43fe853c3605696e2e57e240845d0fc9c284f61.tar.gz misskey-d43fe853c3605696e2e57e240845d0fc9c284f61.tar.bz2 misskey-d43fe853c3605696e2e57e240845d0fc9c284f61.zip | |
Merge pull request #1 from syuilo/master
追従
Diffstat (limited to 'src/web')
290 files changed, 0 insertions, 29103 deletions
diff --git a/src/web/app/app.styl b/src/web/app/app.styl deleted file mode 100644 index de66df74d4..0000000000 --- a/src/web/app/app.styl +++ /dev/null @@ -1,173 +0,0 @@ -json('../../const.json') - -@charset 'utf-8' - -$theme-color = themeColor -$theme-color-foreground = themeColorForeground - -/* - ::selection - background $theme-color - color #fff -*/ - -* - position relative - box-sizing border-box - background-clip padding-box !important - tap-highlight-color rgba($theme-color, 0.7) - -webkit-tap-highlight-color rgba($theme-color, 0.7) - -html, body - margin 0 - padding 0 - scroll-behavior smooth - text-size-adjust 100% - font-family sans-serif - -html - &.progress - &, * - cursor progress !important - -body - overflow-wrap break-word - -#error - padding 32px - color #fff - - hr - border solid 1px #fff - -#nprogress - pointer-events none - - position absolute - z-index 65536 - - .bar - background $theme-color - - position fixed - z-index 65537 - top 0 - left 0 - - width 100% - height 2px - - /* Fancy blur effect */ - .peg - display block - position absolute - right 0px - width 100px - height 100% - box-shadow 0 0 10px $theme-color, 0 0 5px $theme-color - opacity 1 - - transform rotate(3deg) translate(0px, -4px) - -#wait - display block - position fixed - z-index 65537 - top 15px - right 15px - - &:before - content "" - display block - width 18px - height 18px - box-sizing border-box - - border solid 2px transparent - border-top-color $theme-color - border-left-color $theme-color - border-radius 50% - - animation progress-spinner 400ms linear infinite - - @keyframes progress-spinner - 0% - transform rotate(0deg) - 100% - transform rotate(360deg) - -a - text-decoration none - color $theme-color - cursor pointer - - &:hover - text-decoration underline - - * - cursor pointer - -code - font-family Consolas, 'Courier New', Courier, Monaco, monospace - - .comment - opacity 0.5 - - .string - color #e96900 - - .regexp - color #e9003f - - .keyword - color #2973b7 - - &.true - &.false - &.null - &.nil - &.undefined - color #ae81ff - - .symbol - color #42b983 - - .number - .nan - color #ae81ff - - .var:not(.keyword) - font-weight bold - font-style italic - //text-decoration underline - - .method - font-style italic - color #8964c1 - - .property - color #a71d5d - - .label - color #e9003f - -pre - display block - - > code - display block - overflow auto - tab-size 2 - -mk-locker - display block - position fixed - top 0 - left 0 - z-index 65536 - width 100% - height 100% - cursor wait - -[data-fa] - display inline-block diff --git a/src/web/app/auth/assets/logo.svg b/src/web/app/auth/assets/logo.svg deleted file mode 100644 index 19b8a2737e..0000000000 --- a/src/web/app/auth/assets/logo.svg +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
- y="0px" width="1024px" height="512px" viewBox="0 256 1024 512" enable-background="new 0 256 1024 512" xml:space="preserve">
-<polyline opacity="0.5" fill="none" stroke="#000000" stroke-width="34" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="
- 896.5,608.5 800.5,416.5 704.5,608.5 608.5,416.5 512.5,608.5 416.5,416.5 320.5,608.5 224.5,416.5 128.5,608.5 "/>
-</svg>
diff --git a/src/web/app/auth/script.ts b/src/web/app/auth/script.ts deleted file mode 100644 index dd598d1ed6..0000000000 --- a/src/web/app/auth/script.ts +++ /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(() => { - mount(document.createElement('mk-index')); -}); - -function mount(content) { - riot.mount(document.getElementById('app').appendChild(content)); -} diff --git a/src/web/app/auth/style.styl b/src/web/app/auth/style.styl deleted file mode 100644 index bd25e1b572..0000000000 --- a/src/web/app/auth/style.styl +++ /dev/null @@ -1,15 +0,0 @@ -@import "../app" -@import "../reset" - -html - background #eee - - @media (max-width 600px) - background #fff - -body - margin 0 - padding 32px 0 - - @media (max-width 600px) - padding 0 diff --git a/src/web/app/auth/tags/form.tag b/src/web/app/auth/tags/form.tag deleted file mode 100644 index 4a236f7594..0000000000 --- a/src/web/app/auth/tags/form.tag +++ /dev/null @@ -1,130 +0,0 @@ -<mk-form> - <header> - <h1><i>{ app.name }</i>があなたの<b>アカウント</b>に<b>アクセス</b>することを<b>許可</b>しますか?</h1><img src={ app.icon_url + '?thumbnail&size=64' }/> - </header> - <div class="app"> - <section> - <h2>{ app.name }</h2> - <p class="nid">{ app.name_id }</p> - <p class="description">{ app.description }</p> - </section> - <section> - <h2>このアプリは次の権限を要求しています:</h2> - <ul> - <virtual each={ p in app.permission }> - <li if={ p == 'account-read' }>アカウントの情報を見る。</li> - <li if={ p == 'account-write' }>アカウントの情報を操作する。</li> - <li if={ p == 'post-write' }>投稿する。</li> - <li if={ p == 'like-write' }>いいねしたりいいね解除する。</li> - <li if={ p == 'following-write' }>フォローしたりフォロー解除する。</li> - <li if={ p == 'drive-read' }>ドライブを見る。</li> - <li if={ p == 'drive-write' }>ドライブを操作する。</li> - <li if={ p == 'notification-read' }>通知を見る。</li> - <li if={ p == 'notification-write' }>通知を操作する。</li> - </virtual> - </ul> - </section> - </div> - <div class="action"> - <button onclick={ cancel }>キャンセル</button> - <button onclick={ accept }>アクセスを許可</button> - </div> - <style> - :scope - display block - - > header - > h1 - margin 0 - padding 32px 32px 20px 32px - font-size 24px - font-weight normal - color #777 - - i - color #77aeca - - &:before - content '「' - - &:after - content '」' - - b - color #666 - - > img - display block - z-index 1 - width 84px - height 84px - margin 0 auto -38px auto - border solid 5px #fff - border-radius 100% - box-shadow 0 2px 2px rgba(0, 0, 0, 0.1) - - > .app - padding 44px 16px 0 16px - color #555 - background #eee - box-shadow 0 2px 2px rgba(0, 0, 0, 0.1) inset - - &:after - content '' - display block - clear both - - > section - float left - width 50% - padding 8px - text-align left - - > h2 - margin 0 - font-size 16px - color #777 - - > .action - padding 16px - - > button - margin 0 8px - - @media (max-width 600px) - > header - > img - box-shadow none - - > .app - box-shadow none - - @media (max-width 500px) - > header - > h1 - font-size 16px - - </style> - <script> - this.mixin('api'); - - this.session = this.opts.session; - this.app = this.session.app; - - this.cancel = () => { - this.api('auth/deny', { - token: this.session.token - }).then(() => { - this.trigger('denied'); - }); - }; - - this.accept = () => { - this.api('auth/accept', { - token: this.session.token - }).then(() => { - this.trigger('accepted'); - }); - }; - </script> -</mk-form> diff --git a/src/web/app/auth/tags/index.tag b/src/web/app/auth/tags/index.tag deleted file mode 100644 index e71214f4a3..0000000000 --- a/src/web/app/auth/tags/index.tag +++ /dev/null @@ -1,143 +0,0 @@ -<mk-index> - <main if={ SIGNIN }> - <p class="fetching" if={ fetching }>読み込み中<mk-ellipsis/></p> - <mk-form ref="form" if={ state == 'waiting' } session={ session }/> - <div class="denied" if={ state == 'denied' }> - <h1>アプリケーションの連携をキャンセルしました。</h1> - <p>このアプリがあなたのアカウントにアクセスすることはありません。</p> - </div> - <div class="accepted" if={ state == 'accepted' }> - <h1>{ session.app.is_authorized ? 'このアプリは既に連携済みです' : 'アプリケーションの連携を許可しました'}</h1> - <p if={ session.app.callback_url }>アプリケーションに戻っています<mk-ellipsis/></p> - <p if={ !session.app.callback_url }>アプリケーションに戻って、やっていってください。</p> - </div> - <div class="error" if={ state == 'fetch-session-error' }> - <p>セッションが存在しません。</p> - </div> - </main> - <main class="signin" if={ !SIGNIN }> - <h1>サインインしてください</h1> - <mk-signin/> - </main> - <footer><img src="/assets/auth/logo.svg" alt="Misskey"/></footer> - <style> - :scope - display block - - > main - width 100% - max-width 500px - margin 0 auto - text-align center - background #fff - box-shadow 0px 4px 16px rgba(0, 0, 0, 0.2) - - > .fetching - margin 0 - padding 32px - color #555 - - > div - padding 64px - - > h1 - margin 0 0 8px 0 - padding 0 - font-size 20px - font-weight normal - - > p - margin 0 - color #555 - - &.denied > h1 - color #e65050 - - &.accepted > h1 - color #54af7c - - &.signin - padding 32px 32px 16px 32px - - > h1 - margin 0 0 22px 0 - padding 0 - font-size 20px - font-weight normal - color #555 - - @media (max-width 600px) - max-width none - box-shadow none - - @media (max-width 500px) - > div - > h1 - font-size 16px - - > footer - > img - display block - width 64px - height 64px - margin 0 auto - - </style> - <script> - this.mixin('i'); - this.mixin('api'); - - this.state = null; - this.fetching = true; - - this.token = window.location.href.split('/').pop(); - - this.on('mount', () => { - if (!this.SIGNIN) return; - - // Fetch session - this.api('auth/session/show', { - token: this.token - }).then(session => { - this.session = session; - this.fetching = false; - - // 既に連携していた場合 - if (this.session.app.is_authorized) { - this.api('auth/accept', { - token: this.session.token - }).then(() => { - this.accepted(); - }); - } else { - this.update({ - state: 'waiting' - }); - - this.refs.form.on('denied', () => { - this.update({ - state: 'denied' - }); - }); - - this.refs.form.on('accepted', this.accepted); - } - }).catch(error => { - this.update({ - fetching: false, - state: 'fetch-session-error' - }); - }); - }); - - this.accepted = () => { - this.update({ - state: 'accepted' - }); - - if (this.session.app.callback_url) { - location.href = this.session.app.callback_url + '?token=' + this.session.token; - } - }; - </script> -</mk-index> diff --git a/src/web/app/auth/tags/index.ts b/src/web/app/auth/tags/index.ts deleted file mode 100644 index 42dffe67d9..0000000000 --- a/src/web/app/auth/tags/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -require('./index.tag'); -require('./form.tag'); diff --git a/src/web/app/base.pug b/src/web/app/base.pug deleted file mode 100644 index d7c7f0aed4..0000000000 --- a/src/web/app/base.pug +++ /dev/null @@ -1,38 +0,0 @@ -doctype html - -!= '\n<!-- Thank you for using Misskey! @syuilo -->\n' - -html - - head - meta(charset='utf-8') - meta(name='application-name' content='Misskey') - meta(name='theme-color' content=themeColor) - meta(name='referrer' content='origin') - link(rel='manifest' href='/manifest.json') - - title Misskey - - style - include ./../../../built/web/assets/init.css - script - include ./../../../built/web/assets/boot.js - - script - include ./../../../built/web/assets/safe.js - - //- FontAwesome style - style #{facss} - - //- highlight.js style - style #{hljscss} - - body - noscript: p - | JavaScriptを有効にしてください - br - | Please turn on your JavaScript - div#ini: p - span . - span . - span . diff --git a/src/web/app/boot.js b/src/web/app/boot.js deleted file mode 100644 index 211dc2f883..0000000000 --- a/src/web/app/boot.js +++ /dev/null @@ -1,110 +0,0 @@ -/** - * MISSKEY BOOT LOADER - * (ENTRY POINT) - */ - -/** - * ドメインに基づいて適切なスクリプトを読み込みます。 - * ユーザーの言語およびモバイル端末か否かも考慮します。 - * webpackは介さないためrequireやimportは使えません。 - */ - -'use strict'; - -// Chromeで確認したことなのですが、constやletを用いたとしても -// グローバルなスコープで定数/変数を定義するとwindowのプロパティ -// としてそれがアクセスできるようになる訳ではありませんが、普通に -// コンソールから定数/変数名を入力するとアクセスできてしまいます。 -// ブロック内に入れてスコープを非グローバル化するとそれが防げます -// (Chrome以外のブラウザでは検証していません) -{ - // Get the current url information - const url = new URL(location.href); - - // Extarct the (sub) domain part of the current url - // - // e.g. - // misskey.alice => misskey - // misskey.strawberry.pasta => misskey - // dev.misskey.arisu.tachibana => dev - let app = url.host == 'localhost' - ? 'misskey' - : url.host.split('.')[0]; - - // Detect the user language - // Note: The default language is English - let lang = navigator.language.split('-')[0]; - if (!/^(en|ja)$/.test(lang)) lang = 'en'; - - // Detect the user agent - const ua = navigator.userAgent.toLowerCase(); - const isMobile = /mobile|iphone|ipad|android/.test(ua); - - // Get the <head> element - const head = document.getElementsByTagName('head')[0]; - - // If mobile, insert the viewport meta tag - if (isMobile) { - const meta = document.createElement('meta'); - meta.setAttribute('name', 'viewport'); - meta.setAttribute('content', - 'width=device-width,' + - 'initial-scale=1,' + - 'minimum-scale=1,' + - 'maximum-scale=1,' + - 'user-scalable=no'); - head.appendChild(meta); - } - - // Switch desktop or mobile version - if (app == 'misskey') { - app = isMobile ? 'mobile' : 'desktop'; - } - - // Load an app script - // Note: 'async' make it possible to load the script asyncly. - // 'defer' make it possible to run the script when the dom loaded. - const script = document.createElement('script'); - script.setAttribute('src', `/assets/${app}.${VERSION}.${lang}.js`); - script.setAttribute('async', 'true'); - script.setAttribute('defer', 'true'); - head.appendChild(script); - - // 1秒経ってもスクリプトがロードされない場合はバージョンが古くて - // 404になっているせいかもしれないので、バージョンを確認して古ければ更新する - // - // 読み込まれたスクリプトからこのタイマーを解除できるように、 - // グローバルにタイマーIDを代入しておく - window.mkBootTimer = window.setTimeout(async () => { - // Fetch meta - const res = await fetch(API + '/meta', { - method: 'POST', - cache: 'no-cache' - }); - - // Parse - const meta = await res.json(); - - // Compare versions - if (meta.version != VERSION) { - alert( - 'Misskeyの新しいバージョンがあります。ページを再度読み込みします。' + - '\n\n' + - 'New version of Misskey available. The page will be reloaded.'); - - // Clear cache (serive worker) - try { - navigator.serviceWorker.controller.postMessage('clear'); - - navigator.serviceWorker.getRegistrations().then(registrations => { - registrations.forEach(registration => registration.unregister()); - }); - } catch (e) { - console.error(e); - } - - // Force reload - location.reload(true); - } - }, 1000); -} diff --git a/src/web/app/ch/router.ts b/src/web/app/ch/router.ts deleted file mode 100644 index f10c4acdf0..0000000000 --- a/src/web/app/ch/router.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as riot from 'riot'; -import * as route from 'page'; -let page = null; - -export default () => { - 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.ts b/src/web/app/ch/script.ts deleted file mode 100644 index e23558037c..0000000000 --- a/src/web/app/ch/script.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Channels - */ - -// Style -import './style.styl'; - -require('./tags'); -import init from '../init'; -import route from './router'; - -/** - * init - */ -init(() => { - // Start routing - route(); -}); diff --git a/src/web/app/ch/style.styl b/src/web/app/ch/style.styl deleted file mode 100644 index 21ca648cbe..0000000000 --- a/src/web/app/ch/style.styl +++ /dev/null @@ -1,10 +0,0 @@ -@import "../app" - -html - padding 8px - background #efefef - -#wait - top auto - bottom 15px - left 15px diff --git a/src/web/app/ch/tags/channel.tag b/src/web/app/ch/tags/channel.tag deleted file mode 100644 index cc8ce1ed9e..0000000000 --- a/src/web/app/ch/tags/channel.tag +++ /dev/null @@ -1,400 +0,0 @@ -<mk-channel> - <mk-header/> - <hr> - <main if={ !fetching }> - <h1>{ channel.title }</h1> - - <div if={ SIGNIN }> - <p if={ channel.is_watching }>このチャンネルをウォッチしています <a onclick={ unwatch }>ウォッチ解除</a></p> - <p if={ !channel.is_watching }><a onclick={ watch }>このチャンネルをウォッチする</a></p> - </div> - - <div class="share"> - <mk-twitter-button/> - <mk-line-button/> - </div> - - <div class="body"> - <p if={ postsFetching }>読み込み中<mk-ellipsis/></p> - <div if={ !postsFetching }> - <p if={ posts == null || posts.length == 0 }>まだ投稿がありません</p> - <virtual if={ posts != null }> - <mk-channel-post each={ post in posts.slice().reverse() } post={ post } form={ parent.refs.form }/> - </virtual> - </div> - </div> - <hr> - <mk-channel-form if={ SIGNIN } channel={ channel } ref="form"/> - <div if={ !SIGNIN }> - <p>参加するには<a href={ _URL_ }>ログインまたは新規登録</a>してください</p> - </div> - <hr> - <footer> - <small><a href={ _URL_ }>Misskey</a> ver { _VERSION_ } (葵 aoi)</small> - </footer> - </main> - <style> - :scope - display block - - > main - > h1 - font-size 1.5em - color #f00 - - > .share - > * - margin-right 4px - - > .body - margin 8px 0 0 0 - - > mk-channel-form - max-width 500px - - </style> - <script> - import Progress from '../../common/scripts/loading'; - import ChannelStream from '../../common/scripts/streaming/channel-stream'; - - this.mixin('i'); - this.mixin('api'); - - this.id = this.opts.id; - this.fetching = true; - this.postsFetching = true; - this.channel = null; - this.posts = null; - this.connection = new ChannelStream(this.id); - this.unreadCount = 0; - - this.on('mount', () => { - document.documentElement.style.background = '#efefef'; - - Progress.start(); - - let fetched = false; - - // チャンネル概要読み込み - this.api('channels/show', { - channel_id: this.id - }).then(channel => { - if (fetched) { - Progress.done(); - } else { - Progress.set(0.5); - fetched = true; - } - - this.update({ - fetching: false, - channel: channel - }); - - document.title = channel.title + ' | Misskey' - }); - - // 投稿読み込み - this.api('channels/posts', { - channel_id: this.id - }).then(posts => { - if (fetched) { - Progress.done(); - } else { - Progress.set(0.5); - fetched = true; - } - - this.update({ - postsFetching: false, - posts: posts - }); - }); - - this.connection.on('post', this.onPost); - document.addEventListener('visibilitychange', this.onVisibilitychange, false); - }); - - this.on('unmount', () => { - this.connection.off('post', this.onPost); - this.connection.close(); - document.removeEventListener('visibilitychange', this.onVisibilitychange); - }); - - this.onPost = post => { - this.posts.unshift(post); - this.update(); - - if (document.hidden && this.SIGNIN && post.user_id !== this.I.id) { - this.unreadCount++; - document.title = `(${this.unreadCount}) ${this.channel.title} | Misskey`; - } - }; - - this.onVisibilitychange = () => { - if (!document.hidden) { - this.unreadCount = 0; - document.title = this.channel.title + ' | Misskey' - } - }; - - this.watch = () => { - this.api('channels/watch', { - channel_id: this.id - }).then(() => { - this.channel.is_watching = true; - this.update(); - }, e => { - alert('error'); - }); - }; - - this.unwatch = () => { - this.api('channels/unwatch', { - channel_id: this.id - }).then(() => { - this.channel.is_watching = false; - this.update(); - }, e => { - alert('error'); - }); - }; - </script> -</mk-channel> - -<mk-channel-post> - <header> - <a class="index" onclick={ reply }>{ post.index }:</a> - <a class="name" href={ _URL_ + '/' + post.user.username }><b>{ post.user.name }</b></a> - <mk-time time={ post.created_at }/> - <mk-time time={ post.created_at } mode="detail"/> - <span>ID:<i>{ post.user.username }</i></span> - </header> - <div> - <a if={ post.reply }>>>{ post.reply.index }</a> - { post.text } - <div class="media" if={ post.media }> - <virtual each={ file in post.media }> - <a href={ file.url } target="_blank"> - <img src={ file.url + '?thumbnail&size=512' } alt={ file.name } title={ file.name }/> - </a> - </virtual> - </div> - </div> - <style> - :scope - display block - margin 0 - padding 0 - - > header - position -webkit-sticky - position sticky - z-index 1 - top 0 - background rgba(239, 239, 239, 0.9) - - > .index - margin-right 0.25em - color #000 - - > .name - margin-right 0.5em - color #008000 - - > mk-time - margin-right 0.5em - - &:first-of-type - display none - - @media (max-width 600px) - > mk-time - &:first-of-type - display initial - - &:last-of-type - display none - - > div - padding 0 0 1em 2em - - > .media - > a - display inline-block - - > img - max-width 100% - vertical-align bottom - - </style> - <script> - this.post = this.opts.post; - this.form = this.opts.form; - - this.reply = () => { - this.form.update({ - reply: this.post - }); - }; - </script> -</mk-channel-post> - -<mk-channel-form> - <p if={ reply }><b>>>{ reply.index }</b> ({ reply.user.name }): <a onclick={ clearReply }>[x]</a></p> - <textarea ref="text" disabled={ wait } oninput={ update } onkeydown={ onkeydown } onpaste={ onpaste } placeholder="%i18n:ch.tags.mk-channel-form.textarea%"></textarea> - <div class="actions"> - <button onclick={ selectFile }>%fa:upload%%i18n:ch.tags.mk-channel-form.upload%</button> - <button onclick={ drive }>%fa:cloud%%i18n:ch.tags.mk-channel-form.drive%</button> - <button class={ wait: wait } ref="submit" disabled={ wait || (refs.text.value.length == 0) } onclick={ post }> - <virtual if={ !wait }>%fa:paper-plane%</virtual>{ wait ? '%i18n:ch.tags.mk-channel-form.posting%' : '%i18n:ch.tags.mk-channel-form.post%' }<mk-ellipsis if={ wait }/> - </button> - </div> - <mk-uploader ref="uploader"/> - <ol if={ files }> - <li each={ files }>{ name }</li> - </ol> - <input ref="file" type="file" accept="image/*" multiple="multiple" onchange={ changeFile }/> - <style> - :scope - display block - - > textarea - width 100% - max-width 100% - min-width 100% - min-height 5em - - > .actions - display flex - - > button - > [data-fa] - margin-right 0.25em - - &:last-child - margin-left auto - - &.wait - cursor wait - - > input[type='file'] - display none - - </style> - <script> - this.mixin('api'); - - this.channel = this.opts.channel; - this.files = null; - - this.on('mount', () => { - this.refs.uploader.on('uploaded', file => { - this.update({ - files: [file] - }); - }); - }); - - this.upload = file => { - this.refs.uploader.upload(file); - }; - - this.clearReply = () => { - this.update({ - reply: null - }); - }; - - this.clear = () => { - this.clearReply(); - this.update({ - files: null - }); - this.refs.text.value = ''; - }; - - this.post = () => { - this.update({ - wait: true - }); - - const files = this.files && this.files.length > 0 - ? this.files.map(f => f.id) - : undefined; - - this.api('posts/create', { - text: this.refs.text.value == '' ? undefined : this.refs.text.value, - media_ids: files, - reply_id: this.reply ? this.reply.id : undefined, - channel_id: this.channel.id - }).then(data => { - this.clear(); - }).catch(err => { - alert('失敗した'); - }).then(() => { - this.update({ - wait: false - }); - }); - }; - - this.changeFile = () => { - Array.from(this.refs.file.files).forEach(this.upload); - }; - - this.selectFile = () => { - this.refs.file.click(); - }; - - this.drive = () => { - window['cb'] = files => { - this.update({ - files: files - }); - }; - - window.open(_URL_ + '/selectdrive?multiple=true', - 'drive_window', - 'height=500,width=800'); - }; - - this.onkeydown = e => { - if ((e.which == 10 || e.which == 13) && (e.ctrlKey || e.metaKey)) this.post(); - }; - - this.onpaste = e => { - Array.from(e.clipboardData.items).forEach(item => { - if (item.kind == 'file') { - this.upload(item.getAsFile()); - } - }); - }; - </script> -</mk-channel-form> - -<mk-twitter-button> - <a href="https://twitter.com/share?ref_src=twsrc%5Etfw" class="twitter-share-button" data-show-count="false">Tweet</a> - <script> - this.on('mount', () => { - const head = document.getElementsByTagName('head')[0]; - const script = document.createElement('script'); - script.setAttribute('src', 'https://platform.twitter.com/widgets.js'); - script.setAttribute('async', 'async'); - head.appendChild(script); - }); - </script> -</mk-twitter-button> - -<mk-line-button> - <div class="line-it-button" data-lang="ja" data-type="share-a" data-url={ _CH_URL_ } style="display: none;"></div> - <script> - this.on('mount', () => { - const head = document.getElementsByTagName('head')[0]; - const script = document.createElement('script'); - script.setAttribute('src', 'https://d.line-scdn.net/r/web/social-plugin/js/thirdparty/loader.min.js'); - script.setAttribute('async', 'async'); - head.appendChild(script); - }); - </script> -</mk-line-button> diff --git a/src/web/app/ch/tags/header.tag b/src/web/app/ch/tags/header.tag deleted file mode 100644 index dec83c9a5b..0000000000 --- a/src/web/app/ch/tags/header.tag +++ /dev/null @@ -1,20 +0,0 @@ -<mk-header> - <div> - <a href={ _CH_URL_ }>Index</a> | <a href={ _URL_ }>Misskey</a> - </div> - <div> - <a if={ !SIGNIN } href={ _URL_ }>ログイン(新規登録)</a> - <a if={ SIGNIN } href={ _URL_ + '/' + I.username }>{ I.username }</a> - </div> - <style> - :scope - display flex - - > div:last-child - margin-left auto - - </style> - <script> - this.mixin('i'); - </script> -</mk-header> diff --git a/src/web/app/ch/tags/index.tag b/src/web/app/ch/tags/index.tag deleted file mode 100644 index 5f3871802a..0000000000 --- a/src/web/app/ch/tags/index.tag +++ /dev/null @@ -1,37 +0,0 @@ -<mk-index> - <mk-header/> - <hr> - <button onclick={ n }>%i18n:ch.tags.mk-index.new%</button> - <hr> - <ul if={ channels }> - <li each={ channels }><a href={ '/' + this.id }>{ this.title }</a></li> - </ul> - <style> - :scope - display block - - </style> - <script> - this.mixin('api'); - - this.on('mount', () => { - this.api('channels', { - limit: 100 - }).then(channels => { - this.update({ - channels: channels - }); - }); - }); - - this.n = () => { - const title = window.prompt('%i18n:ch.tags.mk-index.channel-title%'); - - this.api('channels/create', { - title: title - }).then(channel => { - location.href = '/' + channel.id; - }); - }; - </script> -</mk-index> diff --git a/src/web/app/ch/tags/index.ts b/src/web/app/ch/tags/index.ts deleted file mode 100644 index 12ffdaeb84..0000000000 --- a/src/web/app/ch/tags/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -require('./index.tag'); -require('./channel.tag'); -require('./header.tag'); diff --git a/src/web/app/common/mios.ts b/src/web/app/common/mios.ts deleted file mode 100644 index 6ee42ea8a7..0000000000 --- a/src/web/app/common/mios.ts +++ /dev/null @@ -1,351 +0,0 @@ -import { EventEmitter } from 'eventemitter3'; -import * as riot from 'riot'; -import signout from './scripts/signout'; -import Progress from './scripts/loading'; -import HomeStreamManager from './scripts/streaming/home-stream-manager'; -import api from './scripts/api'; - -//#region environment variables -declare const _VERSION_: string; -declare const _LANG_: string; -declare const _API_URL_: string; -declare const _SW_PUBLICKEY_: string; -//#endregion - -/** - * Misskey Operating System - */ -export default class MiOS extends EventEmitter { - /** - * Misskeyの /meta で取得できるメタ情報 - */ - private meta: { - data: { [x: string]: any }; - chachedAt: Date; - }; - - private isMetaFetching = false; - - /** - * A signing user - */ - public i: { [x: string]: any }; - - /** - * Whether signed in - */ - public get isSignedin() { - return this.i != null; - } - - /** - * Whether is debug mode - */ - public get debug() { - return localStorage.getItem('debug') == 'true'; - } - - /** - * A connection manager of home stream - */ - public stream: HomeStreamManager; - - /** - * A registration of service worker - */ - private swRegistration: ServiceWorkerRegistration = null; - - /** - * Whether should register ServiceWorker - */ - private shouldRegisterSw: boolean; - - /** - * MiOSインスタンスを作成します - * @param shouldRegisterSw ServiceWorkerを登録するかどうか - */ - constructor(shouldRegisterSw = false) { - super(); - - this.shouldRegisterSw = shouldRegisterSw; - - //#region BIND - this.log = this.log.bind(this); - this.logInfo = this.logInfo.bind(this); - this.logWarn = this.logWarn.bind(this); - this.logError = this.logError.bind(this); - this.init = this.init.bind(this); - this.api = this.api.bind(this); - this.getMeta = this.getMeta.bind(this); - this.registerSw = this.registerSw.bind(this); - //#endregion - } - - public log(...args) { - if (!this.debug) return; - console.log.apply(null, args); - } - - public logInfo(...args) { - if (!this.debug) return; - console.info.apply(null, args); - } - - public logWarn(...args) { - if (!this.debug) return; - console.warn.apply(null, args); - } - - public logError(...args) { - if (!this.debug) return; - console.error.apply(null, args); - } - - /** - * Initialize MiOS (boot) - * @param callback A function that call when initialized - */ - public async init(callback) { - // ユーザーをフェッチしてコールバックする - const fetchme = (token, cb) => { - let me = null; - - // Return when not signed in - if (token == null) { - return done(); - } - - // Fetch user - fetch(`${_API_URL_}/i`, { - method: 'POST', - body: JSON.stringify({ - i: token - }) - }) - // When success - .then(res => { - // When failed to authenticate user - if (res.status !== 200) { - return signout(); - } - - // Parse response - res.json().then(i => { - me = i; - me.token = token; - done(); - }); - }) - // When failure - .catch(() => { - // Render the error screen - document.body.innerHTML = '<mk-error />'; - riot.mount('*'); - - Progress.done(); - }); - - function done() { - if (cb) cb(me); - } - }; - - // フェッチが完了したとき - const 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)); - }); - } - - this.i = me; - - // Init home stream manager - this.stream = this.isSignedin - ? new HomeStreamManager(this.i) - : null; - - // Finish init - callback(); - - //#region Post - - // Init service worker - if (this.shouldRegisterSw) this.registerSw(); - - //#endregion - }; - - // 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'); - cachedMe.trigger('refreshed'); - }); - } else { - // Get token from cookie - const i = (document.cookie.match(/i=(!\w+)/) || [null, null])[1]; - - fetchme(i, fetched); - } - } - - /** - * Register service worker - */ - private registerSw() { - // Check whether service worker and push manager supported - const isSwSupported = - ('serviceWorker' in navigator) && ('PushManager' in window); - - // Reject when browser not service worker supported - if (!isSwSupported) return; - - // Reject when not signed in to Misskey - if (!this.isSignedin) return; - - // When service worker activated - navigator.serviceWorker.ready.then(registration => { - this.log('[sw] ready: ', registration); - - this.swRegistration = registration; - - // Options of pushManager.subscribe - // SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters - const opts = { - // A boolean indicating that the returned push subscription - // will only be used for messages whose effect is made visible to the user. - userVisibleOnly: true, - - // A public key your push server will use to send - // messages to client apps via a push server. - applicationServerKey: urlBase64ToUint8Array(_SW_PUBLICKEY_) - }; - - // Subscribe push notification - this.swRegistration.pushManager.subscribe(opts).then(subscription => { - this.log('[sw] Subscribe OK:', subscription); - - function encode(buffer: ArrayBuffer) { - return btoa(String.fromCharCode.apply(null, new Uint8Array(buffer))); - } - - // Register - this.api('sw/register', { - endpoint: subscription.endpoint, - auth: encode(subscription.getKey('auth')), - publickey: encode(subscription.getKey('p256dh')) - }); - }) - // When subscribe failed - .catch(async (err: Error) => { - this.logError('[sw] Subscribe Error:', err); - - // 通知が許可されていなかったとき - if (err.name == 'NotAllowedError') { - this.logError('[sw] Subscribe failed due to notification not allowed'); - return; - } - - // 違うapplicationServerKey (または gcm_sender_id)のサブスクリプションが - // 既に存在していることが原因でエラーになった可能性があるので、 - // そのサブスクリプションを解除しておく - const subscription = await this.swRegistration.pushManager.getSubscription(); - if (subscription) subscription.unsubscribe(); - }); - }); - - // The path of service worker script - const sw = `/sw.${_VERSION_}.${_LANG_}.js`; - - // Register service worker - navigator.serviceWorker.register(sw).then(registration => { - // 登録成功 - this.logInfo('[sw] Registration successful with scope: ', registration.scope); - }).catch(err => { - // 登録失敗 :( - this.logError('[sw] Registration failed: ', err); - }); - } - - /** - * Misskey APIにリクエストします - * @param endpoint エンドポイント名 - * @param data パラメータ - */ - public api(endpoint: string, data?: { [x: string]: any }) { - return api(this.i, endpoint, data); - } - - /** - * Misskeyのメタ情報を取得します - * @param force キャッシュを無視するか否か - */ - public getMeta(force = false) { - return new Promise<{ [x: string]: any }>(async (res, rej) => { - if (this.isMetaFetching) { - this.once('_meta_fetched_', () => { - res(this.meta.data); - }); - return; - } - - const expire = 1000 * 60; // 1min - - // forceが有効, meta情報を保持していない or 期限切れ - if (force || this.meta == null || Date.now() - this.meta.chachedAt.getTime() > expire) { - this.isMetaFetching = true; - const meta = await this.api('meta'); - this.meta = { - data: meta, - chachedAt: new Date() - }; - this.isMetaFetching = false; - this.emit('_meta_fetched_'); - res(meta); - } else { - res(this.meta.data); - } - }); - } -} - -/** - * Convert the URL safe base64 string to a Uint8Array - * @param base64String base64 string - */ -function urlBase64ToUint8Array(base64String: string): Uint8Array { - const padding = '='.repeat((4 - base64String.length % 4) % 4); - const base64 = (base64String + padding) - .replace(/\-/g, '+') - .replace(/_/g, '/'); - - const rawData = window.atob(base64); - const outputArray = new Uint8Array(rawData.length); - - for (let i = 0; i < rawData.length; ++i) { - outputArray[i] = rawData.charCodeAt(i); - } - return outputArray; -} diff --git a/src/web/app/common/mixins.ts b/src/web/app/common/mixins.ts deleted file mode 100644 index e9c3625937..0000000000 --- a/src/web/app/common/mixins.ts +++ /dev/null @@ -1,40 +0,0 @@ -import * as riot from 'riot'; - -import MiOS from './mios'; -import ServerStreamManager from './scripts/streaming/server-stream-manager'; -import RequestsStreamManager from './scripts/streaming/requests-stream-manager'; -import MessagingIndexStreamManager from './scripts/streaming/messaging-index-stream-manager'; -import DriveStreamManager from './scripts/streaming/drive-stream-manager'; - -export default (mios: MiOS) => { - (riot as any).mixin('os', { - mios: mios - }); - - (riot as any).mixin('i', { - init: function() { - this.I = mios.i; - this.SIGNIN = mios.isSignedin; - - if (this.SIGNIN) { - this.on('mount', () => { - mios.i.on('updated', this.update); - }); - this.on('unmount', () => { - mios.i.off('updated', this.update); - }); - } - }, - me: mios.i - }); - - (riot as any).mixin('api', { - api: mios.api - }); - - (riot as any).mixin('stream', { stream: mios.stream }); - (riot as any).mixin('drive-stream', { driveStream: new DriveStreamManager(mios.i) }); - (riot as any).mixin('server-stream', { serverStream: new ServerStreamManager() }); - (riot as any).mixin('requests-stream', { requestsStream: new RequestsStreamManager() }); - (riot as any).mixin('messaging-index-stream', { messagingIndexStream: new MessagingIndexStreamManager(mios.i) }); -}; diff --git a/src/web/app/common/scripts/api.ts b/src/web/app/common/scripts/api.ts deleted file mode 100644 index 2008e6f5ac..0000000000 --- a/src/web/app/common/scripts/api.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * API Request - */ - -declare const _API_URL_: string; - -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<any>} Response - */ -export default (i, endpoint, data = {}): Promise<{ [x: string]: any }> => { - 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 : `${_API_URL_}/${endpoint}`, { - method: 'POST', - body: JSON.stringify(data), - credentials: endpoint === 'signin' ? 'include' : 'omit', - cache: 'no-cache' - }).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.ts b/src/web/app/common/scripts/bytes-to-size.ts deleted file mode 100644 index 1d2b1e7ce3..0000000000 --- a/src/web/app/common/scripts/bytes-to-size.ts +++ /dev/null @@ -1,6 +0,0 @@ -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/check-for-update.ts b/src/web/app/common/scripts/check-for-update.ts deleted file mode 100644 index 0b58c0a674..0000000000 --- a/src/web/app/common/scripts/check-for-update.ts +++ /dev/null @@ -1,24 +0,0 @@ -import MiOS from '../mios'; - -declare const _VERSION_: string; - -export default async function(mios: MiOS) { - const meta = await mios.getMeta(); - - if (meta.version != _VERSION_) { - localStorage.setItem('should-refresh', 'true'); - - // Clear cache (serive worker) - try { - navigator.serviceWorker.controller.postMessage('clear'); - - navigator.serviceWorker.getRegistrations().then(registrations => { - registrations.forEach(registration => registration.unregister()); - }); - } catch (e) { - console.error(e); - } - - alert('%i18n:common.update-available%'.replace('{newer}', meta.version).replace('{current}', _VERSION_)); - } -} diff --git a/src/web/app/common/scripts/compose-notification.ts b/src/web/app/common/scripts/compose-notification.ts deleted file mode 100644 index d0e0c2098d..0000000000 --- a/src/web/app/common/scripts/compose-notification.ts +++ /dev/null @@ -1,60 +0,0 @@ -import getPostSummary from '../../../../common/get-post-summary'; -import getReactionEmoji from '../../../../common/get-reaction-emoji'; - -type Notification = { - title: string; - body: string; - icon: string; - onclick?: any; -}; - -// TODO: i18n - -export default function(type, data): Notification { - switch (type) { - case 'drive_file_created': - return { - title: 'ファイルがアップロードされました', - body: data.name, - icon: data.url + '?thumbnail&size=64' - }; - - case 'mention': - return { - title: `${data.user.name}さんから:`, - body: getPostSummary(data), - icon: data.user.avatar_url + '?thumbnail&size=64' - }; - - case 'reply': - return { - title: `${data.user.name}さんから返信:`, - body: getPostSummary(data), - icon: data.user.avatar_url + '?thumbnail&size=64' - }; - - case 'quote': - return { - title: `${data.user.name}さんが引用:`, - body: getPostSummary(data), - icon: data.user.avatar_url + '?thumbnail&size=64' - }; - - case 'reaction': - return { - title: `${data.user.name}: ${getReactionEmoji(data.reaction)}:`, - body: getPostSummary(data.post), - icon: data.user.avatar_url + '?thumbnail&size=64' - }; - - case 'unread_messaging_message': - return { - title: `${data.user.name}さんからメッセージ:`, - body: data.text, // TODO: getMessagingMessageSummary(data), - icon: data.user.avatar_url + '?thumbnail&size=64' - }; - - default: - return null; - } -} diff --git a/src/web/app/common/scripts/contains.ts b/src/web/app/common/scripts/contains.ts deleted file mode 100644 index a5071b3f25..0000000000 --- a/src/web/app/common/scripts/contains.ts +++ /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/copy-to-clipboard.ts b/src/web/app/common/scripts/copy-to-clipboard.ts deleted file mode 100644 index 3d2741f8d7..0000000000 --- a/src/web/app/common/scripts/copy-to-clipboard.ts +++ /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/date-stringify.ts b/src/web/app/common/scripts/date-stringify.ts deleted file mode 100644 index e51de8833d..0000000000 --- a/src/web/app/common/scripts/date-stringify.ts +++ /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/gcd.ts b/src/web/app/common/scripts/gcd.ts deleted file mode 100644 index 9a19f9da66..0000000000 --- a/src/web/app/common/scripts/gcd.ts +++ /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/get-kao.ts b/src/web/app/common/scripts/get-kao.ts deleted file mode 100644 index 2168c5be88..0000000000 --- a/src/web/app/common/scripts/get-kao.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default () => [ - '(=^・・^=)', - 'v(‘ω’)v', - '🐡( \'-\' 🐡 )フグパンチ!!!!' -][Math.floor(Math.random() * 3)]; diff --git a/src/web/app/common/scripts/get-median.ts b/src/web/app/common/scripts/get-median.ts deleted file mode 100644 index 91a415d5b2..0000000000 --- a/src/web/app/common/scripts/get-median.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * 中央値を求めます - * @param samples サンプル - */ -export default function(samples) { - if (!samples.length) return 0; - const numbers = samples.slice(0).sort((a, b) => a - b); - const middle = Math.floor(numbers.length / 2); - const isEven = numbers.length % 2 === 0; - return isEven ? (numbers[middle] + numbers[middle - 1]) / 2 : numbers[middle]; -} diff --git a/src/web/app/common/scripts/is-promise.ts b/src/web/app/common/scripts/is-promise.ts deleted file mode 100644 index 3b4cd70b49..0000000000 --- a/src/web/app/common/scripts/is-promise.ts +++ /dev/null @@ -1 +0,0 @@ -export default x => typeof x.then == 'function'; diff --git a/src/web/app/common/scripts/loading.ts b/src/web/app/common/scripts/loading.ts deleted file mode 100644 index c48e626648..0000000000 --- a/src/web/app/common/scripts/loading.ts +++ /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/signout.ts b/src/web/app/common/scripts/signout.ts deleted file mode 100644 index 2923196549..0000000000 --- a/src/web/app/common/scripts/signout.ts +++ /dev/null @@ -1,7 +0,0 @@ -declare const _HOST_: string; - -export default () => { - localStorage.removeItem('me'); - document.cookie = `i=; domain=.${_HOST_}; expires=Thu, 01 Jan 1970 00:00:01 GMT;`; - location.href = '/'; -}; diff --git a/src/web/app/common/scripts/streaming/channel-stream.ts b/src/web/app/common/scripts/streaming/channel-stream.ts deleted file mode 100644 index 434b108b9e..0000000000 --- a/src/web/app/common/scripts/streaming/channel-stream.ts +++ /dev/null @@ -1,12 +0,0 @@ -import Stream from './stream'; - -/** - * Channel stream connection - */ -export default class Connection extends Stream { - constructor(channelId) { - super('channel', { - channel: channelId - }); - } -} diff --git a/src/web/app/common/scripts/streaming/drive-stream-manager.ts b/src/web/app/common/scripts/streaming/drive-stream-manager.ts deleted file mode 100644 index 8acdd7cbba..0000000000 --- a/src/web/app/common/scripts/streaming/drive-stream-manager.ts +++ /dev/null @@ -1,20 +0,0 @@ -import StreamManager from './stream-manager'; -import Connection from './drive-stream'; - -export default class DriveStreamManager extends StreamManager<Connection> { - private me; - - constructor(me) { - super(); - - this.me = me; - } - - public getConnection() { - if (this.connection == null) { - this.connection = new Connection(this.me); - } - - return this.connection; - } -} diff --git a/src/web/app/common/scripts/streaming/drive-stream.ts b/src/web/app/common/scripts/streaming/drive-stream.ts deleted file mode 100644 index 0da3f12554..0000000000 --- a/src/web/app/common/scripts/streaming/drive-stream.ts +++ /dev/null @@ -1,12 +0,0 @@ -import Stream from './stream'; - -/** - * Drive stream connection - */ -export default class Connection extends Stream { - constructor(me) { - super('drive', { - i: me.token - }); - } -} diff --git a/src/web/app/common/scripts/streaming/home-stream-manager.ts b/src/web/app/common/scripts/streaming/home-stream-manager.ts deleted file mode 100644 index ad1dc870eb..0000000000 --- a/src/web/app/common/scripts/streaming/home-stream-manager.ts +++ /dev/null @@ -1,20 +0,0 @@ -import StreamManager from './stream-manager'; -import Connection from './home-stream'; - -export default class HomeStreamManager extends StreamManager<Connection> { - private me; - - constructor(me) { - super(); - - this.me = me; - } - - public getConnection() { - if (this.connection == null) { - this.connection = new Connection(this.me); - } - - return this.connection; - } -} diff --git a/src/web/app/common/scripts/streaming/home-stream.ts b/src/web/app/common/scripts/streaming/home-stream.ts deleted file mode 100644 index 11ad754ef0..0000000000 --- a/src/web/app/common/scripts/streaming/home-stream.ts +++ /dev/null @@ -1,28 +0,0 @@ -import Stream from './stream'; -import signout from '../signout'; - -/** - * Home stream connection - */ -export default class Connection extends Stream { - constructor(me) { - super('', { - i: me.token - }); - - // 最終利用日時を更新するため定期的にaliveメッセージを送信 - setInterval(() => { - this.send({ type: 'alive' }); - }, 1000 * 60); - - // 自分の情報が更新されたとき - this.on('i_updated', me.update); - - // トークンが再生成されたとき - // このままではAPIが利用できないので強制的にサインアウトさせる - this.on('my_token_regenerated', () => { - alert('%i18n:common.my-token-regenerated%'); - signout(); - }); - } -} diff --git a/src/web/app/common/scripts/streaming/messaging-index-stream-manager.ts b/src/web/app/common/scripts/streaming/messaging-index-stream-manager.ts deleted file mode 100644 index 0f08b01481..0000000000 --- a/src/web/app/common/scripts/streaming/messaging-index-stream-manager.ts +++ /dev/null @@ -1,20 +0,0 @@ -import StreamManager from './stream-manager'; -import Connection from './messaging-index-stream'; - -export default class MessagingIndexStreamManager extends StreamManager<Connection> { - private me; - - constructor(me) { - super(); - - this.me = me; - } - - public getConnection() { - if (this.connection == null) { - this.connection = new Connection(this.me); - } - - return this.connection; - } -} diff --git a/src/web/app/common/scripts/streaming/messaging-index-stream.ts b/src/web/app/common/scripts/streaming/messaging-index-stream.ts deleted file mode 100644 index 8015c840b4..0000000000 --- a/src/web/app/common/scripts/streaming/messaging-index-stream.ts +++ /dev/null @@ -1,12 +0,0 @@ -import Stream from './stream'; - -/** - * Messaging index stream connection - */ -export default class Connection extends Stream { - constructor(me) { - super('messaging-index', { - i: me.token - }); - } -} diff --git a/src/web/app/common/scripts/streaming/messaging-stream.ts b/src/web/app/common/scripts/streaming/messaging-stream.ts deleted file mode 100644 index 68dfc5ec09..0000000000 --- a/src/web/app/common/scripts/streaming/messaging-stream.ts +++ /dev/null @@ -1,19 +0,0 @@ -import Stream from './stream'; - -/** - * Messaging stream connection - */ -export default class Connection extends Stream { - constructor(me, otherparty) { - super('messaging', { - i: me.token, - otherparty - }); - - (this as any).on('_connected_', () => { - this.send({ - i: me.token - }); - }); - } -} diff --git a/src/web/app/common/scripts/streaming/requests-stream-manager.ts b/src/web/app/common/scripts/streaming/requests-stream-manager.ts deleted file mode 100644 index 44db913e78..0000000000 --- a/src/web/app/common/scripts/streaming/requests-stream-manager.ts +++ /dev/null @@ -1,12 +0,0 @@ -import StreamManager from './stream-manager'; -import Connection from './requests-stream'; - -export default class RequestsStreamManager extends StreamManager<Connection> { - public getConnection() { - if (this.connection == null) { - this.connection = new Connection(); - } - - return this.connection; - } -} diff --git a/src/web/app/common/scripts/streaming/requests-stream.ts b/src/web/app/common/scripts/streaming/requests-stream.ts deleted file mode 100644 index 22ecea6c07..0000000000 --- a/src/web/app/common/scripts/streaming/requests-stream.ts +++ /dev/null @@ -1,10 +0,0 @@ -import Stream from './stream'; - -/** - * Requests stream connection - */ -export default class Connection extends Stream { - constructor() { - super('requests'); - } -} diff --git a/src/web/app/common/scripts/streaming/server-stream-manager.ts b/src/web/app/common/scripts/streaming/server-stream-manager.ts deleted file mode 100644 index a170daebb9..0000000000 --- a/src/web/app/common/scripts/streaming/server-stream-manager.ts +++ /dev/null @@ -1,12 +0,0 @@ -import StreamManager from './stream-manager'; -import Connection from './server-stream'; - -export default class ServerStreamManager extends StreamManager<Connection> { - public getConnection() { - if (this.connection == null) { - this.connection = new Connection(); - } - - return this.connection; - } -} diff --git a/src/web/app/common/scripts/streaming/server-stream.ts b/src/web/app/common/scripts/streaming/server-stream.ts deleted file mode 100644 index b9e0684465..0000000000 --- a/src/web/app/common/scripts/streaming/server-stream.ts +++ /dev/null @@ -1,10 +0,0 @@ -import Stream from './stream'; - -/** - * Server stream connection - */ -export default class Connection extends Stream { - constructor() { - super('server'); - } -} diff --git a/src/web/app/common/scripts/streaming/stream-manager.ts b/src/web/app/common/scripts/streaming/stream-manager.ts deleted file mode 100644 index 5bb0dc701c..0000000000 --- a/src/web/app/common/scripts/streaming/stream-manager.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { EventEmitter } from 'eventemitter3'; -import * as uuid from 'uuid'; -import Connection from './stream'; - -/** - * ストリーム接続を管理するクラス - * 複数の場所から同じストリームを利用する際、接続をまとめたりする - */ -export default abstract class StreamManager<T extends Connection> extends EventEmitter { - private _connection: T = null; - - private disposeTimerId: any; - - /** - * コネクションを必要としているユーザー - */ - private users = []; - - protected set connection(connection: T) { - this._connection = connection; - - if (this._connection == null) { - this.emit('disconnected'); - } else { - this.emit('connected', this._connection); - } - } - - protected get connection() { - return this._connection; - } - - /** - * コネクションを持っているか否か - */ - public get hasConnection() { - return this._connection != null; - } - - /** - * コネクションを要求します - */ - public abstract getConnection(): T; - - /** - * 現在接続しているコネクションを取得します - */ - public borrow() { - return this._connection; - } - - /** - * コネクションを要求するためのユーザーIDを発行します - */ - public use() { - // タイマー解除 - if (this.disposeTimerId) { - clearTimeout(this.disposeTimerId); - this.disposeTimerId = null; - } - - // ユーザーID生成 - const userId = uuid(); - - this.users.push(userId); - - return userId; - } - - /** - * コネクションを利用し終わってもう必要ないことを通知します - * @param userId use で発行したユーザーID - */ - public dispose(userId) { - this.users = this.users.filter(id => id != userId); - - // 誰もコネクションの利用者がいなくなったら - if (this.users.length == 0) { - // また直ぐに再利用される可能性があるので、一定時間待ち、 - // 新たな利用者が現れなければコネクションを切断する - this.disposeTimerId = setTimeout(() => { - this.disposeTimerId = null; - - this.connection.close(); - this.connection = null; - }, 3000); - } - } -} diff --git a/src/web/app/common/scripts/streaming/stream.ts b/src/web/app/common/scripts/streaming/stream.ts deleted file mode 100644 index 770d77510f..0000000000 --- a/src/web/app/common/scripts/streaming/stream.ts +++ /dev/null @@ -1,96 +0,0 @@ -declare const _API_URL_: string; - -import { EventEmitter } from 'eventemitter3'; -import * as ReconnectingWebsocket from 'reconnecting-websocket'; - -/** - * Misskey stream connection - */ -export default class Connection extends EventEmitter { - private state: string; - private buffer: any[]; - private socket: ReconnectingWebsocket; - - constructor(endpoint, params?) { - super(); - - //#region 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); - //#endregion - - this.state = 'initializing'; - this.buffer = []; - - const host = _API_URL_.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.emit('_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.emit('_closed_'); - } - - /** - * Callback of when received a message from connection - */ - private onMessage(message) { - try { - const msg = JSON.parse(message.data); - if (msg.type) this.emit(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); - } -} diff --git a/src/web/app/common/scripts/text-compiler.ts b/src/web/app/common/scripts/text-compiler.ts deleted file mode 100644 index e0ea47df26..0000000000 --- a/src/web/app/common/scripts/text-compiler.ts +++ /dev/null @@ -1,48 +0,0 @@ -declare const _URL_: string; - -import * as riot from 'riot'; -import * as pictograph from 'pictograph'; - -const escape = text => - text - .replace(/>/g, '>') - .replace(/</g, '<'); - -export default (tokens, shouldBreak) => { - 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 ? '<br>' : ' '); - case 'bold': - return `<strong>${escape(token.bold)}</strong>`; - case 'url': - return `<mk-url href="${escape(token.content)}" target="_blank"></mk-url>`; - case 'link': - return `<a class="link" href="${escape(token.url)}" target="_blank" title="${escape(token.url)}">${escape(token.title)}</a>`; - case 'mention': - return `<a href="${_URL_ + '/' + escape(token.username)}" target="_blank" data-user-preview="${token.content}" ${me && me.username == token.username ? 'data-is-me' : ''}>${token.content}</a>`; - case 'hashtag': // TODO - return `<a>${escape(token.content)}</a>`; - case 'code': - return `<pre><code>${token.html}</code></pre>`; - case 'inline-code': - return `<code>${token.html}</code>`; - case 'emoji': - return pictograph.dic[token.emoji] || token.content; - } - }).join(''); - - // Remove needless whitespaces - text = text - .replace(/ <code>/g, '<code>').replace(/<\/code> /g, '</code>') - .replace(/<br><code><pre>/g, '<code><pre>').replace(/<\/code><\/pre><br>/g, '</code></pre>'); - - return text; -}; diff --git a/src/web/app/common/tags/activity-table.tag b/src/web/app/common/tags/activity-table.tag deleted file mode 100644 index 1d26d1788a..0000000000 --- a/src/web/app/common/tags/activity-table.tag +++ /dev/null @@ -1,57 +0,0 @@ -<mk-activity-table> - <svg if={ data } ref="canvas" viewBox="0 0 53 7" preserveAspectRatio="none"> - <rect each={ data } width="1" height="1" - riot-x={ x } riot-y={ date.weekday } - rx="1" ry="1" - fill={ color } - style="transform: scale({ v });"/> - <rect class="today" width="1" height="1" - riot-x={ data[data.length - 1].x } riot-y={ data[data.length - 1].date.weekday } - rx="1" ry="1" - fill="none" - stroke-width="0.1" - stroke="#f73520"/> - </svg> - <style> - :scope - display block - max-width 600px - margin 0 auto - - > svg - display block - - > rect - transform-origin center - - </style> - <script> - this.mixin('api'); - - this.user = this.opts.user; - - this.on('mount', () => { - this.api('aggregation/users/activity', { - user_id: this.user.id - }).then(data => { - data.forEach(d => d.total = d.posts + d.replies + d.reposts); - this.peak = Math.max.apply(null, data.map(d => d.total)) / 2; - let x = 0; - data.reverse().forEach(d => { - d.x = x; - d.date.weekday = (new Date(d.date.year, d.date.month - 1, d.date.day)).getDay(); - - d.v = d.total / this.peak; - if (d.v > 1) d.v = 1; - const ch = d.date.weekday == 0 || d.date.weekday == 6 ? 275 : 170; - const cs = d.v * 100; - const cl = 15 + ((1 - d.v) * 80); - d.color = `hsl(${ch}, ${cs}%, ${cl}%)`; - - if (d.date.weekday == 6) x++; - }); - this.update({ data }); - }); - }); - </script> -</mk-activity-table> diff --git a/src/web/app/common/tags/authorized-apps.tag b/src/web/app/common/tags/authorized-apps.tag deleted file mode 100644 index 0594032de6..0000000000 --- a/src/web/app/common/tags/authorized-apps.tag +++ /dev/null @@ -1,35 +0,0 @@ -<mk-authorized-apps> - <div class="none ui info" if={ !fetching && apps.length == 0 }> - <p>%fa:info-circle%%i18n:common.tags.mk-authorized-apps.no-apps%</p> - </div> - <div class="apps" if={ apps.length != 0 }> - <div each={ app in apps }> - <p><b>{ app.name }</b></p> - <p>{ app.description }</p> - </div> - </div> - <style> - :scope - display block - - > .apps - > div - padding 16px 0 0 0 - border-bottom solid 1px #eee - - </style> - <script> - this.mixin('api'); - - this.apps = []; - this.fetching = true; - - this.on('mount', () => { - this.api('i/authorized_apps').then(apps => { - this.apps = apps; - this.fetching = false; - this.update(); - }); - }); - </script> -</mk-authorized-apps> diff --git a/src/web/app/common/tags/copyright.tag b/src/web/app/common/tags/copyright.tag deleted file mode 100644 index 9c3f1f648b..0000000000 --- a/src/web/app/common/tags/copyright.tag +++ /dev/null @@ -1,7 +0,0 @@ -<mk-copyright> - <span>(c) syuilo 2014-2017</span> - <style> - :scope - display block - </style> -</mk-copyright> diff --git a/src/web/app/common/tags/ellipsis.tag b/src/web/app/common/tags/ellipsis.tag deleted file mode 100644 index 97ef745d02..0000000000 --- a/src/web/app/common/tags/ellipsis.tag +++ /dev/null @@ -1,24 +0,0 @@ -<mk-ellipsis><span>.</span><span>.</span><span>.</span> - <style> - :scope - display inline - - > span - animation ellipsis 1.4s infinite ease-in-out both - - &:nth-child(1) - animation-delay 0s - - &:nth-child(2) - animation-delay 0.16s - - &:nth-child(3) - animation-delay 0.32s - - @keyframes ellipsis - 0%, 80%, 100% - opacity 1 - 40% - opacity 0 - </style> -</mk-ellipsis> diff --git a/src/web/app/common/tags/error.tag b/src/web/app/common/tags/error.tag deleted file mode 100644 index a5b8d14898..0000000000 --- a/src/web/app/common/tags/error.tag +++ /dev/null @@ -1,215 +0,0 @@ -<mk-error> - <img src="data:image/jpeg;base64,%base64:/assets/error.jpg%" alt=""/> - <h1>%i18n:common.tags.mk-error.title%</h1> - <p class="text">{ - '%i18n:common.tags.mk-error.description%'.substr(0, '%i18n:common.tags.mk-error.description%'.indexOf('{')) - }<a onclick={ reload }>{ - '%i18n:common.tags.mk-error.description%'.match(/\{(.+?)\}/)[1] - }</a>{ - '%i18n:common.tags.mk-error.description%'.substr('%i18n:common.tags.mk-error.description%'.indexOf('}') + 1) - }</p> - <button if={ !troubleshooting } onclick={ troubleshoot }>%i18n:common.tags.mk-error.troubleshoot%</button> - <mk-troubleshooter if={ troubleshooting }/> - <p class="thanks">%i18n:common.tags.mk-error.thanks%</p> - <style> - :scope - display block - width 100% - padding 32px 18px - text-align center - - > img - display block - height 200px - margin 0 auto - pointer-events none - user-select none - - > h1 - display block - margin 1.25em auto 0.65em auto - font-size 1.5em - color #555 - - > .text - display block - margin 0 auto - max-width 600px - font-size 1em - color #666 - - > button - display block - margin 1em auto 0 auto - padding 8px 10px - color $theme-color-foreground - background $theme-color - - &:focus - outline solid 3px rgba($theme-color, 0.3) - - &:hover - background lighten($theme-color, 10%) - - &:active - background darken($theme-color, 10%) - - > mk-troubleshooter - margin 1em auto 0 auto - - > .thanks - display block - margin 2em auto 0 auto - padding 2em 0 0 0 - max-width 600px - font-size 0.9em - font-style oblique - color #aaa - border-top solid 1px #eee - - @media (max-width 500px) - padding 24px 18px - font-size 80% - - > img - height 150px - - </style> - <script> - this.troubleshooting = false; - - this.on('mount', () => { - document.title = 'Oops!'; - document.documentElement.style.background = '#f8f8f8'; - }); - - this.reload = () => { - location.reload(); - }; - - this.troubleshoot = () => { - this.update({ - troubleshooting: true - }); - }; - </script> -</mk-error> - -<mk-troubleshooter> - <h1>%fa:wrench%%i18n:common.tags.mk-error.troubleshooter.title%</h1> - <div> - <p data-wip={ network == null }><virtual if={ network != null }><virtual if={ network }>%fa:check%</virtual><virtual if={ !network }>%fa:times%</virtual></virtual>{ network == null ? '%i18n:common.tags.mk-error.troubleshooter.checking-network%' : '%i18n:common.tags.mk-error.troubleshooter.network%' }<mk-ellipsis if={ network == null }/></p> - <p if={ network == true } data-wip={ internet == null }><virtual if={ internet != null }><virtual if={ internet }>%fa:check%</virtual><virtual if={ !internet }>%fa:times%</virtual></virtual>{ internet == null ? '%i18n:common.tags.mk-error.troubleshooter.checking-internet%' : '%i18n:common.tags.mk-error.troubleshooter.internet%' }<mk-ellipsis if={ internet == null }/></p> - <p if={ internet == true } data-wip={ server == null }><virtual if={ server != null }><virtual if={ server }>%fa:check%</virtual><virtual if={ !server }>%fa:times%</virtual></virtual>{ server == null ? '%i18n:common.tags.mk-error.troubleshooter.checking-server%' : '%i18n:common.tags.mk-error.troubleshooter.server%' }<mk-ellipsis if={ server == null }/></p> - </div> - <p if={ !end }>%i18n:common.tags.mk-error.troubleshooter.finding%<mk-ellipsis/></p> - <p if={ network === false }><b>%fa:exclamation-triangle%%i18n:common.tags.mk-error.troubleshooter.no-network%</b><br>%i18n:common.tags.mk-error.troubleshooter.no-network-desc%</p> - <p if={ internet === false }><b>%fa:exclamation-triangle%%i18n:common.tags.mk-error.troubleshooter.no-internet%</b><br>%i18n:common.tags.mk-error.troubleshooter.no-internet-desc%</p> - <p if={ server === false }><b>%fa:exclamation-triangle%%i18n:common.tags.mk-error.troubleshooter.no-server%</b><br>%i18n:common.tags.mk-error.troubleshooter.no-server-desc%</p> - <p if={ server === true } class="success"><b>%fa:info-circle%%i18n:common.tags.mk-error.troubleshooter.success%</b><br>%i18n:common.tags.mk-error.troubleshooter.success-desc%</p> - - <style> - :scope - display block - width 100% - max-width 500px - text-align left - background #fff - border-radius 8px - border solid 1px #ddd - - > h1 - margin 0 - padding 0.6em 1.2em - font-size 1em - color #444 - border-bottom solid 1px #eee - - > [data-fa] - margin-right 0.25em - - > div - overflow hidden - padding 0.6em 1.2em - - > p - margin 0.5em 0 - font-size 0.9em - color #444 - - &[data-wip] - color #888 - - > [data-fa] - margin-right 0.25em - - &.times - color #e03524 - - &.check - color #84c32f - - > p - margin 0 - padding 0.6em 1.2em - font-size 1em - color #444 - border-top solid 1px #eee - - > b - > [data-fa] - margin-right 0.25em - - &.success - > b - color #39adad - - &:not(.success) - > b - color #ad4339 - - </style> - <script> - this.on('mount', () => { - this.update({ - network: navigator.onLine - }); - - if (!this.network) { - this.update({ - end: true - }); - return; - } - - // Check internet connection - fetch('https://google.com?rand=' + Math.random(), { - mode: 'no-cors' - }).then(() => { - this.update({ - internet: true - }); - - // Check misskey server is available - fetch(`${_API_URL_}/meta`).then(() => { - this.update({ - end: true, - server: true - }); - }) - .catch(() => { - this.update({ - end: true, - server: false - }); - }); - }) - .catch(() => { - this.update({ - end: true, - internet: false - }); - }); - }); - </script> -</mk-troubleshooter> diff --git a/src/web/app/common/tags/file-type-icon.tag b/src/web/app/common/tags/file-type-icon.tag deleted file mode 100644 index dba2ae44d6..0000000000 --- a/src/web/app/common/tags/file-type-icon.tag +++ /dev/null @@ -1,10 +0,0 @@ -<mk-file-type-icon> - <virtual if={ kind == 'image' }>%fa:file-image%</virtual> - <style> - :scope - display inline - </style> - <script> - this.kind = this.opts.type.split('/')[0]; - </script> -</mk-file-type-icon> diff --git a/src/web/app/common/tags/forkit.tag b/src/web/app/common/tags/forkit.tag deleted file mode 100644 index 55d5731081..0000000000 --- a/src/web/app/common/tags/forkit.tag +++ /dev/null @@ -1,40 +0,0 @@ -<mk-forkit><a href="https://github.com/syuilo/misskey" target="_blank" title="%i18n:common.tags.mk-forkit.open-github-link%" aria-label="%i18n:common.tags.mk-forkit.open-github-link%"> - <svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="aria-hidden"> - <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path> - <path class="octo-arm" d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor"></path> - <path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor"></path> - </svg></a> - <style> - :scope - display block - position absolute - top 0 - right 0 - - > a - display block - - > svg - display block - //fill #151513 - //color #fff - fill $theme-color - color $theme-color-foreground - - .octo-arm - transform-origin 130px 106px - - &:hover - .octo-arm - animation octocat-wave 560ms ease-in-out - - @keyframes octocat-wave - 0%, 100% - transform rotate(0) - 20%, 60% - transform rotate(-25deg) - 40%, 80% - transform rotate(10deg) - - </style> -</mk-forkit> diff --git a/src/web/app/common/tags/index.ts b/src/web/app/common/tags/index.ts deleted file mode 100644 index 2f4e1181d4..0000000000 --- a/src/web/app/common/tags/index.ts +++ /dev/null @@ -1,31 +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'); -require('./nav-links.tag'); diff --git a/src/web/app/common/tags/introduction.tag b/src/web/app/common/tags/introduction.tag deleted file mode 100644 index 3256688d10..0000000000 --- a/src/web/app/common/tags/introduction.tag +++ /dev/null @@ -1,25 +0,0 @@ -<mk-introduction> - <article> - <h1>Misskeyとは?</h1> - <p><ruby>Misskey<rt>みすきー</rt></ruby>は、<a href="http://syuilo.com" target="_blank">syuilo</a>が2014年くらいから<a href="https://github.com/syuilo/misskey" target="_blank">オープンソースで</a>開発・運営を行っている、ミニブログベースのSNSです。</p> - <p>無料で誰でも利用でき、広告も掲載していません。</p> - <p><a href={ _ABOUT_URL_ } target="_blank">もっと知りたい方はこちら</a></p> - </article> - <style> - :scope - display block - - h1 - margin 0 - text-align center - font-size 1.2em - - p - margin 16px 0 - - &:last-child - margin 0 - text-align center - - </style> -</mk-introduction> diff --git a/src/web/app/common/tags/messaging/form.tag b/src/web/app/common/tags/messaging/form.tag deleted file mode 100644 index 7b133a71c4..0000000000 --- a/src/web/app/common/tags/messaging/form.tag +++ /dev/null @@ -1,175 +0,0 @@ -<mk-messaging-form> - <textarea ref="text" onkeypress={ onkeypress } onpaste={ onpaste } placeholder="%i18n:common.input-message-here%"></textarea> - <div class="files"></div> - <mk-uploader ref="uploader"/> - <button class="send" onclick={ send } disabled={ sending } title="%i18n:common.send%"> - <virtual if={ !sending }>%fa:paper-plane%</virtual><virtual if={ sending }>%fa:spinner .spin%</virtual> - </button> - <button class="attach-from-local" type="button" title="%i18n:common.tags.mk-messaging-form.attach-from-local%"> - %fa:upload% - </button> - <button class="attach-from-drive" type="button" title="%i18n:common.tags.mk-messaging-form.attach-from-drive%"> - %fa:R folder-open% - </button> - <input name="file" type="file" accept="image/*"/> - <style> - :scope - display block - - > textarea - cursor auto - display block - width 100% - min-width 100% - max-width 100% - height 64px - margin 0 - padding 8px - font-size 1em - color #000 - outline none - border none - border-top solid 1px #eee - border-radius 0 - box-shadow none - background transparent - - > .send - position absolute - bottom 0 - right 0 - margin 0 - padding 10px 14px - line-height 1em - font-size 1em - color #aaa - transition color 0.1s ease - - &:hover - color $theme-color - - &:active - color darken($theme-color, 10%) - transition color 0s ease - - .files - display block - margin 0 - padding 0 8px - list-style none - - &:after - content '' - display block - clear both - - > li - display block - float left - margin 4px - padding 0 - width 64px - height 64px - background-color #eee - background-repeat no-repeat - background-position center center - background-size cover - cursor move - - &:hover - > .remove - display block - - > .remove - display none - position absolute - right -6px - top -6px - margin 0 - padding 0 - background transparent - outline none - border none - border-radius 0 - box-shadow none - cursor pointer - - .attach-from-local - .attach-from-drive - margin 0 - padding 10px 14px - line-height 1em - font-size 1em - font-weight normal - text-decoration none - color #aaa - transition color 0.1s ease - - &:hover - color $theme-color - - &:active - color darken($theme-color, 10%) - transition color 0s ease - - input[type=file] - display none - - </style> - <script> - this.mixin('api'); - - this.onpaste = e => { - const data = e.clipboardData; - const items = data.items; - for (const item of items) { - if (item.kind == 'file') { - this.upload(item.getAsFile()); - } - } - }; - - this.onkeypress = e => { - if ((e.which == 10 || e.which == 13) && e.ctrlKey) { - this.send(); - } - }; - - this.selectFile = () => { - this.refs.file.click(); - }; - - this.selectFileFromDrive = () => { - const browser = document.body.appendChild(document.createElement('mk-select-file-from-drive-window')); - const event = riot.observable(); - riot.mount(browser, { - multiple: true, - event: event - }); - event.one('selected', files => { - files.forEach(this.addFile); - }); - }; - - this.send = () => { - this.sending = true; - this.api('messaging/messages/create', { - user_id: this.opts.user.id, - text: this.refs.text.value - }).then(message => { - this.clear(); - }).catch(err => { - console.error(err); - }).then(() => { - this.sending = false; - this.update(); - }); - }; - - this.clear = () => { - this.refs.text.value = ''; - this.files = []; - this.update(); - }; - </script> -</mk-messaging-form> diff --git a/src/web/app/common/tags/messaging/index.tag b/src/web/app/common/tags/messaging/index.tag deleted file mode 100644 index d26cec6cdf..0000000000 --- a/src/web/app/common/tags/messaging/index.tag +++ /dev/null @@ -1,456 +0,0 @@ -<mk-messaging data-compact={ opts.compact }> - <div class="search" if={ !opts.compact }> - <div class="form"> - <label for="search-input">%fa:search%</label> - <input ref="search" type="search" oninput={ search } onkeydown={ onSearchKeydown } placeholder="%i18n:common.tags.mk-messaging.search-user%"/> - </div> - <div class="result"> - <ol class="users" if={ searchResult.length > 0 } ref="searchResult"> - <li each={ user, i in searchResult } onkeydown={ parent.onSearchResultKeydown.bind(null, i) } onclick={ user._click } tabindex="-1"> - <img class="avatar" src={ user.avatar_url + '?thumbnail&size=32' } alt=""/> - <span class="name">{ user.name }</span> - <span class="username">@{ user.username }</span> - </li> - </ol> - </div> - </div> - <div class="history" if={ history.length > 0 }> - <virtual each={ history }> - <a class="user" data-is-me={ is_me } data-is-read={ is_read } onclick={ _click }> - <div> - <img class="avatar" src={ (is_me ? recipient.avatar_url : user.avatar_url) + '?thumbnail&size=64' } alt=""/> - <header> - <span class="name">{ is_me ? recipient.name : user.name }</span> - <span class="username">{ '@' + (is_me ? recipient.username : user.username ) }</span> - <mk-time time={ created_at }/> - </header> - <div class="body"> - <p class="text"><span class="me" if={ is_me }>%i18n:common.tags.mk-messaging.you%:</span>{ text }</p> - </div> - </div> - </a> - </virtual> - </div> - <p class="no-history" if={ !fetching && history.length == 0 }>%i18n:common.tags.mk-messaging.no-history%</p> - <p class="fetching" if={ fetching }>%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p> - <style> - :scope - display block - - &[data-compact] - font-size 0.8em - - > .history - > a - &:last-child - border-bottom none - - &:not([data-is-me]):not([data-is-read]) - > div - background-image none - border-left solid 4px #3aa2dc - - > div - padding 16px - - > header - > mk-time - font-size 1em - - > .avatar - width 42px - height 42px - margin 0 12px 0 0 - - > .search - display block - position -webkit-sticky - position sticky - top 0 - left 0 - z-index 1 - width 100% - background #fff - box-shadow 0 0px 2px rgba(0, 0, 0, 0.2) - - > .form - padding 8px - background #f7f7f7 - - > label - display block - position absolute - top 0 - left 8px - z-index 1 - height 100% - width 38px - pointer-events none - - > [data-fa] - display block - position absolute - top 0 - right 0 - bottom 0 - left 0 - width 1em - height 1em - margin auto - color #555 - - > input - margin 0 - padding 0 0 0 38px - width 100% - font-size 1em - line-height 38px - color #000 - outline none - border solid 1px #eee - border-radius 5px - box-shadow none - transition color 0.5s ease, border 0.5s ease - - &:hover - border solid 1px #ddd - transition border 0.2s ease - - &:focus - color darken($theme-color, 20%) - border solid 1px $theme-color - transition color 0, border 0 - - > .result - display block - top 0 - left 0 - z-index 2 - width 100% - margin 0 - padding 0 - background #fff - - > .users - margin 0 - padding 0 - list-style none - - > li - display inline-block - z-index 1 - width 100% - padding 8px 32px - vertical-align top - white-space nowrap - overflow hidden - color rgba(0, 0, 0, 0.8) - text-decoration none - transition none - cursor pointer - - &:hover - &:focus - color #fff - background $theme-color - - .name - color #fff - - .username - color #fff - - &:active - color #fff - background darken($theme-color, 10%) - - .name - color #fff - - .username - color #fff - - .avatar - vertical-align middle - min-width 32px - min-height 32px - max-width 32px - max-height 32px - margin 0 8px 0 0 - border-radius 6px - - .name - margin 0 8px 0 0 - /*font-weight bold*/ - font-weight normal - color rgba(0, 0, 0, 0.8) - - .username - font-weight normal - color rgba(0, 0, 0, 0.3) - - > .history - - > a - display block - text-decoration none - background #fff - border-bottom solid 1px #eee - - * - pointer-events none - user-select none - - &:hover - background #fafafa - - > .avatar - filter saturate(200%) - - &:active - background #eee - - &[data-is-read] - &[data-is-me] - opacity 0.8 - - &:not([data-is-me]):not([data-is-read]) - > div - background-image url("/assets/unread.svg") - background-repeat no-repeat - background-position 0 center - - &:after - content "" - display block - clear both - - > div - max-width 500px - margin 0 auto - padding 20px 30px - - &:after - content "" - display block - clear both - - > header - margin-bottom 2px - white-space nowrap - overflow hidden - - > .name - text-align left - display inline - margin 0 - padding 0 - font-size 1em - color rgba(0, 0, 0, 0.9) - font-weight bold - transition all 0.1s ease - - > .username - text-align left - margin 0 0 0 8px - color rgba(0, 0, 0, 0.5) - - > mk-time - position absolute - top 0 - right 0 - display inline - color rgba(0, 0, 0, 0.5) - font-size 80% - - > .avatar - float left - width 54px - height 54px - margin 0 16px 0 0 - border-radius 8px - transition all 0.1s ease - - > .body - - > .text - display block - margin 0 0 0 0 - padding 0 - overflow hidden - overflow-wrap break-word - font-size 1.1em - color rgba(0, 0, 0, 0.8) - - .me - color rgba(0, 0, 0, 0.4) - - > .image - display block - max-width 100% - max-height 512px - - > .no-history - margin 0 - padding 2em 1em - text-align center - color #999 - font-weight 500 - - > .fetching - margin 0 - padding 16px - text-align center - color #aaa - - > [data-fa] - margin-right 4px - - // TODO: element base media query - @media (max-width 400px) - > .search - > .result - > .users - > li - padding 8px 16px - - > .history - > a - &:not([data-is-me]):not([data-is-read]) - > div - background-image none - border-left solid 4px #3aa2dc - - > div - padding 16px - font-size 14px - - > .avatar - margin 0 12px 0 0 - - </style> - <script> - this.mixin('i'); - this.mixin('api'); - - this.mixin('messaging-index-stream'); - this.connection = this.messagingIndexStream.getConnection(); - this.connectionId = this.messagingIndexStream.use(); - - this.searchResult = []; - this.history = []; - this.fetching = true; - - this.registerMessage = message => { - message.is_me = message.user_id == this.I.id; - message._click = () => { - this.trigger('navigate-user', message.is_me ? message.recipient : message.user); - }; - }; - - this.on('mount', () => { - this.connection.on('message', this.onMessage); - this.connection.on('read', this.onRead); - - this.api('messaging/history').then(history => { - this.fetching = false; - history.forEach(message => { - this.registerMessage(message); - }); - this.history = history; - this.update(); - }); - }); - - this.on('unmount', () => { - this.connection.off('message', this.onMessage); - this.connection.off('read', this.onRead); - this.messagingIndexStream.dispose(this.connectionId); - }); - - this.onMessage = message => { - this.history = this.history.filter(m => !( - (m.recipient_id == message.recipient_id && m.user_id == message.user_id) || - (m.recipient_id == message.user_id && m.user_id == message.recipient_id))); - - this.registerMessage(message); - - this.history.unshift(message); - this.update(); - }; - - this.onRead = ids => { - ids.forEach(id => { - const found = this.history.find(m => m.id == id); - if (found) found.is_read = true; - }); - - this.update(); - }; - - this.search = () => { - const q = this.refs.search.value; - if (q == '') { - this.searchResult = []; - return; - } - this.api('users/search', { - query: q, - max: 5 - }).then(users => { - users.forEach(user => { - user._click = () => { - this.trigger('navigate-user', user); - this.searchResult = []; - }; - }); - this.update({ - searchResult: users - }); - }); - }; - - this.onSearchKeydown = e => { - switch (e.which) { - case 9: // [TAB] - case 40: // [↓] - e.preventDefault(); - e.stopPropagation(); - this.refs.searchResult.childNodes[0].focus(); - break; - } - }; - - this.onSearchResultKeydown = (i, e) => { - const cancel = () => { - e.preventDefault(); - e.stopPropagation(); - }; - switch (true) { - case e.which == 10: // [ENTER] - case e.which == 13: // [ENTER] - cancel(); - this.searchResult[i]._click(); - break; - - case e.which == 27: // [ESC] - cancel(); - this.refs.search.focus(); - break; - - case e.which == 9 && e.shiftKey: // [TAB] + [Shift] - case e.which == 38: // [↑] - cancel(); - (this.refs.searchResult.childNodes[i].previousElementSibling || this.refs.searchResult.childNodes[this.searchResult.length - 1]).focus(); - break; - - case e.which == 9: // [TAB] - case e.which == 40: // [↓] - cancel(); - (this.refs.searchResult.childNodes[i].nextElementSibling || this.refs.searchResult.childNodes[0]).focus(); - break; - } - }; - - </script> -</mk-messaging> diff --git a/src/web/app/common/tags/messaging/message.tag b/src/web/app/common/tags/messaging/message.tag deleted file mode 100644 index 354022d7df..0000000000 --- a/src/web/app/common/tags/messaging/message.tag +++ /dev/null @@ -1,238 +0,0 @@ -<mk-messaging-message data-is-me={ message.is_me }> - <a class="avatar-anchor" href={ '/' + message.user.username } title={ message.user.username } target="_blank"> - <img class="avatar" src={ message.user.avatar_url + '?thumbnail&size=80' } alt=""/> - </a> - <div class="content-container"> - <div class="balloon"> - <p class="read" if={ message.is_me && message.is_read }>%i18n:common.tags.mk-messaging-message.is-read%</p> - <button class="delete-button" if={ message.is_me } title="%i18n:common.delete%"><img src="/assets/desktop/messaging/delete.png" alt="Delete"/></button> - <div class="content" if={ !message.is_deleted }> - <div ref="text"></div> - <div class="image" if={ message.file }><img src={ message.file.url } alt="image" title={ message.file.name }/></div> - </div> - <div class="content" if={ message.is_deleted }> - <p class="is-deleted">%i18n:common.tags.mk-messaging-message.deleted%</p> - </div> - </div> - <footer> - <mk-time time={ message.created_at }/><virtual if={ message.is_edited }>%fa:pencil-alt%</virtual> - </footer> - </div> - <style> - :scope - $me-balloon-color = #23A7B6 - - display block - padding 10px 12px 10px 12px - background-color transparent - - &:after - content "" - display block - clear both - - > .avatar-anchor - display block - - > .avatar - display block - min-width 54px - min-height 54px - max-width 54px - max-height 54px - margin 0 - border-radius 8px - transition all 0.1s ease - - > .content-container - display block - margin 0 12px - padding 0 - max-width calc(100% - 78px) - - > .balloon - display block - float inherit - margin 0 - padding 0 - max-width 100% - min-height 38px - border-radius 16px - - &:before - content "" - pointer-events none - display block - position absolute - top 12px - - &:hover - > .delete-button - display block - - > .delete-button - display none - position absolute - z-index 1 - top -4px - right -4px - margin 0 - padding 0 - cursor pointer - outline none - border none - border-radius 0 - box-shadow none - background transparent - - > img - vertical-align bottom - width 16px - height 16px - cursor pointer - - > .read - user-select none - display block - position absolute - z-index 1 - bottom -4px - left -12px - margin 0 - color rgba(0, 0, 0, 0.5) - font-size 11px - - > .content - - > .is-deleted - display block - margin 0 - padding 0 - overflow hidden - overflow-wrap break-word - font-size 1em - color rgba(0, 0, 0, 0.5) - - > [ref='text'] - display block - margin 0 - padding 8px 16px - overflow hidden - overflow-wrap break-word - font-size 1em - color rgba(0, 0, 0, 0.8) - - &, * - user-select text - cursor auto - - & + .file - &.image - > img - border-radius 0 0 16px 16px - - > .file - &.image - > img - display block - max-width 100% - max-height 512px - border-radius 16px - - > footer - display block - clear both - margin 0 - padding 2px - font-size 10px - color rgba(0, 0, 0, 0.4) - - > [data-fa] - margin-left 4px - - &:not([data-is-me='true']) - > .avatar-anchor - float left - - > .content-container - float left - - > .balloon - background #eee - - &:before - left -14px - border-top solid 8px transparent - border-right solid 8px #eee - border-bottom solid 8px transparent - border-left solid 8px transparent - - > footer - text-align left - - &[data-is-me='true'] - > .avatar-anchor - float right - - > .content-container - float right - - > .balloon - background $me-balloon-color - - &:before - right -14px - left auto - border-top solid 8px transparent - border-right solid 8px transparent - border-bottom solid 8px transparent - border-left solid 8px $me-balloon-color - - > .content - - > p.is-deleted - color rgba(255, 255, 255, 0.5) - - > [ref='text'] - &, * - color #fff !important - - > footer - text-align right - - &[data-is-deleted='true'] - > .content-container - opacity 0.5 - - </style> - <script> - import compile from '../../../common/scripts/text-compiler'; - - this.mixin('i'); - - this.message = this.opts.message; - this.message.is_me = this.message.user.id == this.I.id; - - this.on('mount', () => { - if (this.message.text) { - const tokens = this.message.ast; - - this.refs.text.innerHTML = compile(tokens); - - Array.from(this.refs.text.children).forEach(e => { - if (e.tagName == 'MK-URL') riot.mount(e); - }); - - // URLをプレビュー - tokens - .filter(t => t.type == 'link') - .map(t => { - const el = this.refs.text.appendChild(document.createElement('mk-url-preview')); - riot.mount(el, { - url: t.content - }); - }); - } - }); - </script> -</mk-messaging-message> diff --git a/src/web/app/common/tags/messaging/room.tag b/src/web/app/common/tags/messaging/room.tag deleted file mode 100644 index a149e1de22..0000000000 --- a/src/web/app/common/tags/messaging/room.tag +++ /dev/null @@ -1,319 +0,0 @@ -<mk-messaging-room> - <div class="stream"> - <p class="init" if={ init }>%fa:spinner .spin%%i18n:common.loading%</p> - <p class="empty" if={ !init && messages.length == 0 }>%fa:info-circle%%i18n:common.tags.mk-messaging-room.empty%</p> - <p class="no-history" if={ !init && messages.length > 0 && !moreMessagesIsInStock }>%fa:flag%%i18n:common.tags.mk-messaging-room.no-history%</p> - <button class="more { fetching: fetchingMoreMessages }" if={ moreMessagesIsInStock } onclick={ fetchMoreMessages } disabled={ fetchingMoreMessages }> - <virtual if={ fetchingMoreMessages }>%fa:spinner .pulse .fw%</virtual>{ fetchingMoreMessages ? '%i18n:common.loading%' : '%i18n:common.tags.mk-messaging-room.more%' } - </button> - <virtual each={ message, i in messages }> - <mk-messaging-message message={ message }/> - <p class="date" if={ i != messages.length - 1 && message._date != messages[i + 1]._date }><span>{ messages[i + 1]._datetext }</span></p> - </virtual> - </div> - <footer> - <div ref="notifications"></div> - <div class="grippie" title="%i18n:common.tags.mk-messaging-room.resize-form%"></div> - <mk-messaging-form user={ user }/> - </footer> - <style> - :scope - display block - - > .stream - max-width 600px - margin 0 auto - - > .init - width 100% - margin 0 - padding 16px 8px 8px 8px - text-align center - font-size 0.8em - color rgba(0, 0, 0, 0.4) - - [data-fa] - margin-right 4px - - > .empty - width 100% - margin 0 - padding 16px 8px 8px 8px - text-align center - font-size 0.8em - color rgba(0, 0, 0, 0.4) - - [data-fa] - margin-right 4px - - > .no-history - display block - margin 0 - padding 16px - text-align center - font-size 0.8em - color rgba(0, 0, 0, 0.4) - - [data-fa] - margin-right 4px - - > .more - display block - margin 16px auto - padding 0 12px - line-height 24px - color #fff - background rgba(0, 0, 0, 0.3) - border-radius 12px - - &:hover - background rgba(0, 0, 0, 0.4) - - &:active - background rgba(0, 0, 0, 0.5) - - &.fetching - cursor wait - - > [data-fa] - margin-right 4px - - > .message - // something - - > .date - display block - margin 8px 0 - text-align center - - &:before - content '' - display block - position absolute - height 1px - width 90% - top 16px - left 0 - right 0 - margin 0 auto - background rgba(0, 0, 0, 0.1) - - > span - display inline-block - margin 0 - padding 0 16px - //font-weight bold - line-height 32px - color rgba(0, 0, 0, 0.3) - background #fff - - > footer - position -webkit-sticky - position sticky - z-index 2 - bottom 0 - width 100% - max-width 600px - margin 0 auto - padding 0 - background rgba(255, 255, 255, 0.95) - background-clip content-box - - > [ref='notifications'] - position absolute - top -48px - width 100% - padding 8px 0 - text-align center - - &:empty - display none - - > p - display inline-block - margin 0 - padding 0 12px 0 28px - cursor pointer - line-height 32px - font-size 12px - color $theme-color-foreground - background $theme-color - border-radius 16px - transition opacity 1s ease - - > [data-fa] - position absolute - top 0 - left 10px - line-height 32px - font-size 16px - - > .grippie - height 10px - margin-top -10px - background transparent - cursor ns-resize - - &:hover - //background rgba(0, 0, 0, 0.1) - - &:active - //background rgba(0, 0, 0, 0.2) - - </style> - <script> - import MessagingStreamConnection from '../../scripts/streaming/messaging-stream'; - - this.mixin('i'); - this.mixin('api'); - - this.user = this.opts.user; - this.init = true; - this.sending = false; - this.messages = []; - this.isNaked = this.opts.isNaked; - - this.connection = new MessagingStreamConnection(this.I, this.user.id); - - this.on('mount', () => { - this.connection.on('message', this.onMessage); - this.connection.on('read', this.onRead); - - document.addEventListener('visibilitychange', this.onVisibilitychange); - - this.fetchMessages().then(() => { - this.init = false; - this.update(); - this.scrollToBottom(); - }); - }); - - this.on('unmount', () => { - this.connection.off('message', this.onMessage); - this.connection.off('read', this.onRead); - this.connection.close(); - - document.removeEventListener('visibilitychange', this.onVisibilitychange); - }); - - this.on('update', () => { - this.messages.forEach(message => { - const date = (new Date(message.created_at)).getDate(); - const month = (new Date(message.created_at)).getMonth() + 1; - message._date = date; - message._datetext = month + '月 ' + date + '日'; - }); - }); - - this.onMessage = (message) => { - const isBottom = this.isBottom(); - - this.messages.push(message); - if (message.user_id != this.I.id && !document.hidden) { - this.connection.send({ - type: 'read', - id: message.id - }); - } - this.update(); - - if (isBottom) { - // Scroll to bottom - this.scrollToBottom(); - } else if (message.user_id != this.I.id) { - // Notify - this.notify('%i18n:common.tags.mk-messaging-room.new-message%'); - } - }; - - this.onRead = ids => { - if (!Array.isArray(ids)) ids = [ids]; - ids.forEach(id => { - if (this.messages.some(x => x.id == id)) { - const exist = this.messages.map(x => x.id).indexOf(id); - this.messages[exist].is_read = true; - this.update(); - } - }); - }; - - this.fetchMoreMessages = () => { - this.update({ - fetchingMoreMessages: true - }); - this.fetchMessages().then(() => { - this.update({ - fetchingMoreMessages: false - }); - }); - }; - - this.fetchMessages = () => new Promise((resolve, reject) => { - const max = this.moreMessagesIsInStock ? 20 : 10; - - this.api('messaging/messages', { - user_id: this.user.id, - limit: max + 1, - max_id: this.moreMessagesIsInStock ? this.messages[0].id : undefined - }).then(messages => { - if (messages.length == max + 1) { - this.moreMessagesIsInStock = true; - messages.pop(); - } else { - this.moreMessagesIsInStock = false; - } - - this.messages.unshift.apply(this.messages, messages.reverse()); - this.update(); - - resolve(); - }); - }); - - this.isBottom = () => { - const asobi = 32; - const current = this.isNaked - ? window.scrollY + window.innerHeight - : this.root.scrollTop + this.root.offsetHeight; - const max = this.isNaked - ? document.body.offsetHeight - : this.root.scrollHeight; - return current > (max - asobi); - }; - - this.scrollToBottom = () => { - if (this.isNaked) { - window.scroll(0, document.body.offsetHeight); - } else { - this.root.scrollTop = this.root.scrollHeight; - } - }; - - this.notify = message => { - const n = document.createElement('p'); - n.innerHTML = '%fa:arrow-circle-down%' + message; - n.onclick = () => { - this.scrollToBottom(); - n.parentNode.removeChild(n); - }; - this.refs.notifications.appendChild(n); - - setTimeout(() => { - n.style.opacity = 0; - setTimeout(() => n.parentNode.removeChild(n), 1000); - }, 4000); - }; - - this.onVisibilitychange = () => { - if (document.hidden) return; - this.messages.forEach(message => { - if (message.user_id !== this.I.id && !message.is_read) { - this.connection.send({ - type: 'read', - id: message.id - }); - } - }); - }; - </script> -</mk-messaging-room> diff --git a/src/web/app/common/tags/nav-links.tag b/src/web/app/common/tags/nav-links.tag deleted file mode 100644 index 71f0453db0..0000000000 --- a/src/web/app/common/tags/nav-links.tag +++ /dev/null @@ -1,7 +0,0 @@ -<mk-nav-links> - <a href={ _ABOUT_URL_ }>%i18n:common.tags.mk-nav-links.about%</a><i>・</i><a href={ _STATS_URL_ }>%i18n:common.tags.mk-nav-links.stats%</a><i>・</i><a href={ _STATUS_URL_ }>%i18n:common.tags.mk-nav-links.status%</a><i>・</i><a href="http://zawazawa.jp/misskey/">%i18n:common.tags.mk-nav-links.wiki%</a><i>・</i><a href="https://github.com/syuilo/misskey/blob/master/DONORS.md">%i18n:common.tags.mk-nav-links.donors%</a><i>・</i><a href="https://github.com/syuilo/misskey">%i18n:common.tags.mk-nav-links.repository%</a><i>・</i><a href={ _DEV_URL_ }>%i18n:common.tags.mk-nav-links.develop%</a><i>・</i><a href="https://twitter.com/misskey_xyz" target="_blank">Follow us on %fa:B twitter%</a> - <style> - :scope - display inline - </style> -</mk-nav-links> diff --git a/src/web/app/common/tags/number.tag b/src/web/app/common/tags/number.tag deleted file mode 100644 index 7afb8b3983..0000000000 --- a/src/web/app/common/tags/number.tag +++ /dev/null @@ -1,16 +0,0 @@ -<mk-number> - <style> - :scope - display inline - </style> - <script> - this.on('mount', () => { - let value = this.opts.value; - const max = this.opts.max; - - if (max != null && value > max) value = max; - - this.root.innerHTML = value.toLocaleString(); - }); - </script> -</mk-number> diff --git a/src/web/app/common/tags/poll-editor.tag b/src/web/app/common/tags/poll-editor.tag deleted file mode 100644 index e79209e9b4..0000000000 --- a/src/web/app/common/tags/poll-editor.tag +++ /dev/null @@ -1,121 +0,0 @@ -<mk-poll-editor> - <p class="caution" if={ choices.length < 2 }> - %fa:exclamation-triangle%%i18n:common.tags.mk-poll-editor.no-only-one-choice% - </p> - <ul ref="choices"> - <li each={ choice, i in choices }> - <input value={ choice } oninput={ oninput.bind(null, i) } placeholder={ '%i18n:common.tags.mk-poll-editor.choice-n%'.replace('{}', i + 1) }> - <button onclick={ remove.bind(null, i) } title="%i18n:common.tags.mk-poll-editor.remove%"> - %fa:times% - </button> - </li> - </ul> - <button class="add" if={ choices.length < 10 } onclick={ add }>%i18n:common.tags.mk-poll-editor.add%</button> - <button class="destroy" onclick={ destroy } title="%i18n:common.tags.mk-poll-editor.destroy%"> - %fa:times% - </button> - <style> - :scope - display block - padding 8px - - > .caution - margin 0 0 8px 0 - font-size 0.8em - color #f00 - - > [data-fa] - margin-right 4px - - > ul - display block - margin 0 - padding 0 - list-style none - - > li - display block - margin 8px 0 - padding 0 - width 100% - - &:first-child - margin-top 0 - - &:last-child - margin-bottom 0 - - > input - padding 6px - border solid 1px rgba($theme-color, 0.1) - border-radius 4px - - &:hover - border-color rgba($theme-color, 0.2) - - &:focus - border-color rgba($theme-color, 0.5) - - > button - padding 4px 8px - color rgba($theme-color, 0.4) - - &:hover - color rgba($theme-color, 0.6) - - &:active - color darken($theme-color, 30%) - - > .add - margin 8px 0 0 0 - vertical-align top - color $theme-color - - > .destroy - position absolute - top 0 - right 0 - padding 4px 8px - color rgba($theme-color, 0.4) - - &:hover - color rgba($theme-color, 0.6) - - &:active - color darken($theme-color, 30%) - - </style> - <script> - this.choices = ['', '']; - - this.oninput = (i, e) => { - this.choices[i] = e.target.value; - }; - - this.add = () => { - this.choices.push(''); - this.update(); - this.refs.choices.childNodes[this.choices.length - 1].childNodes[0].focus(); - }; - - this.remove = (i) => { - this.choices = this.choices.filter((_, _i) => _i != i); - this.update(); - }; - - this.destroy = () => { - this.opts.ondestroy(); - }; - - this.get = () => { - return { - choices: this.choices.filter(choice => choice != '') - } - }; - - this.set = data => { - if (data.choices.length == 0) return; - this.choices = data.choices; - }; - </script> -</mk-poll-editor> diff --git a/src/web/app/common/tags/poll.tag b/src/web/app/common/tags/poll.tag deleted file mode 100644 index 32542418aa..0000000000 --- a/src/web/app/common/tags/poll.tag +++ /dev/null @@ -1,109 +0,0 @@ -<mk-poll data-is-voted={ isVoted }> - <ul> - <li each={ poll.choices } onclick={ vote.bind(null, id) } class={ voted: voted } title={ !parent.isVoted ? '%i18n:common.tags.mk-poll.vote-to%'.replace('{}', text) : '' }> - <div class="backdrop" style={ 'width:' + (parent.result ? (votes / parent.total * 100) : 0) + '%' }></div> - <span> - <virtual if={ is_voted }>%fa:check%</virtual> - { text } - <span class="votes" if={ parent.result }>({ '%i18n:common.tags.mk-poll.vote-count%'.replace('{}', votes) })</span> - </span> - </li> - </ul> - <p if={ total > 0 }> - <span>{ '%i18n:common.tags.mk-poll.total-users%'.replace('{}', total) }</span> - ・ - <a if={ !isVoted } onclick={ toggleResult }>{ result ? '%i18n:common.tags.mk-poll.vote%' : '%i18n:common.tags.mk-poll.show-result%' }</a> - <span if={ isVoted }>%i18n:common.tags.mk-poll.voted%</span> - </p> - <style> - :scope - display block - - > ul - display block - margin 0 - padding 0 - list-style none - - > li - display block - margin 4px 0 - padding 4px 8px - width 100% - border solid 1px #eee - border-radius 4px - overflow hidden - cursor pointer - - &:hover - background rgba(0, 0, 0, 0.05) - - &:active - background rgba(0, 0, 0, 0.1) - - > .backdrop - position absolute - top 0 - left 0 - height 100% - background $theme-color - transition width 1s ease - - > .votes - margin-left 4px - - > p - a - color inherit - - &[data-is-voted] - > ul > li - cursor default - - &:hover - background transparent - - &:active - background transparent - - </style> - <script> - this.mixin('api'); - - this.init = post => { - this.post = post; - this.poll = this.post.poll; - this.total = this.poll.choices.reduce((a, b) => a + b.votes, 0); - this.isVoted = this.poll.choices.some(c => c.is_voted); - this.result = this.isVoted; - this.update(); - }; - - this.init(this.opts.post); - - this.toggleResult = () => { - this.result = !this.result; - }; - - this.vote = id => { - if (this.poll.choices.some(c => c.is_voted)) return; - this.api('posts/polls/vote', { - post_id: this.post.id, - choice: id - }).then(() => { - this.poll.choices.forEach(c => { - if (c.id == id) { - c.votes++; - c.is_voted = true; - } - }); - this.update({ - poll: this.poll, - isVoted: true, - result: true, - total: this.total + 1 - }); - }); - }; - </script> -</mk-poll> diff --git a/src/web/app/common/tags/post-menu.tag b/src/web/app/common/tags/post-menu.tag deleted file mode 100644 index be4468a214..0000000000 --- a/src/web/app/common/tags/post-menu.tag +++ /dev/null @@ -1,157 +0,0 @@ -<mk-post-menu> - <div class="backdrop" ref="backdrop" onclick={ close }></div> - <div class="popover { compact: opts.compact }" ref="popover"> - <button if={ post.user_id === I.id } onclick={ pin }>%i18n:common.tags.mk-post-menu.pin%</button> - <div if={ I.is_pro && !post.is_category_verified }> - <select ref="categorySelect"> - <option value="">%i18n:common.tags.mk-post-menu.select%</option> - <option value="music">%i18n:common.post_categories.music%</option> - <option value="game">%i18n:common.post_categories.game%</option> - <option value="anime">%i18n:common.post_categories.anime%</option> - <option value="it">%i18n:common.post_categories.it%</option> - <option value="gadgets">%i18n:common.post_categories.gadgets%</option> - <option value="photography">%i18n:common.post_categories.photography%</option> - </select> - <button onclick={ categorize }>%i18n:common.tags.mk-post-menu.categorize%</button> - </div> - </div> - <style> - $border-color = rgba(27, 31, 35, 0.15) - - :scope - display block - position initial - - > .backdrop - position fixed - top 0 - left 0 - z-index 10000 - width 100% - height 100% - background rgba(0, 0, 0, 0.1) - opacity 0 - - > .popover - position absolute - z-index 10001 - background #fff - border 1px solid $border-color - border-radius 4px - box-shadow 0 3px 12px rgba(27, 31, 35, 0.15) - transform scale(0.5) - opacity 0 - - $balloon-size = 16px - - &:not(.compact) - margin-top $balloon-size - transform-origin center -($balloon-size) - - &:before - content "" - display block - position absolute - top -($balloon-size * 2) - left s('calc(50% - %s)', $balloon-size) - border-top solid $balloon-size transparent - border-left solid $balloon-size transparent - border-right solid $balloon-size transparent - border-bottom solid $balloon-size $border-color - - &:after - content "" - display block - position absolute - top -($balloon-size * 2) + 1.5px - left s('calc(50% - %s)', $balloon-size) - border-top solid $balloon-size transparent - border-left solid $balloon-size transparent - border-right solid $balloon-size transparent - border-bottom solid $balloon-size #fff - - > button - display block - - </style> - <script> - import anime from 'animejs'; - - this.mixin('i'); - this.mixin('api'); - - this.post = this.opts.post; - this.source = this.opts.source; - - this.on('mount', () => { - const rect = this.source.getBoundingClientRect(); - const width = this.refs.popover.offsetWidth; - const height = this.refs.popover.offsetHeight; - if (this.opts.compact) { - const x = rect.left + window.pageXOffset + (this.source.offsetWidth / 2); - const y = rect.top + window.pageYOffset + (this.source.offsetHeight / 2); - this.refs.popover.style.left = (x - (width / 2)) + 'px'; - this.refs.popover.style.top = (y - (height / 2)) + 'px'; - } else { - const x = rect.left + window.pageXOffset + (this.source.offsetWidth / 2); - const y = rect.top + window.pageYOffset + this.source.offsetHeight; - this.refs.popover.style.left = (x - (width / 2)) + 'px'; - this.refs.popover.style.top = y + 'px'; - } - - anime({ - targets: this.refs.backdrop, - opacity: 1, - duration: 100, - easing: 'linear' - }); - - anime({ - targets: this.refs.popover, - opacity: 1, - scale: [0.5, 1], - duration: 500 - }); - }); - - this.pin = () => { - this.api('i/pin', { - post_id: this.post.id - }).then(() => { - if (this.opts.cb) this.opts.cb('pinned', '%i18n:common.tags.mk-post-menu.pinned%'); - this.unmount(); - }); - }; - - this.categorize = () => { - const category = this.refs.categorySelect.options[this.refs.categorySelect.selectedIndex].value; - this.api('posts/categorize', { - post_id: this.post.id, - category: category - }).then(() => { - if (this.opts.cb) this.opts.cb('categorized', '%i18n:common.tags.mk-post-menu.categorized%'); - this.unmount(); - }); - }; - - this.close = () => { - this.refs.backdrop.style.pointerEvents = 'none'; - anime({ - targets: this.refs.backdrop, - opacity: 0, - duration: 200, - easing: 'linear' - }); - - this.refs.popover.style.pointerEvents = 'none'; - anime({ - targets: this.refs.popover, - opacity: 0, - scale: 0.5, - duration: 200, - easing: 'easeInBack', - complete: () => this.unmount() - }); - }; - </script> -</mk-post-menu> diff --git a/src/web/app/common/tags/raw.tag b/src/web/app/common/tags/raw.tag deleted file mode 100644 index adc6de5a3b..0000000000 --- a/src/web/app/common/tags/raw.tag +++ /dev/null @@ -1,13 +0,0 @@ -<mk-raw> - <style> - :scope - display inline - </style> - <script> - this.root.innerHTML = this.opts.content; - - this.on('updated', () => { - this.root.innerHTML = this.opts.content; - }); - </script> -</mk-raw> diff --git a/src/web/app/common/tags/reaction-icon.tag b/src/web/app/common/tags/reaction-icon.tag deleted file mode 100644 index 0127293917..0000000000 --- a/src/web/app/common/tags/reaction-icon.tag +++ /dev/null @@ -1,21 +0,0 @@ -<mk-reaction-icon> - <virtual if={ opts.reaction == 'like' }><img src="/assets/reactions/like.png" alt="%i18n:common.reactions.like%"></virtual> - <virtual if={ opts.reaction == 'love' }><img src="/assets/reactions/love.png" alt="%i18n:common.reactions.love%"></virtual> - <virtual if={ opts.reaction == 'laugh' }><img src="/assets/reactions/laugh.png" alt="%i18n:common.reactions.laugh%"></virtual> - <virtual if={ opts.reaction == 'hmm' }><img src="/assets/reactions/hmm.png" alt="%i18n:common.reactions.hmm%"></virtual> - <virtual if={ opts.reaction == 'surprise' }><img src="/assets/reactions/surprise.png" alt="%i18n:common.reactions.surprise%"></virtual> - <virtual if={ opts.reaction == 'congrats' }><img src="/assets/reactions/congrats.png" alt="%i18n:common.reactions.congrats%"></virtual> - <virtual if={ opts.reaction == 'angry' }><img src="/assets/reactions/angry.png" alt="%i18n:common.reactions.angry%"></virtual> - <virtual if={ opts.reaction == 'confused' }><img src="/assets/reactions/confused.png" alt="%i18n:common.reactions.confused%"></virtual> - <virtual if={ opts.reaction == 'pudding' }><img src="/assets/reactions/pudding.png" alt="%i18n:common.reactions.pudding%"></virtual> - - <style> - :scope - display inline - - img - vertical-align middle - width 1em - height 1em - </style> -</mk-reaction-icon> diff --git a/src/web/app/common/tags/reaction-picker.tag b/src/web/app/common/tags/reaction-picker.tag deleted file mode 100644 index 458d16ec71..0000000000 --- a/src/web/app/common/tags/reaction-picker.tag +++ /dev/null @@ -1,184 +0,0 @@ -<mk-reaction-picker> - <div class="backdrop" ref="backdrop" onclick={ close }></div> - <div class="popover { compact: opts.compact }" ref="popover"> - <p if={ !opts.compact }>{ title }</p> - <div> - <button onclick={ react.bind(null, 'like') } onmouseover={ onmouseover } onmouseout={ onmouseout } tabindex="1" title="%i18n:common.reactions.like%"><mk-reaction-icon reaction='like'/></button> - <button onclick={ react.bind(null, 'love') } onmouseover={ onmouseover } onmouseout={ onmouseout } tabindex="2" title="%i18n:common.reactions.love%"><mk-reaction-icon reaction='love'/></button> - <button onclick={ react.bind(null, 'laugh') } onmouseover={ onmouseover } onmouseout={ onmouseout } tabindex="3" title="%i18n:common.reactions.laugh%"><mk-reaction-icon reaction='laugh'/></button> - <button onclick={ react.bind(null, 'hmm') } onmouseover={ onmouseover } onmouseout={ onmouseout } tabindex="4" title="%i18n:common.reactions.hmm%"><mk-reaction-icon reaction='hmm'/></button> - <button onclick={ react.bind(null, 'surprise') } onmouseover={ onmouseover } onmouseout={ onmouseout } tabindex="5" title="%i18n:common.reactions.surprise%"><mk-reaction-icon reaction='surprise'/></button> - <button onclick={ react.bind(null, 'congrats') } onmouseover={ onmouseover } onmouseout={ onmouseout } tabindex="6" title="%i18n:common.reactions.congrats%"><mk-reaction-icon reaction='congrats'/></button> - <button onclick={ react.bind(null, 'angry') } onmouseover={ onmouseover } onmouseout={ onmouseout } tabindex="4" title="%i18n:common.reactions.angry%"><mk-reaction-icon reaction='angry'/></button> - <button onclick={ react.bind(null, 'confused') } onmouseover={ onmouseover } onmouseout={ onmouseout } tabindex="5" title="%i18n:common.reactions.confused%"><mk-reaction-icon reaction='confused'/></button> - <button onclick={ react.bind(null, 'pudding') } onmouseover={ onmouseover } onmouseout={ onmouseout } tabindex="6" title="%i18n:common.reactions.pudding%"><mk-reaction-icon reaction='pudding'/></button> - </div> - </div> - <style> - $border-color = rgba(27, 31, 35, 0.15) - - :scope - display block - position initial - - > .backdrop - position fixed - top 0 - left 0 - z-index 10000 - width 100% - height 100% - background rgba(0, 0, 0, 0.1) - opacity 0 - - > .popover - position absolute - z-index 10001 - background #fff - border 1px solid $border-color - border-radius 4px - box-shadow 0 3px 12px rgba(27, 31, 35, 0.15) - transform scale(0.5) - opacity 0 - - $balloon-size = 16px - - &:not(.compact) - margin-top $balloon-size - transform-origin center -($balloon-size) - - &:before - content "" - display block - position absolute - top -($balloon-size * 2) - left s('calc(50% - %s)', $balloon-size) - border-top solid $balloon-size transparent - border-left solid $balloon-size transparent - border-right solid $balloon-size transparent - border-bottom solid $balloon-size $border-color - - &:after - content "" - display block - position absolute - top -($balloon-size * 2) + 1.5px - left s('calc(50% - %s)', $balloon-size) - border-top solid $balloon-size transparent - border-left solid $balloon-size transparent - border-right solid $balloon-size transparent - border-bottom solid $balloon-size #fff - - > p - display block - margin 0 - padding 8px 10px - font-size 14px - color #586069 - border-bottom solid 1px #e1e4e8 - - > div - padding 4px - width 240px - text-align center - - > button - width 40px - height 40px - font-size 24px - border-radius 2px - - &:hover - background #eee - - &:active - background $theme-color - box-shadow inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15) - - </style> - <script> - import anime from 'animejs'; - - this.mixin('api'); - - this.post = this.opts.post; - this.source = this.opts.source; - - const placeholder = '%i18n:common.tags.mk-reaction-picker.choose-reaction%'; - - this.title = placeholder; - - this.onmouseover = e => { - this.update({ - title: e.target.title - }); - }; - - this.onmouseout = () => { - this.update({ - title: placeholder - }); - }; - - this.on('mount', () => { - const rect = this.source.getBoundingClientRect(); - const width = this.refs.popover.offsetWidth; - const height = this.refs.popover.offsetHeight; - if (this.opts.compact) { - const x = rect.left + window.pageXOffset + (this.source.offsetWidth / 2); - const y = rect.top + window.pageYOffset + (this.source.offsetHeight / 2); - this.refs.popover.style.left = (x - (width / 2)) + 'px'; - this.refs.popover.style.top = (y - (height / 2)) + 'px'; - } else { - const x = rect.left + window.pageXOffset + (this.source.offsetWidth / 2); - const y = rect.top + window.pageYOffset + this.source.offsetHeight; - this.refs.popover.style.left = (x - (width / 2)) + 'px'; - this.refs.popover.style.top = y + 'px'; - } - - anime({ - targets: this.refs.backdrop, - opacity: 1, - duration: 100, - easing: 'linear' - }); - - anime({ - targets: this.refs.popover, - opacity: 1, - scale: [0.5, 1], - duration: 500 - }); - }); - - this.react = reaction => { - this.api('posts/reactions/create', { - post_id: this.post.id, - reaction: reaction - }).then(() => { - if (this.opts.cb) this.opts.cb(); - this.unmount(); - }); - }; - - this.close = () => { - this.refs.backdrop.style.pointerEvents = 'none'; - anime({ - targets: this.refs.backdrop, - opacity: 0, - duration: 200, - easing: 'linear' - }); - - this.refs.popover.style.pointerEvents = 'none'; - anime({ - targets: this.refs.popover, - opacity: 0, - scale: 0.5, - duration: 200, - easing: 'easeInBack', - complete: () => this.unmount() - }); - }; - </script> -</mk-reaction-picker> diff --git a/src/web/app/common/tags/reactions-viewer.tag b/src/web/app/common/tags/reactions-viewer.tag deleted file mode 100644 index 50fb023f70..0000000000 --- a/src/web/app/common/tags/reactions-viewer.tag +++ /dev/null @@ -1,46 +0,0 @@ -<mk-reactions-viewer> - <virtual if={ reactions }> - <span if={ reactions.like }><mk-reaction-icon reaction='like'/><span>{ reactions.like }</span></span> - <span if={ reactions.love }><mk-reaction-icon reaction='love'/><span>{ reactions.love }</span></span> - <span if={ reactions.laugh }><mk-reaction-icon reaction='laugh'/><span>{ reactions.laugh }</span></span> - <span if={ reactions.hmm }><mk-reaction-icon reaction='hmm'/><span>{ reactions.hmm }</span></span> - <span if={ reactions.surprise }><mk-reaction-icon reaction='surprise'/><span>{ reactions.surprise }</span></span> - <span if={ reactions.congrats }><mk-reaction-icon reaction='congrats'/><span>{ reactions.congrats }</span></span> - <span if={ reactions.angry }><mk-reaction-icon reaction='angry'/><span>{ reactions.angry }</span></span> - <span if={ reactions.confused }><mk-reaction-icon reaction='confused'/><span>{ reactions.confused }</span></span> - <span if={ reactions.pudding }><mk-reaction-icon reaction='pudding'/><span>{ reactions.pudding }</span></span> - </virtual> - <style> - :scope - display block - border-top dashed 1px #eee - border-bottom dashed 1px #eee - margin 4px 0 - - &:empty - display none - - > span - margin-right 8px - - > mk-reaction-icon - font-size 1.4em - - > span - margin-left 4px - font-size 1.2em - color #444 - - </style> - <script> - this.post = this.opts.post; - - this.on('mount', () => { - this.update(); - }); - - this.on('update', () => { - this.reactions = this.post.reaction_counts; - }); - </script> -</mk-reactions-viewer> diff --git a/src/web/app/common/tags/signin-history.tag b/src/web/app/common/tags/signin-history.tag deleted file mode 100644 index cdd58c4c67..0000000000 --- a/src/web/app/common/tags/signin-history.tag +++ /dev/null @@ -1,116 +0,0 @@ -<mk-signin-history> - <div class="records" if={ history.length != 0 }> - <mk-signin-record each={ rec in history } rec={ rec }/> - </div> - <style> - :scope - display block - - </style> - <script> - this.mixin('i'); - this.mixin('api'); - - this.mixin('stream'); - this.connection = this.stream.getConnection(); - this.connectionId = this.stream.use(); - - this.history = []; - this.fetching = true; - - this.on('mount', () => { - this.api('i/signin_history').then(history => { - this.update({ - fetching: false, - history: history - }); - }); - - this.connection.on('signin', this.onSignin); - }); - - this.on('unmount', () => { - this.connection.off('signin', this.onSignin); - this.stream.dispose(this.connectionId); - }); - - this.onSignin = signin => { - this.history.unshift(signin); - this.update(); - }; - </script> -</mk-signin-history> - -<mk-signin-record> - <header onclick={ toggle }> - <virtual if={ rec.success }>%fa:check%</virtual> - <virtual if={ !rec.success }>%fa:times%</virtual> - <span class="ip">{ rec.ip }</span> - <mk-time time={ rec.created_at }/> - </header> - <pre ref="headers" class="json" show={ show }>{ JSON.stringify(rec.headers, null, 2) }</pre> - - <style> - :scope - display block - border-bottom solid 1px #eee - - > header - display flex - padding 8px 0 - line-height 32px - cursor pointer - - > [data-fa] - margin-right 8px - text-align left - - &.check - color #0fda82 - - &.times - color #ff3100 - - > .ip - display inline-block - text-align left - padding 8px - line-height 16px - font-family monospace - font-size 14px - color #444 - background #f8f8f8 - border-radius 4px - - > mk-time - margin-left auto - text-align right - color #777 - - > pre - overflow auto - margin 0 0 16px 0 - max-height 100px - white-space pre-wrap - word-break break-all - color #4a535a - - </style> - - <script> - import hljs from 'highlight.js'; - - this.rec = this.opts.rec; - this.show = false; - - this.on('mount', () => { - hljs.highlightBlock(this.refs.headers); - }); - - this.toggle = () => { - this.update({ - show: !this.show - }); - }; - </script> -</mk-signin-record> diff --git a/src/web/app/common/tags/signin.tag b/src/web/app/common/tags/signin.tag deleted file mode 100644 index f5a2be94ed..0000000000 --- a/src/web/app/common/tags/signin.tag +++ /dev/null @@ -1,155 +0,0 @@ -<mk-signin> - <form class={ signing: signing } onsubmit={ onsubmit }> - <label class="user-name"> - <input ref="username" type="text" pattern="^[a-zA-Z0-9-]+$" placeholder="%i18n:common.tags.mk-signin.username%" autofocus="autofocus" required="required" oninput={ oninput }/>%fa:at% - </label> - <label class="password"> - <input ref="password" type="password" placeholder="%i18n:common.tags.mk-signin.password%" required="required"/>%fa:lock% - </label> - <label class="token" if={ user && user.two_factor_enabled }> - <input ref="token" type="number" placeholder="%i18n:common.tags.mk-signin.token%" required="required"/>%fa:lock% - </label> - <button type="submit" disabled={ signing }>{ signing ? '%i18n:common.tags.mk-signin.signing-in%' : '%i18n:common.tags.mk-signin.signin%' }</button> - </form> - <style> - :scope - display block - - > form - display block - z-index 2 - - &.signing - &, * - cursor wait !important - - label - display block - margin 12px 0 - - [data-fa] - display block - pointer-events none - position absolute - bottom 0 - top 0 - left 0 - z-index 1 - margin auto - padding 0 16px - height 1em - color #898786 - - input[type=text] - input[type=password] - input[type=number] - user-select text - display inline-block - cursor auto - padding 0 0 0 38px - margin 0 - width 100% - line-height 44px - font-size 1em - color rgba(0, 0, 0, 0.7) - background #fff - outline none - border solid 1px #eee - border-radius 4px - - &:hover - background rgba(255, 255, 255, 0.7) - border-color #ddd - - & + i - color #797776 - - &:focus - background #fff - border-color #ccc - - & + i - color #797776 - - [type=submit] - cursor pointer - padding 16px - margin -6px 0 0 0 - width 100% - font-size 1.2em - color rgba(0, 0, 0, 0.5) - outline none - border none - border-radius 0 - background transparent - transition all .5s ease - - &:hover - color $theme-color - transition all .2s ease - - &:focus - color $theme-color - transition all .2s ease - - &:active - color darken($theme-color, 30%) - transition all .2s ease - - &:disabled - opacity 0.7 - - </style> - <script> - this.mixin('api'); - - this.user = null; - this.signing = false; - - this.oninput = () => { - this.api('users/show', { - username: this.refs.username.value - }).then(user => { - this.user = user; - this.trigger('user', user); - this.update(); - }); - }; - - this.onsubmit = e => { - e.preventDefault(); - - if (this.refs.username.value == '') { - this.refs.username.focus(); - return false; - } - if (this.refs.password.value == '') { - this.refs.password.focus(); - return false; - } - if (this.user && this.user.two_factor_enabled && this.refs.token.value == '') { - this.refs.token.focus(); - return false; - } - - this.update({ - signing: true - }); - - this.api('signin', { - username: this.refs.username.value, - password: this.refs.password.value, - token: this.user && this.user.two_factor_enabled ? this.refs.token.value : undefined - }).then(() => { - location.reload(); - }).catch(() => { - alert('something happened'); - this.update({ - signing: false - }); - }); - - return false; - }; - </script> -</mk-signin> diff --git a/src/web/app/common/tags/signup.tag b/src/web/app/common/tags/signup.tag deleted file mode 100644 index 4816fe66db..0000000000 --- a/src/web/app/common/tags/signup.tag +++ /dev/null @@ -1,305 +0,0 @@ -<mk-signup> - <form onsubmit={ onsubmit } autocomplete="off"> - <label class="username"> - <p class="caption">%fa:at%%i18n:common.tags.mk-signup.username%</p> - <input ref="username" type="text" pattern="^[a-zA-Z0-9-]{3,20}$" placeholder="a~z、A~Z、0~9、-" autocomplete="off" required="required" onkeyup={ onChangeUsername }/> - <p class="profile-page-url-preview" if={ refs.username.value != '' && username-state != 'invalidFormat' && username-state != 'minRange' && username-state != 'maxRange' }>{ _URL_ + '/' + refs.username.value }</p> - <p class="info" if={ usernameState == 'wait' } style="color:#999">%fa:spinner .pulse .fw%%i18n:common.tags.mk-signup.checking%</p> - <p class="info" if={ usernameState == 'ok' } style="color:#3CB7B5">%fa:check .fw%%i18n:common.tags.mk-signup.available%</p> - <p class="info" if={ usernameState == 'unavailable' } style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:common.tags.mk-signup.unavailable%</p> - <p class="info" if={ usernameState == 'error' } style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:common.tags.mk-signup.error%</p> - <p class="info" if={ usernameState == 'invalid-format' } style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:common.tags.mk-signup.invalid-format%</p> - <p class="info" if={ usernameState == 'min-range' } style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:common.tags.mk-signup.too-short%</p> - <p class="info" if={ usernameState == 'max-range' } style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:common.tags.mk-signup.too-long%</p> - </label> - <label class="password"> - <p class="caption">%fa:lock%%i18n:common.tags.mk-signup.password%</p> - <input ref="password" type="password" placeholder="%i18n:common.tags.mk-signup.password-placeholder%" autocomplete="off" required="required" onkeyup={ onChangePassword }/> - <div class="meter" if={ passwordStrength != '' } data-strength={ passwordStrength }> - <div class="value" ref="passwordMetar"></div> - </div> - <p class="info" if={ passwordStrength == 'low' } style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:common.tags.mk-signup.weak-password%</p> - <p class="info" if={ passwordStrength == 'medium' } style="color:#3CB7B5">%fa:check .fw%%i18n:common.tags.mk-signup.normal-password%</p> - <p class="info" if={ passwordStrength == 'high' } style="color:#3CB7B5">%fa:check .fw%%i18n:common.tags.mk-signup.strong-password%</p> - </label> - <label class="retype-password"> - <p class="caption">%fa:lock%%i18n:common.tags.mk-signup.password%(%i18n:common.tags.mk-signup.retype%)</p> - <input ref="passwordRetype" type="password" placeholder="%i18n:common.tags.mk-signup.retype-placeholder%" autocomplete="off" required="required" onkeyup={ onChangePasswordRetype }/> - <p class="info" if={ passwordRetypeState == 'match' } style="color:#3CB7B5">%fa:check .fw%%i18n:common.tags.mk-signup.password-matched%</p> - <p class="info" if={ passwordRetypeState == 'not-match' } style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:common.tags.mk-signup.password-not-matched%</p> - </label> - <label class="recaptcha"> - <p class="caption"><virtual if={ recaptchaed }>%fa:toggle-on%</virtual><virtual if={ !recaptchaed }>%fa:toggle-off%</virtual>%i18n:common.tags.mk-signup.recaptcha%</p> - <div if={ recaptcha } class="g-recaptcha" data-callback="onRecaptchaed" data-expired-callback="onRecaptchaExpired" data-sitekey={ recaptcha.site_key }></div> - </label> - <label class="agree-tou"> - <input name="agree-tou" type="checkbox" autocomplete="off" required="required"/> - <p><a href="https://github.com/syuilo/misskey/blob/master/src/docs/tou.md" target="_blank">利用規約</a>に同意する</p> - </label> - <button onclick={ onsubmit }>%i18n:common.tags.mk-signup.create%</button> - </form> - <style> - :scope - display block - min-width 302px - overflow hidden - - > form - - label - display block - margin 16px 0 - - > .caption - margin 0 0 4px 0 - color #828888 - 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 - - &.username - .profile-page-url-preview - display block - margin 4px 8px 0 4px - font-size 0.8em - color #888 - - &:empty - display none - - &:not(:empty) + .info - margin-top 0 - - &.password - .meter - display block - margin-top 8px - width 100% - height 8px - - &[data-strength=''] - display none - - &[data-strength='low'] - > .value - background #d73612 - - &[data-strength='medium'] - > .value - background #d7ca12 - - &[data-strength='high'] - > .value - background #61bb22 - - > .value - display block - width 0% - height 100% - background transparent - border-radius 4px - transition all 0.1s ease - - [type=text], [type=password] - user-select text - display inline-block - cursor auto - padding 0 12px - margin 0 - width 100% - line-height 44px - font-size 1em - color #333 !important - background #fff !important - outline none - border solid 1px rgba(0, 0, 0, 0.1) - border-radius 4px - box-shadow 0 0 0 114514px #fff inset - transition all .3s ease - - &:hover - border-color rgba(0, 0, 0, 0.2) - transition all .1s ease - - &:focus - color $theme-color !important - border-color $theme-color - box-shadow 0 0 0 1024px #fff inset, 0 0 0 4px rgba($theme-color, 10%) - transition all 0s ease - - &:disabled - opacity 0.5 - - .agree-tou - padding 4px - border-radius 4px - - &:hover - background #f4f4f4 - - &:active - background #eee - - &, * - cursor pointer - - p - display inline - color #555 - - button - margin 0 0 32px 0 - padding 16px - width 100% - font-size 1em - color #fff - background $theme-color - border-radius 3px - - &:hover - background lighten($theme-color, 5%) - - &:active - background darken($theme-color, 5%) - - </style> - <script> - this.mixin('api'); - const getPasswordStrength = require('syuilo-password-strength'); - - this.usernameState = null; - this.passwordStrength = ''; - this.passwordRetypeState = null; - this.recaptchaed = false; - - window.onRecaptchaed = () => { - this.recaptchaed = true; - this.update(); - }; - - window.onRecaptchaExpired = () => { - this.recaptchaed = false; - this.update(); - }; - - this.on('mount', () => { - this.update({ - recaptcha: { - site_key: _RECAPTCHA_SITEKEY_ - } - }); - - const head = document.getElementsByTagName('head')[0]; - const script = document.createElement('script'); - script.setAttribute('src', 'https://www.google.com/recaptcha/api.js'); - head.appendChild(script); - }); - - this.onChangeUsername = () => { - const username = this.refs.username.value; - - if (username == '') { - this.update({ - usernameState: null - }); - return; - } - - const err = - !username.match(/^[a-zA-Z0-9\-]+$/) ? 'invalid-format' : - username.length < 3 ? 'min-range' : - username.length > 20 ? 'max-range' : - null; - - if (err) { - this.update({ - usernameState: err - }); - return; - } - - this.update({ - usernameState: 'wait' - }); - - this.api('username/available', { - username: username - }).then(result => { - this.update({ - usernameState: result.available ? 'ok' : 'unavailable' - }); - }).catch(err => { - this.update({ - usernameState: 'error' - }); - }); - }; - - this.onChangePassword = () => { - const password = this.refs.password.value; - - if (password == '') { - this.passwordStrength = ''; - return; - } - - const strength = getPasswordStrength(password); - this.passwordStrength = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low'; - this.update(); - this.refs.passwordMetar.style.width = `${strength * 100}%`; - }; - - this.onChangePasswordRetype = () => { - const password = this.refs.password.value; - const retypedPassword = this.refs.passwordRetype.value; - - if (retypedPassword == '') { - this.passwordRetypeState = null; - return; - } - - this.passwordRetypeState = password == retypedPassword ? 'match' : 'not-match'; - }; - - this.onsubmit = e => { - e.preventDefault(); - - const username = this.refs.username.value; - const password = this.refs.password.value; - - const locker = document.body.appendChild(document.createElement('mk-locker')); - - this.api('signup', { - username: username, - password: password, - 'g-recaptcha-response': grecaptcha.getResponse() - }).then(() => { - this.api('signin', { - username: username, - password: password - }).then(() => { - location.href = '/'; - }); - }).catch(() => { - alert('%i18n:common.tags.mk-signup.some-error%'); - - grecaptcha.reset(); - this.recaptchaed = false; - - locker.parentNode.removeChild(locker); - }); - - return false; - }; - </script> -</mk-signup> diff --git a/src/web/app/common/tags/special-message.tag b/src/web/app/common/tags/special-message.tag deleted file mode 100644 index 6643b1324a..0000000000 --- a/src/web/app/common/tags/special-message.tag +++ /dev/null @@ -1,27 +0,0 @@ -<mk-special-message> - <p if={ m == 1 && d == 1 }>%i18n:common.tags.mk-special-message.new-year%</p> - <p if={ m == 12 && d == 25 }>%i18n:common.tags.mk-special-message.christmas%</p> - <style> - :scope - display block - - &:empty - display none - - > p - margin 0 - padding 4px - text-align center - font-size 14px - font-weight bold - text-transform uppercase - color #fff - background #ff1036 - - </style> - <script> - const now = new Date(); - this.d = now.getDate(); - this.m = now.getMonth() + 1; - </script> -</mk-special-message> diff --git a/src/web/app/common/tags/stream-indicator.tag b/src/web/app/common/tags/stream-indicator.tag deleted file mode 100644 index 0eb6196b6d..0000000000 --- a/src/web/app/common/tags/stream-indicator.tag +++ /dev/null @@ -1,78 +0,0 @@ -<mk-stream-indicator> - <p if={ connection.state == 'initializing' }> - %fa:spinner .pulse% - <span>%i18n:common.tags.mk-stream-indicator.connecting%<mk-ellipsis/></span> - </p> - <p if={ connection.state == 'reconnecting' }> - %fa:spinner .pulse% - <span>%i18n:common.tags.mk-stream-indicator.reconnecting%<mk-ellipsis/></span> - </p> - <p if={ connection.state == 'connected' }> - %fa:check% - <span>%i18n:common.tags.mk-stream-indicator.connected%</span> - </p> - <style> - :scope - display block - pointer-events none - position fixed - z-index 16384 - bottom 8px - right 8px - margin 0 - padding 6px 12px - font-size 0.9em - color #fff - background rgba(0, 0, 0, 0.8) - border-radius 4px - - > p - display block - margin 0 - - > [data-fa] - margin-right 0.25em - - </style> - <script> - import anime from 'animejs'; - - this.mixin('i'); - - this.mixin('stream'); - this.connection = this.stream.getConnection(); - this.connectionId = this.stream.use(); - - this.on('before-mount', () => { - if (this.connection.state == 'connected') { - this.root.style.opacity = 0; - } - - this.connection.on('_connected_', () => { - this.update(); - setTimeout(() => { - anime({ - targets: this.root, - opacity: 0, - easing: 'linear', - duration: 200 - }); - }, 1000); - }); - - this.connection.on('_closed_', () => { - this.update(); - anime({ - targets: this.root, - opacity: 1, - easing: 'linear', - duration: 100 - }); - }); - }); - - this.on('unmount', () => { - this.stream.dispose(this.connectionId); - }); - </script> -</mk-stream-indicator> diff --git a/src/web/app/common/tags/time.tag b/src/web/app/common/tags/time.tag deleted file mode 100644 index b0d7d24533..0000000000 --- a/src/web/app/common/tags/time.tag +++ /dev/null @@ -1,50 +0,0 @@ -<mk-time> - <time datetime={ opts.time }> - <span if={ mode == 'relative' }>{ relative }</span> - <span if={ mode == 'absolute' }>{ absolute }</span> - <span if={ mode == 'detail' }>{ absolute } ({ relative })</span> - </time> - <script> - this.time = new Date(this.opts.time); - this.mode = this.opts.mode || 'relative'; - this.tickid = null; - - this.absolute = - this.time.getFullYear() + '年' + - (this.time.getMonth() + 1) + '月' + - this.time.getDate() + '日' + - ' ' + - this.time.getHours() + '時' + - this.time.getMinutes() + '分'; - - this.on('mount', () => { - if (this.mode == 'relative' || this.mode == 'detail') { - this.tick(); - this.tickid = setInterval(this.tick, 1000); - } - }); - - this.on('unmount', () => { - if (this.mode === 'relative' || this.mode === 'detail') { - clearInterval(this.tickid); - } - }); - - this.tick = () => { - const now = new Date(); - const ago = (now - this.time) / 1000/*ms*/; - this.relative = - ago >= 31536000 ? '%i18n:common.time.years_ago%' .replace('{}', ~~(ago / 31536000)) : - ago >= 2592000 ? '%i18n:common.time.months_ago%' .replace('{}', ~~(ago / 2592000)) : - ago >= 604800 ? '%i18n:common.time.weeks_ago%' .replace('{}', ~~(ago / 604800)) : - ago >= 86400 ? '%i18n:common.time.days_ago%' .replace('{}', ~~(ago / 86400)) : - ago >= 3600 ? '%i18n:common.time.hours_ago%' .replace('{}', ~~(ago / 3600)) : - ago >= 60 ? '%i18n:common.time.minutes_ago%'.replace('{}', ~~(ago / 60)) : - ago >= 10 ? '%i18n:common.time.seconds_ago%'.replace('{}', ~~(ago % 60)) : - ago >= 0 ? '%i18n:common.time.just_now%' : - ago < 0 ? '%i18n:common.time.future%' : - '%i18n:common.time.unknown%'; - this.update(); - }; - </script> -</mk-time> diff --git a/src/web/app/common/tags/twitter-setting.tag b/src/web/app/common/tags/twitter-setting.tag deleted file mode 100644 index 3b70505ba2..0000000000 --- a/src/web/app/common/tags/twitter-setting.tag +++ /dev/null @@ -1,62 +0,0 @@ -<mk-twitter-setting> - <p>%i18n:common.tags.mk-twitter-setting.description%<a href={ _ABOUT_URL_ + '/link-to-twitter' } target="_blank">%i18n:common.tags.mk-twitter-setting.detail%</a></p> - <p class="account" if={ I.twitter } title={ 'Twitter ID: ' + I.twitter.user_id }>%i18n:common.tags.mk-twitter-setting.connected-to%: <a href={ 'https://twitter.com/' + I.twitter.screen_name } target="_blank">@{ I.twitter.screen_name }</a></p> - <p> - <a href={ _API_URL_ + '/connect/twitter' } target="_blank" onclick={ connect }>{ I.twitter ? '%i18n:common.tags.mk-twitter-setting.reconnect%' : '%i18n:common.tags.mk-twitter-setting.connect%' }</a> - <span if={ I.twitter }> or </span> - <a href={ _API_URL_ + '/disconnect/twitter' } target="_blank" if={ I.twitter } onclick={ disconnect }>%i18n:common.tags.mk-twitter-setting.disconnect%</a> - </p> - <p class="id" if={ I.twitter }>Twitter ID: { I.twitter.user_id }</p> - <style> - :scope - display block - color #4a535a - - .account - border solid 1px #e1e8ed - border-radius 4px - padding 16px - - a - font-weight bold - color inherit - - .id - color #8899a6 - </style> - <script> - this.mixin('i'); - - this.form = null; - - this.on('mount', () => { - this.I.on('updated', this.onMeUpdated); - }); - - this.on('unmount', () => { - this.I.off('updated', this.onMeUpdated); - }); - - this.onMeUpdated = () => { - if (this.I.twitter) { - if (this.form) this.form.close(); - } - }; - - this.connect = e => { - e.preventDefault(); - this.form = window.open(_API_URL_ + '/connect/twitter', - 'twitter_connect_window', - 'height=570,width=520'); - return false; - }; - - this.disconnect = e => { - e.preventDefault(); - window.open(_API_URL_ + '/disconnect/twitter', - 'twitter_disconnect_window', - 'height=570,width=520'); - return false; - }; - </script> -</mk-twitter-setting> diff --git a/src/web/app/common/tags/uploader.tag b/src/web/app/common/tags/uploader.tag deleted file mode 100644 index a95004b46d..0000000000 --- a/src/web/app/common/tags/uploader.tag +++ /dev/null @@ -1,199 +0,0 @@ -<mk-uploader> - <ol if={ uploads.length > 0 }> - <li each={ uploads }> - <div class="img" style="background-image: url({ img })"></div> - <p class="name">%fa:spinner .pulse%{ name }</p> - <p class="status"><span class="initing" if={ progress == undefined }>%i18n:common.tags.mk-uploader.waiting%<mk-ellipsis/></span><span class="kb" if={ progress != undefined }>{ String(Math.floor(progress.value / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }<i>KB</i> / { String(Math.floor(progress.max / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }<i>KB</i></span><span class="percentage" if={ progress != undefined }>{ Math.floor((progress.value / progress.max) * 100) }</span></p> - <progress if={ progress != undefined && progress.value != progress.max } value={ progress.value } max={ progress.max }></progress> - <div class="progress initing" if={ progress == undefined }></div> - <div class="progress waiting" if={ progress != undefined && progress.value == progress.max }></div> - </li> - </ol> - <style> - :scope - display block - overflow auto - - &:empty - display none - - > ol - display block - margin 0 - padding 0 - list-style none - - > li - display block - margin 8px 0 0 0 - padding 0 - height 36px - box-shadow 0 -1px 0 rgba($theme-color, 0.1) - border-top solid 8px transparent - - &:first-child - margin 0 - box-shadow none - border-top none - - > .img - display block - position absolute - top 0 - left 0 - width 36px - height 36px - background-size cover - background-position center center - - > .name - display block - position absolute - top 0 - left 44px - margin 0 - padding 0 - max-width 256px - font-size 0.8em - color rgba($theme-color, 0.7) - white-space nowrap - text-overflow ellipsis - overflow hidden - - > [data-fa] - margin-right 4px - - > .status - display block - position absolute - top 0 - right 0 - margin 0 - padding 0 - font-size 0.8em - - > .initing - color rgba($theme-color, 0.5) - - > .kb - color rgba($theme-color, 0.5) - - > .percentage - display inline-block - width 48px - text-align right - - color rgba($theme-color, 0.7) - - &:after - content '%' - - > progress - display block - position absolute - bottom 0 - right 0 - margin 0 - width calc(100% - 44px) - height 8px - background transparent - border none - border-radius 4px - overflow hidden - - &::-webkit-progress-value - background $theme-color - - &::-webkit-progress-bar - background rgba($theme-color, 0.1) - - > .progress - display block - position absolute - bottom 0 - right 0 - margin 0 - width calc(100% - 44px) - height 8px - border none - border-radius 4px - background linear-gradient( - 45deg, - lighten($theme-color, 30%) 25%, - $theme-color 25%, - $theme-color 50%, - lighten($theme-color, 30%) 50%, - lighten($theme-color, 30%) 75%, - $theme-color 75%, - $theme-color - ) - background-size 32px 32px - animation bg 1.5s linear infinite - - &.initing - opacity 0.3 - - @keyframes bg - from {background-position: 0 0;} - to {background-position: -64px 32px;} - - </style> - <script> - this.mixin('i'); - - this.uploads = []; - - this.upload = (file, folder) => { - if (folder && typeof folder == 'object') folder = folder.id; - - const id = Math.random(); - - const ctx = { - id: id, - name: file.name || 'untitled', - progress: undefined - }; - - this.uploads.push(ctx); - this.trigger('change-uploads', this.uploads); - this.update(); - - const reader = new FileReader(); - reader.onload = e => { - ctx.img = e.target.result; - this.update(); - }; - reader.readAsDataURL(file); - - const data = new FormData(); - data.append('i', this.I.token); - data.append('file', file); - - if (folder) data.append('folder_id', folder); - - const xhr = new XMLHttpRequest(); - xhr.open('POST', _API_URL_ + '/drive/files/create', true); - xhr.onload = e => { - const driveFile = JSON.parse(e.target.response); - - this.trigger('uploaded', driveFile); - - this.uploads = this.uploads.filter(x => x.id != id); - this.trigger('change-uploads', this.uploads); - - this.update(); - }; - - xhr.upload.onprogress = e => { - if (e.lengthComputable) { - if (ctx.progress == undefined) ctx.progress = {}; - ctx.progress.max = e.total; - ctx.progress.value = e.loaded; - this.update(); - } - }; - - xhr.send(data); - }; - </script> -</mk-uploader> diff --git a/src/web/app/common/tags/url-preview.tag b/src/web/app/common/tags/url-preview.tag deleted file mode 100644 index 7dbdd8fea2..0000000000 --- a/src/web/app/common/tags/url-preview.tag +++ /dev/null @@ -1,117 +0,0 @@ -<mk-url-preview> - <a href={ url } target="_blank" title={ url } if={ !loading }> - <div class="thumbnail" if={ thumbnail } style={ 'background-image: url(' + thumbnail + ')' }></div> - <article> - <header> - <h1>{ title }</h1> - </header> - <p>{ description }</p> - <footer> - <img class="icon" if={ icon } src={ icon }/> - <p>{ sitename }</p> - </footer> - </article> - </a> - <style> - :scope - display block - font-size 16px - - > a - display block - border solid 1px #eee - border-radius 4px - overflow hidden - - &:hover - text-decoration none - border-color #ddd - - > article > header > h1 - text-decoration underline - - > .thumbnail - position absolute - width 100px - height 100% - background-position center - background-size cover - - & + article - left 100px - width calc(100% - 100px) - - > article - padding 16px - - > header - margin-bottom 8px - - > h1 - margin 0 - font-size 1em - color #555 - - > p - margin 0 - color #777 - font-size 0.8em - - > footer - margin-top 8px - height 16px - - > img - display inline-block - width 16px - height 16px - margin-right 4px - vertical-align top - - > p - display inline-block - margin 0 - color #666 - font-size 0.8em - line-height 16px - vertical-align top - - @media (max-width 500px) - font-size 8px - - > a - border none - - > .thumbnail - width 70px - - & + article - left 70px - width calc(100% - 70px) - - > article - padding 8px - - </style> - <script> - this.mixin('api'); - - this.url = this.opts.url; - this.loading = true; - - this.on('mount', () => { - fetch('/api:url?url=' + this.url).then(res => { - res.json().then(info => { - this.title = info.title; - this.description = info.description; - this.thumbnail = info.thumbnail; - this.icon = info.icon; - this.sitename = info.sitename; - - this.loading = false; - this.update(); - }); - }); - }); - </script> -</mk-url-preview> diff --git a/src/web/app/common/tags/url.tag b/src/web/app/common/tags/url.tag deleted file mode 100644 index 2690afc5da..0000000000 --- a/src/web/app/common/tags/url.tag +++ /dev/null @@ -1,54 +0,0 @@ -<mk-url> - <a href={ url } target={ opts.target }> - <span class="schema">{ schema }//</span> - <span class="hostname">{ hostname }</span> - <span class="port" if={ port != '' }>:{ port }</span> - <span class="pathname" if={ pathname != '' }>{ pathname }</span> - <span class="query">{ query }</span> - <span class="hash">{ hash }</span> - %fa:external-link-square-alt% - </a> - <style> - :scope - word-break break-all - - > a - > [data-fa] - padding-left 2px - font-size .9em - font-weight 400 - font-style normal - - > .schema - opacity 0.5 - - > .hostname - font-weight bold - - > .pathname - opacity 0.8 - - > .query - opacity 0.5 - - > .hash - font-style italic - - </style> - <script> - this.url = this.opts.href; - - this.on('before-mount', () => { - const url = new URL(this.url); - - this.schema = url.protocol; - this.hostname = url.hostname; - this.port = url.port; - this.pathname = url.pathname; - this.query = url.search; - this.hash = url.hash; - - this.update(); - }); - </script> -</mk-url> diff --git a/src/web/app/desktop/assets/grid.svg b/src/web/app/desktop/assets/grid.svg deleted file mode 100644 index d1d72cd8ce..0000000000 --- a/src/web/app/desktop/assets/grid.svg +++ /dev/null @@ -1,150 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="32" - height="32" - viewBox="0 0 8.4666665 8.4666669" - version="1.1" - id="svg8" - inkscape:version="0.92.1 r15371" - sodipodi:docname="grid.svg"> - <defs - id="defs2" /> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0.0" - inkscape:pageshadow="2" - inkscape:zoom="22.4" - inkscape:cx="14.687499" - inkscape:cy="14.558219" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="true" - units="px" - showguides="true" - inkscape:window-width="1920" - inkscape:window-height="1017" - inkscape:window-x="-8" - inkscape:window-y="1072" - inkscape:window-maximized="1"> - <inkscape:grid - type="xygrid" - id="grid3680" - empspacing="8" - empcolor="#ff3fff" - empopacity="0.41176471" /> - </sodipodi:namedview> - <metadata - id="metadata5"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="レイヤー 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-288.53331)"> - <path - style="fill:#000000;fill-opacity:0.05;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 0,296.99998 v -8.46667 h 8.4666666 l 10e-8,0.26458 H 0.26458333 l 0,8.20209 z" - id="path3684" - inkscape:connector-curvature="0" - sodipodi:nodetypes="ccccccc" /> - <path - style="fill:#000000;fill-opacity:0.05;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 4.2333334,292.23748 h 0.2645833 v 0.52916 h 0.5291667 l 0,0.26459 H 4.4979167 v 0.52917 H 4.2333334 v -0.52917 H 3.7041667 l 0,-0.26459 h 0.5291667 z" - id="path4491" - inkscape:connector-curvature="0" - sodipodi:nodetypes="ccccccccccccc" /> - <path - style="fill:#000000;fill-opacity:0.05;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 3.4395833,292.76664 0,0.26459 H 2.38125 l 0,-0.26459 z" - id="path4493" - inkscape:connector-curvature="0" - sodipodi:nodetypes="ccccc" /> - <path - style="fill:#000000;fill-opacity:0.05;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 6.3499999,292.76664 10e-8,0.26459 H 5.2916667 l -1e-7,-0.26459 z" - id="path4493-2" - inkscape:connector-curvature="0" - sodipodi:nodetypes="ccccc" /> - <path - style="fill:#000000;fill-opacity:0.05;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 7.6729167,292.76664 v 0.26459 H 6.6145834 v -0.26459 z" - id="path4493-6" - inkscape:connector-curvature="0" - sodipodi:nodetypes="ccccc" /> - <path - style="fill:#000000;fill-opacity:0.05;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 2.1166666,292.76664 1e-7,0.26459 H 1.0583334 l -1e-7,-0.26459 z" - id="path4493-1" - inkscape:connector-curvature="0" - sodipodi:nodetypes="ccccc" /> - <path - style="fill:#000000;fill-opacity:0.05;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 4.2333333,291.97289 0.2645834,0 v -1.05833 l -0.2645834,0 z" - id="path4522" - inkscape:connector-curvature="0" - sodipodi:nodetypes="ccccc" /> - <path - style="fill:#000000;fill-opacity:0.05;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 4.2333334,290.64997 0.2645833,1e-5 v -1.05833 l -0.2645833,-1e-5 z" - id="path4522-7" - inkscape:connector-curvature="0" - sodipodi:nodetypes="ccccc" /> - <path - style="fill:#000000;fill-opacity:0.05;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 4.2333334,294.88331 h 0.2645833 v -1.05833 H 4.2333334 Z" - id="path4522-5" - inkscape:connector-curvature="0" - sodipodi:nodetypes="ccccc" /> - <path - style="fill:#000000;fill-opacity:0.05;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 4.2333333,296.20622 h 0.2645833 v -1.05833 H 4.2333333 Z" - id="path4522-74" - inkscape:connector-curvature="0" - sodipodi:nodetypes="ccccc" /> - <path - style="fill:#000000;fill-opacity:0.05;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 4.2333334,289.32706 0.2645834,10e-6 -10e-8,-0.52918 -0.2645834,-10e-6 z" - id="path4522-7-4" - inkscape:connector-curvature="0" - sodipodi:nodetypes="ccccc" /> - <path - style="fill:#000000;fill-opacity:0.05;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 4.2333332,296.99998 h 0.2645835 l 0,-0.52917 H 4.2333333 Z" - id="path4522-7-4-4" - inkscape:connector-curvature="0" - sodipodi:nodetypes="ccccc" /> - <path - style="fill:#000000;fill-opacity:0.05;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 0.79375,292.76664 -3e-8,0.26459 -0.52916667,0 3e-8,-0.26459 z" - id="path4493-1-7" - inkscape:connector-curvature="0" - sodipodi:nodetypes="ccccc" /> - <path - style="fill:#000000;fill-opacity:0.05;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 8.4666667,292.76664 v 0.26459 l -0.5291667,0 v -0.26459 z" - id="path4493-1-7-2" - inkscape:connector-curvature="0" - sodipodi:nodetypes="ccccc" /> - </g> -</svg> diff --git a/src/web/app/desktop/assets/header-logo-white.svg b/src/web/app/desktop/assets/header-logo-white.svg deleted file mode 100644 index 8082edb30d..0000000000 --- a/src/web/app/desktop/assets/header-logo-white.svg +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 16.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
- y="0px" width="256px" height="256px" viewBox="0 0 256 256" enable-background="new 0 0 256 256" xml:space="preserve">
-<circle fill="#FFFFFF" cx="128" cy="153.6" r="19.201"/>
-<circle fill="#FFFFFF" cx="51.2" cy="153.6" r="19.2"/>
-<circle fill="#FFFFFF" cx="204.8" cy="153.6" r="19.2"/>
-<polyline fill="none" stroke="#FFFFFF" stroke-width="16" stroke-linejoin="round" stroke-miterlimit="10" points="51.2,153.6
- 89.601,102.4 128,153.6 166.4,102.4 204.799,153.6 "/>
-<circle fill="#FFFFFF" cx="89.6" cy="102.4" r="19.2"/>
-<circle fill="#FFFFFF" cx="166.4" cy="102.4" r="19.199"/>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-</svg>
diff --git a/src/web/app/desktop/assets/header-logo.svg b/src/web/app/desktop/assets/header-logo.svg deleted file mode 100644 index 3a2207954a..0000000000 --- a/src/web/app/desktop/assets/header-logo.svg +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 16.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
- y="0px" width="256px" height="256px" viewBox="0 0 256 256" enable-background="new 0 0 256 256" xml:space="preserve">
-<circle cx="128" cy="153.6" r="19.201"/>
-<circle cx="51.2" cy="153.6" r="19.2"/>
-<circle cx="204.8" cy="153.6" r="19.2"/>
-<polyline fill="none" stroke="#000000" stroke-width="16" stroke-linejoin="round" stroke-miterlimit="10" points="51.2,153.6
- 89.601,102.4 128,153.6 166.4,102.4 204.799,153.6 "/>
-<circle cx="89.6" cy="102.4" r="19.2"/>
-<circle cx="166.4" cy="102.4" r="19.199"/>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-</svg>
diff --git a/src/web/app/desktop/assets/index.jpg b/src/web/app/desktop/assets/index.jpg Binary files differdeleted file mode 100644 index 10c412efe2..0000000000 --- a/src/web/app/desktop/assets/index.jpg +++ /dev/null diff --git a/src/web/app/desktop/assets/remove.png b/src/web/app/desktop/assets/remove.png Binary files differdeleted file mode 100644 index 8b1f4c06c9..0000000000 --- a/src/web/app/desktop/assets/remove.png +++ /dev/null diff --git a/src/web/app/desktop/mixins/index.ts b/src/web/app/desktop/mixins/index.ts deleted file mode 100644 index e0c94ec5ee..0000000000 --- a/src/web/app/desktop/mixins/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -require('./user-preview'); -require('./widget'); diff --git a/src/web/app/desktop/mixins/user-preview.ts b/src/web/app/desktop/mixins/user-preview.ts deleted file mode 100644 index 614de72bea..0000000000 --- a/src/web/app/desktop/mixins/user-preview.ts +++ /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 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.ts b/src/web/app/desktop/mixins/widget.ts deleted file mode 100644 index 04131cd8f0..0000000000 --- a/src/web/app/desktop/mixins/widget.ts +++ /dev/null @@ -1,31 +0,0 @@ -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.ts b/src/web/app/desktop/router.ts deleted file mode 100644 index 27b63ab2ef..0000000000 --- a/src/web/app/desktop/router.ts +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Desktop App Router - */ - -import * as riot from 'riot'; -import * as route from 'page'; -import MiOS from '../common/mios'; -let page = null; - -export default (mios: MiOS) => { - 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() { - mios.isSignedin ? 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.ts b/src/web/app/desktop/script.ts deleted file mode 100644 index b06cb180e1..0000000000 --- a/src/web/app/desktop/script.ts +++ /dev/null @@ -1,108 +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 MiOS from '../common/mios'; -import HomeStreamManager from '../common/scripts/streaming/home-stream-manager'; -import composeNotification from '../common/scripts/compose-notification'; - -/** - * init - */ -init(async (mios: MiOS) => { - /** - * 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(mios.stream); - } - } - - // Start routing - route(mios); -}, true); - -function registerNotifications(stream: HomeStreamManager) { - if (stream == null) return; - - if (stream.hasConnection) { - attach(stream.borrow()); - } - - stream.on('connected', connection => { - attach(connection); - }); - - function attach(connection) { - connection.on('drive_file_created', file => { - const _n = composeNotification('drive_file_created', file); - const n = new Notification(_n.title, { - body: _n.body, - icon: _n.icon - }); - setTimeout(n.close.bind(n), 5000); - }); - - connection.on('mention', post => { - const _n = composeNotification('mention', post); - const n = new Notification(_n.title, { - body: _n.body, - icon: _n.icon - }); - setTimeout(n.close.bind(n), 6000); - }); - - connection.on('reply', post => { - const _n = composeNotification('reply', post); - const n = new Notification(_n.title, { - body: _n.body, - icon: _n.icon - }); - setTimeout(n.close.bind(n), 6000); - }); - - connection.on('quote', post => { - const _n = composeNotification('quote', post); - const n = new Notification(_n.title, { - body: _n.body, - icon: _n.icon - }); - setTimeout(n.close.bind(n), 6000); - }); - - connection.on('unread_messaging_message', message => { - const _n = composeNotification('unread_messaging_message', message); - const n = new Notification(_n.title, { - body: _n.body, - icon: _n.icon - }); - 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.ts b/src/web/app/desktop/scripts/autocomplete.ts deleted file mode 100644 index 9df7aae08d..0000000000 --- a/src/web/app/desktop/scripts/autocomplete.ts +++ /dev/null @@ -1,132 +0,0 @@ -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.ts b/src/web/app/desktop/scripts/dialog.ts deleted file mode 100644 index 816ba4b5f5..0000000000 --- a/src/web/app/desktop/scripts/dialog.ts +++ /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 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.ts b/src/web/app/desktop/scripts/fuck-ad-block.ts deleted file mode 100644 index ddeb600b6e..0000000000 --- a/src/web/app/desktop/scripts/fuck-ad-block.ts +++ /dev/null @@ -1,20 +0,0 @@ -require('fuckadblock'); -import dialog from './dialog'; - -declare const fuckAdBlock: any; - -export default () => { - if (fuckAdBlock === undefined) { - adBlockDetected(); - } else { - fuckAdBlock.onDetected(adBlockDetected); - } -}; - -function adBlockDetected() { - dialog('%fa:exclamation-triangle%広告ブロッカーを無効にしてください', - '<strong>Misskeyは広告を掲載していません</strong>が、広告をブロックする機能が有効だと一部の機能が利用できなかったり、不具合が発生する場合があります。', - [{ - text: 'OK' - }]); -} diff --git a/src/web/app/desktop/scripts/input-dialog.ts b/src/web/app/desktop/scripts/input-dialog.ts deleted file mode 100644 index b06d011c6b..0000000000 --- a/src/web/app/desktop/scripts/input-dialog.ts +++ /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 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.ts b/src/web/app/desktop/scripts/not-implemented-exception.ts deleted file mode 100644 index b4660fa62f..0000000000 --- a/src/web/app/desktop/scripts/not-implemented-exception.ts +++ /dev/null @@ -1,8 +0,0 @@ -import dialog from './dialog'; - -export default () => { - dialog('%fa:exclamation-triangle%Not implemented yet', - '要求された操作は実装されていません。<br>→<a href="https://github.com/syuilo/misskey" target="_blank">Misskeyの開発に参加する</a>', [{ - text: 'OK' - }]); -}; diff --git a/src/web/app/desktop/scripts/notify.ts b/src/web/app/desktop/scripts/notify.ts deleted file mode 100644 index 2e6cbdeed8..0000000000 --- a/src/web/app/desktop/scripts/notify.ts +++ /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 as any).mount(notification, { - message: message - }); -}; diff --git a/src/web/app/desktop/scripts/password-dialog.ts b/src/web/app/desktop/scripts/password-dialog.ts deleted file mode 100644 index 39d7f3db7a..0000000000 --- a/src/web/app/desktop/scripts/password-dialog.ts +++ /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 as any).mount(dialog, { - title: title, - type: 'password', - onOk: onOk, - onCancel: onCancel - }); -}; diff --git a/src/web/app/desktop/scripts/scroll-follower.ts b/src/web/app/desktop/scripts/scroll-follower.ts deleted file mode 100644 index 05072958ce..0000000000 --- a/src/web/app/desktop/scripts/scroll-follower.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** - * 要素をスクロールに追従させる - */ -export default class ScrollFollower { - private follower: Element; - private containerTop: number; - private topPadding: number; - - constructor(follower: Element, topPadding: number) { - //#region - this.follow = this.follow.bind(this); - //#endregion - - this.follower = follower; - this.containerTop = follower.getBoundingClientRect().top; - this.topPadding = topPadding; - - window.addEventListener('scroll', this.follow); - window.addEventListener('resize', this.follow); - } - - /** - * 追従解除 - */ - public dispose() { - window.removeEventListener('scroll', this.follow); - window.removeEventListener('resize', this.follow); - } - - private follow() { - const windowBottom = window.scrollY + window.innerHeight; - const windowTop = window.scrollY + this.topPadding; - - const rect = this.follower.getBoundingClientRect(); - const followerBottom = (rect.top + window.scrollY) + rect.height; - const screenHeight = window.innerHeight - this.topPadding; - - // スクロールの上部(+余白)がフォロワーコンテナの上部よりも上方にある - if (window.scrollY + this.topPadding < this.containerTop) { - // フォロワーをコンテナの最上部に合わせる - (this.follower.parentNode as any).style.marginTop = '0px'; - return; - } - - // スクロールの下部がフォロワーの下部よりも下方にある かつ 表示領域の縦幅がフォロワーの縦幅よりも狭い - if (windowBottom > followerBottom && rect.height > screenHeight) { - // フォロワーの下部をスクロール下部に合わせる - const top = (windowBottom - rect.height) - this.containerTop; - (this.follower.parentNode as any).style.marginTop = `${top}px`; - return; - } - - // スクロールの上部(+余白)がフォロワーの上部よりも上方にある または 表示領域の縦幅がフォロワーの縦幅よりも広い - if (windowTop < rect.top + window.scrollY || rect.height < screenHeight) { - // フォロワーの上部をスクロール上部(+余白)に合わせる - const top = windowTop - this.containerTop; - (this.follower.parentNode as any).style.marginTop = `${top}px`; - return; - } - } -} diff --git a/src/web/app/desktop/scripts/update-avatar.ts b/src/web/app/desktop/scripts/update-avatar.ts deleted file mode 100644 index fea5db80bb..0000000000 --- a/src/web/app/desktop/scripts/update-avatar.ts +++ /dev/null @@ -1,88 +0,0 @@ -declare const _API_URL_: string; - -import * as riot from 'riot'; -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', _API_URL_ + '/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('%fa:info-circle%アバターを更新しました', - '新しいアバターが反映されるまで時間がかかる場合があります。', - [{ - 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: '%fa:image%アバターにする画像を選択' - })[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 deleted file mode 100644 index 325775622d..0000000000 --- a/src/web/app/desktop/scripts/update-banner.ts +++ /dev/null @@ -1,88 +0,0 @@ -declare const _API_URL_: string; - -import * as riot from 'riot'; -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', _API_URL_ + '/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('%fa:info-circle%バナーを更新しました', - '新しいバナーが反映されるまで時間がかかる場合があります。', - [{ - 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: '%fa:image%バナーにする画像を選択' - })[0]; - - browser.one('selected', file => { - fileSelected(file); - }); - } -}; diff --git a/src/web/app/desktop/style.styl b/src/web/app/desktop/style.styl deleted file mode 100644 index c893e2ed67..0000000000 --- a/src/web/app/desktop/style.styl +++ /dev/null @@ -1,51 +0,0 @@ -@import "../app" -@import "../reset" -@import "../../../../node_modules/cropperjs/dist/cropper.css" - -@import "./ui" - -*::input-placeholder - color #D8CBC5 - -* - &:focus - outline none - - &::scrollbar - width 5px - background transparent - - &:horizontal - height 5px - - &::scrollbar-button - width 0 - height 0 - background rgba(0, 0, 0, 0.2) - - &::scrollbar-piece - background transparent - - &:start - background transparent - - &::scrollbar-thumb - background rgba(0, 0, 0, 0.2) - - &:hover - background rgba(0, 0, 0, 0.4) - - &:active - background $theme-color - - &::scrollbar-corner - background rgba(0, 0, 0, 0.2) - -html - background #f7f7f7 - - // ↓ workaround of https://github.com/riot/riot/issues/2134 - &[data-page='entrance'] - #wait - right auto - left 15px diff --git a/src/web/app/desktop/tags/analog-clock.tag b/src/web/app/desktop/tags/analog-clock.tag deleted file mode 100644 index c0489d3feb..0000000000 --- a/src/web/app/desktop/tags/analog-clock.tag +++ /dev/null @@ -1,95 +0,0 @@ -<mk-analog-clock> - <canvas ref="canvas" width="256" height="256"></canvas> - <style> - :scope - > canvas - display block - width 256px - height 256px - </style> - <script> - const Vec2 = function(x, y) { - this.x = x; - this.y = y; - }; - - this.on('mount', () => { - this.draw() - this.clock = setInterval(this.draw, 1000); - }); - - this.on('unmount', () => { - clearInterval(this.clock); - }); - - this.draw = () => { - const now = new Date(); - const s = now.getSeconds(); - const m = now.getMinutes(); - const h = now.getHours(); - - const ctx = this.refs.canvas.getContext('2d'); - const canvW = this.refs.canvas.width; - const canvH = this.refs.canvas.height; - ctx.clearRect(0, 0, canvW, canvH); - - { // 背景 - const center = Math.min((canvW / 2), (canvH / 2)); - const lineStart = center * 0.90; - const shortLineEnd = center * 0.87; - const longLineEnd = center * 0.84; - for (let i = 0; i < 60; i++) { - const angle = Math.PI * i / 30; - const uv = new Vec2(Math.sin(angle), -Math.cos(angle)); - ctx.beginPath(); - ctx.lineWidth = 1; - ctx.moveTo((canvW / 2) + uv.x * lineStart, (canvH / 2) + uv.y * lineStart); - if (i % 5 == 0) { - ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)'; - ctx.lineTo((canvW / 2) + uv.x * longLineEnd, (canvH / 2) + uv.y * longLineEnd); - } else { - ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)'; - ctx.lineTo((canvW / 2) + uv.x * shortLineEnd, (canvH / 2) + uv.y * shortLineEnd); - } - ctx.stroke(); - } - } - - { // 分 - const angle = Math.PI * (m + s / 60) / 30; - const length = Math.min(canvW, canvH) / 2.6; - const uv = new Vec2(Math.sin(angle), -Math.cos(angle)); - ctx.beginPath(); - ctx.strokeStyle = '#ffffff'; - ctx.lineWidth = 2; - ctx.moveTo(canvW / 2 - uv.x * length / 5, canvH / 2 - uv.y * length / 5); - ctx.lineTo(canvW / 2 + uv.x * length, canvH / 2 + uv.y * length); - ctx.stroke(); - } - - { // 時 - const angle = Math.PI * (h % 12 + m / 60) / 6; - const length = Math.min(canvW, canvH) / 4; - const uv = new Vec2(Math.sin(angle), -Math.cos(angle)); - ctx.beginPath(); - ctx.strokeStyle = _THEME_COLOR_; - ctx.lineWidth = 2; - ctx.moveTo(canvW / 2 - uv.x * length / 5, canvH / 2 - uv.y * length / 5); - ctx.lineTo(canvW / 2 + uv.x * length, canvH / 2 + uv.y * length); - ctx.stroke(); - } - - { // 秒 - const angle = Math.PI * s / 30; - const length = Math.min(canvW, canvH) / 2.6; - const uv = new Vec2(Math.sin(angle), -Math.cos(angle)); - ctx.beginPath(); - ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)'; - ctx.lineWidth = 1; - ctx.moveTo(canvW / 2 - uv.x * length / 5, canvH / 2 - uv.y * length / 5); - ctx.lineTo(canvW / 2 + uv.x * length, canvH / 2 + uv.y * length); - ctx.stroke(); - } - }; - </script> -</mk-analog-clock> diff --git a/src/web/app/desktop/tags/autocomplete-suggestion.tag b/src/web/app/desktop/tags/autocomplete-suggestion.tag deleted file mode 100644 index 7311606694..0000000000 --- a/src/web/app/desktop/tags/autocomplete-suggestion.tag +++ /dev/null @@ -1,197 +0,0 @@ -<mk-autocomplete-suggestion> - <ol class="users" ref="users" if={ users.length > 0 }> - <li each={ users } onclick={ parent.onClick } onkeydown={ parent.onKeydown } tabindex="-1"> - <img class="avatar" src={ avatar_url + '?thumbnail&size=32' } alt=""/> - <span class="name">{ name }</span> - <span class="username">@{ username }</span> - </li> - </ol> - <style> - :scope - display block - position absolute - z-index 65535 - margin-top calc(1em + 8px) - overflow hidden - background #fff - border solid 1px rgba(0, 0, 0, 0.1) - border-radius 4px - - > .users - display block - margin 0 - padding 4px 0 - max-height 190px - max-width 500px - overflow auto - list-style none - - > li - display block - padding 4px 12px - white-space nowrap - overflow hidden - font-size 0.9em - color rgba(0, 0, 0, 0.8) - cursor default - - &, * - user-select none - - &:hover - &[data-selected='true'] - color #fff - background $theme-color - - .name - color #fff - - .username - color #fff - - &:active - color #fff - background darken($theme-color, 10%) - - .name - color #fff - - .username - color #fff - - .avatar - vertical-align middle - min-width 28px - min-height 28px - max-width 28px - max-height 28px - margin 0 8px 0 0 - border-radius 100% - - .name - margin 0 8px 0 0 - /*font-weight bold*/ - font-weight normal - color rgba(0, 0, 0, 0.8) - - .username - font-weight normal - color rgba(0, 0, 0, 0.3) - - </style> - <script> - import contains from '../../common/scripts/contains'; - - this.mixin('api'); - - this.q = this.opts.q; - this.textarea = this.opts.textarea; - this.fetching = true; - this.users = []; - this.select = -1; - - this.on('mount', () => { - this.textarea.addEventListener('keydown', this.onKeydown); - - document.querySelectorAll('body *').forEach(el => { - el.addEventListener('mousedown', this.mousedown); - }); - - this.api('users/search_by_username', { - query: this.q, - limit: 30 - }).then(users => { - this.update({ - fetching: false, - users: users - }); - }); - }); - - this.on('unmount', () => { - this.textarea.removeEventListener('keydown', this.onKeydown); - - document.querySelectorAll('body *').forEach(el => { - el.removeEventListener('mousedown', this.mousedown); - }); - }); - - this.mousedown = e => { - if (!contains(this.root, e.target) && (this.root != e.target)) this.close(); - }; - - this.onClick = e => { - this.complete(e.item); - }; - - this.onKeydown = e => { - const cancel = () => { - e.preventDefault(); - e.stopPropagation(); - }; - - switch (e.which) { - case 10: // [ENTER] - case 13: // [ENTER] - if (this.select !== -1) { - cancel(); - this.complete(this.users[this.select]); - } else { - this.close(); - } - break; - - case 27: // [ESC] - cancel(); - this.close(); - break; - - case 38: // [↑] - if (this.select !== -1) { - cancel(); - this.selectPrev(); - } else { - this.close(); - } - break; - - case 9: // [TAB] - case 40: // [↓] - cancel(); - this.selectNext(); - break; - - default: - this.close(); - } - }; - - this.selectNext = () => { - if (++this.select >= this.users.length) this.select = 0; - this.applySelect(); - }; - - this.selectPrev = () => { - if (--this.select < 0) this.select = this.users.length - 1; - this.applySelect(); - }; - - this.applySelect = () => { - Array.from(this.refs.users.children).forEach(el => { - el.removeAttribute('data-selected'); - }); - - this.refs.users.children[this.select].setAttribute('data-selected', 'true'); - this.refs.users.children[this.select].focus(); - }; - - this.complete = user => { - this.opts.complete(user); - }; - - this.close = () => { - this.opts.close(); - }; - - </script> -</mk-autocomplete-suggestion> diff --git a/src/web/app/desktop/tags/big-follow-button.tag b/src/web/app/desktop/tags/big-follow-button.tag deleted file mode 100644 index 7634043b20..0000000000 --- a/src/web/app/desktop/tags/big-follow-button.tag +++ /dev/null @@ -1,153 +0,0 @@ -<mk-big-follow-button> - <button class={ wait: wait, follow: !user.is_following, unfollow: user.is_following } if={ !init } onclick={ onclick } disabled={ wait } title={ user.is_following ? 'フォロー解除' : 'フォローする' }> - <span if={ !wait && user.is_following }>%fa:minus%フォロー解除</span> - <span if={ !wait && !user.is_following }>%fa:plus%フォロー</span> - <virtual if={ wait }>%fa:spinner .pulse .fw%</virtual> - </button> - <div class="init" if={ init }>%fa:spinner .pulse .fw%</div> - <style> - :scope - display block - - > button - > .init - display block - cursor pointer - padding 0 - margin 0 - width 100% - line-height 38px - font-size 1em - outline none - border-radius 4px - - * - pointer-events none - - i - margin-right 8px - - &:focus - &:after - content "" - pointer-events none - position absolute - top -5px - right -5px - bottom -5px - left -5px - border 2px solid rgba($theme-color, 0.3) - border-radius 8px - - &.follow - color #888 - background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%) - border solid 1px #e2e2e2 - - &:hover - background linear-gradient(to bottom, #f9f9f9 0%, #ececec 100%) - border-color #dcdcdc - - &:active - background #ececec - border-color #dcdcdc - - &.unfollow - color $theme-color-foreground - background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%) - border solid 1px lighten($theme-color, 15%) - - &:not(:disabled) - font-weight bold - - &:hover:not(:disabled) - background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%) - border-color $theme-color - - &:active:not(:disabled) - background $theme-color - border-color $theme-color - - &.wait - cursor wait !important - opacity 0.7 - - </style> - <script> - import isPromise from '../../common/scripts/is-promise'; - - this.mixin('i'); - this.mixin('api'); - - this.mixin('stream'); - this.connection = this.stream.getConnection(); - this.connectionId = this.stream.use(); - - this.user = null; - this.userPromise = isPromise(this.opts.user) - ? this.opts.user - : Promise.resolve(this.opts.user); - this.init = true; - this.wait = false; - - this.on('mount', () => { - this.userPromise.then(user => { - this.update({ - init: false, - user: user - }); - this.connection.on('follow', this.onStreamFollow); - this.connection.on('unfollow', this.onStreamUnfollow); - }); - }); - - this.on('unmount', () => { - this.connection.off('follow', this.onStreamFollow); - this.connection.off('unfollow', this.onStreamUnfollow); - this.stream.dispose(this.connectionId); - }); - - this.onStreamFollow = user => { - if (user.id == this.user.id) { - this.update({ - user: user - }); - } - }; - - this.onStreamUnfollow = user => { - if (user.id == this.user.id) { - this.update({ - user: user - }); - } - }; - - this.onclick = () => { - this.wait = true; - if (this.user.is_following) { - this.api('following/delete', { - user_id: this.user.id - }).then(() => { - this.user.is_following = false; - }).catch(err => { - console.error(err); - }).then(() => { - this.wait = false; - this.update(); - }); - } else { - this.api('following/create', { - user_id: this.user.id - }).then(() => { - this.user.is_following = true; - }).catch(err => { - console.error(err); - }).then(() => { - this.wait = false; - this.update(); - }); - } - }; - </script> -</mk-big-follow-button> diff --git a/src/web/app/desktop/tags/contextmenu.tag b/src/web/app/desktop/tags/contextmenu.tag deleted file mode 100644 index 2a3b2a7726..0000000000 --- a/src/web/app/desktop/tags/contextmenu.tag +++ /dev/null @@ -1,138 +0,0 @@ -<mk-contextmenu> - <yield /> - <style> - :scope - $width = 240px - $item-height = 38px - $padding = 10px - - display none - position fixed - top 0 - left 0 - z-index 4096 - width $width - font-size 0.8em - background #fff - border-radius 0 4px 4px 4px - box-shadow 2px 2px 8px rgba(0, 0, 0, 0.2) - opacity 0 - - ul - display block - margin 0 - padding $padding 0 - list-style none - - li - display block - - &.separator - margin-top $padding - padding-top $padding - border-top solid 1px #eee - - &.has-child - > p - cursor default - - > [data-fa]:last-child - position absolute - top 0 - right 8px - line-height $item-height - - &:hover > ul - visibility visible - - &:active - > p, a - background $theme-color - - > p, a - display block - z-index 1 - margin 0 - padding 0 32px 0 38px - line-height $item-height - color #868C8C - text-decoration none - cursor pointer - - &:hover - text-decoration none - - * - pointer-events none - - > i - width 28px - margin-left -28px - text-align center - - &:hover - > p, a - text-decoration none - background $theme-color - color $theme-color-foreground - - &:active - > p, a - text-decoration none - background darken($theme-color, 10%) - color $theme-color-foreground - - li > ul - visibility hidden - position absolute - top 0 - left $width - margin-top -($padding) - width $width - background #fff - border-radius 0 4px 4px 4px - box-shadow 2px 2px 8px rgba(0, 0, 0, 0.2) - transition visibility 0s linear 0.2s - - </style> - <script> - import anime from 'animejs'; - import contains from '../../common/scripts/contains'; - - this.root.addEventListener('contextmenu', e => { - e.preventDefault(); - }); - - this.mousedown = e => { - e.preventDefault(); - if (!contains(this.root, e.target) && (this.root != e.target)) this.close(); - return false; - }; - - this.open = pos => { - document.querySelectorAll('body *').forEach(el => { - el.addEventListener('mousedown', this.mousedown); - }); - - this.root.style.display = 'block'; - this.root.style.left = pos.x + 'px'; - this.root.style.top = pos.y + 'px'; - - anime({ - targets: this.root, - opacity: [0, 1], - duration: 100, - easing: 'linear' - }); - }; - - this.close = () => { - document.querySelectorAll('body *').forEach(el => { - el.removeEventListener('mousedown', this.mousedown); - }); - - this.trigger('closed'); - this.unmount(); - }; - </script> -</mk-contextmenu> diff --git a/src/web/app/desktop/tags/crop-window.tag b/src/web/app/desktop/tags/crop-window.tag deleted file mode 100644 index 4845b669d2..0000000000 --- a/src/web/app/desktop/tags/crop-window.tag +++ /dev/null @@ -1,196 +0,0 @@ -<mk-crop-window> - <mk-window ref="window" is-modal={ true } width={ '800px' }> - <yield to="header">%fa:crop%{ parent.title }</yield> - <yield to="content"> - <div class="body"><img ref="img" src={ parent.image.url + '?thumbnail&quality=80' } alt=""/></div> - <div class="action"> - <button class="skip" onclick={ parent.skip }>クロップをスキップ</button> - <button class="cancel" onclick={ parent.cancel }>キャンセル</button> - <button class="ok" onclick={ parent.ok }>決定</button> - </div> - </yield> - </mk-window> - <style> - :scope - display block - - > mk-window - [data-yield='header'] - > [data-fa] - margin-right 4px - - [data-yield='content'] - - > .body - > img - width 100% - max-height 400px - - .cropper-modal { - opacity: 0.8; - } - - .cropper-view-box { - outline-color: $theme-color; - } - - .cropper-line, .cropper-point { - background-color: $theme-color; - } - - .cropper-bg { - animation: cropper-bg 0.5s linear infinite; - } - - @-webkit-keyframes cropper-bg { - 0% { - background-position: 0 0; - } - - 100% { - background-position: -8px -8px; - } - } - - @-moz-keyframes cropper-bg { - 0% { - background-position: 0 0; - } - - 100% { - background-position: -8px -8px; - } - } - - @-ms-keyframes cropper-bg { - 0% { - background-position: 0 0; - } - - 100% { - background-position: -8px -8px; - } - } - - @keyframes cropper-bg { - 0% { - background-position: 0 0; - } - - 100% { - background-position: -8px -8px; - } - } - - > .action - height 72px - background lighten($theme-color, 95%) - - .ok - .cancel - .skip - display block - position absolute - bottom 16px - cursor pointer - padding 0 - margin 0 - height 40px - font-size 1em - outline none - border-radius 4px - - &:focus - &:after - content "" - pointer-events none - position absolute - top -5px - right -5px - bottom -5px - left -5px - border 2px solid rgba($theme-color, 0.3) - border-radius 8px - - &:disabled - opacity 0.7 - cursor default - - .ok - .cancel - width 120px - - .ok - right 16px - color $theme-color-foreground - background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%) - border solid 1px lighten($theme-color, 15%) - - &:not(:disabled) - font-weight bold - - &:hover:not(:disabled) - background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%) - border-color $theme-color - - &:active:not(:disabled) - background $theme-color - border-color $theme-color - - .cancel - .skip - color #888 - background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%) - border solid 1px #e2e2e2 - - &:hover - background linear-gradient(to bottom, #f9f9f9 0%, #ececec 100%) - border-color #dcdcdc - - &:active - background #ececec - border-color #dcdcdc - - .cancel - right 148px - - .skip - left 16px - width 150px - - </style> - <script> - const Cropper = require('cropperjs'); - - this.image = this.opts.file; - this.title = this.opts.title; - this.aspectRatio = this.opts.aspectRatio; - this.cropper = null; - - this.on('mount', () => { - this.img = this.refs.window.refs.img; - this.cropper = new Cropper(this.img, { - aspectRatio: this.aspectRatio, - highlight: false, - viewMode: 1 - }); - }); - - this.ok = () => { - this.cropper.getCroppedCanvas().toBlob(blob => { - this.trigger('cropped', blob); - this.refs.window.close(); - }); - }; - - this.skip = () => { - this.trigger('skipped'); - this.refs.window.close(); - }; - - this.cancel = () => { - this.trigger('canceled'); - this.refs.window.close(); - }; - </script> -</mk-crop-window> diff --git a/src/web/app/desktop/tags/detailed-post-window.tag b/src/web/app/desktop/tags/detailed-post-window.tag deleted file mode 100644 index 04f9acf974..0000000000 --- a/src/web/app/desktop/tags/detailed-post-window.tag +++ /dev/null @@ -1,80 +0,0 @@ -<mk-detailed-post-window> - <div class="bg" ref="bg" onclick={ bgClick }></div> - <div class="main" ref="main" if={ !fetching }> - <mk-post-detail ref="detail" post={ post }/> - </div> - <style> - :scope - display block - opacity 0 - - > .bg - display block - position fixed - z-index 1000 - top 0 - left 0 - width 100% - height 100% - background rgba(0, 0, 0, 0.7) - - > .main - display block - position fixed - z-index 1000 - top 20% - left 0 - right 0 - margin 0 auto 0 auto - padding 0 - width 638px - text-align center - - > mk-post-detail - margin 0 auto - - </style> - <script> - import anime from 'animejs'; - - this.mixin('api'); - - this.fetching = true; - this.post = null; - - this.on('mount', () => { - anime({ - targets: this.root, - opacity: 1, - duration: 100, - easing: 'linear' - }); - - this.api('posts/show', { - post_id: this.opts.post - }).then(post => { - - this.update({ - fetching: false, - post: post - }); - }); - }); - - this.close = () => { - this.refs.bg.style.pointerEvents = 'none'; - this.refs.main.style.pointerEvents = 'none'; - anime({ - targets: this.root, - opacity: 0, - duration: 300, - easing: 'linear', - complete: () => this.unmount() - }); - }; - - this.bgClick = () => { - this.close(); - }; - </script> -</mk-detailed-post-window> diff --git a/src/web/app/desktop/tags/dialog.tag b/src/web/app/desktop/tags/dialog.tag deleted file mode 100644 index 743fd63942..0000000000 --- a/src/web/app/desktop/tags/dialog.tag +++ /dev/null @@ -1,144 +0,0 @@ -<mk-dialog> - <div class="bg" ref="bg" onclick={ bgClick }></div> - <div class="main" ref="main"> - <header ref="header"></header> - <div class="body" ref="body"></div> - <div class="buttons"> - <virtual each={ opts.buttons }> - <button onclick={ _onclick }>{ text }</button> - </virtual> - </div> - </div> - <style> - :scope - display block - - > .bg - display block - position fixed - z-index 8192 - top 0 - left 0 - width 100% - height 100% - background rgba(0, 0, 0, 0.7) - opacity 0 - pointer-events none - - > .main - display block - position fixed - z-index 8192 - top 20% - left 0 - right 0 - margin 0 auto 0 auto - padding 32px 42px - width 480px - background #fff - opacity 0 - - > header - margin 1em 0 - color $theme-color - // color #43A4EC - font-weight bold - - &:empty - display none - - > i - margin-right 0.5em - - > .body - margin 1em 0 - color #888 - - > .buttons - > button - display inline-block - float right - margin 0 - padding 10px 10px - font-size 1.1em - font-weight normal - text-decoration none - color #888 - background transparent - outline none - border none - border-radius 0 - cursor pointer - transition color 0.1s ease - - i - margin 0 0.375em - - &:hover - color $theme-color - - &:active - color darken($theme-color, 10%) - transition color 0s ease - - </style> - <script> - import anime from 'animejs'; - - this.canThrough = opts.canThrough != null ? opts.canThrough : true; - this.opts.buttons.forEach(button => { - button._onclick = () => { - if (button.onclick) button.onclick(); - this.close(); - }; - }); - - this.on('mount', () => { - this.refs.header.innerHTML = this.opts.title; - this.refs.body.innerHTML = this.opts.text; - - this.refs.bg.style.pointerEvents = 'auto'; - anime({ - targets: this.refs.bg, - opacity: 1, - duration: 100, - easing: 'linear' - }); - - anime({ - targets: this.refs.main, - opacity: 1, - scale: [1.2, 1], - duration: 300, - easing: [ 0, 0.5, 0.5, 1 ] - }); - }); - - this.close = () => { - this.refs.bg.style.pointerEvents = 'none'; - anime({ - targets: this.refs.bg, - opacity: 0, - duration: 300, - easing: 'linear' - }); - - this.refs.main.style.pointerEvents = 'none'; - anime({ - targets: this.refs.main, - opacity: 0, - scale: 0.8, - duration: 300, - easing: [ 0.5, -0.5, 1, 0.5 ], - complete: () => this.unmount() - }); - }; - - this.bgClick = () => { - if (this.canThrough) { - if (this.opts.onThrough) this.opts.onThrough(); - this.close(); - } - }; - </script> -</mk-dialog> diff --git a/src/web/app/desktop/tags/donation.tag b/src/web/app/desktop/tags/donation.tag deleted file mode 100644 index 1c19fac1f5..0000000000 --- a/src/web/app/desktop/tags/donation.tag +++ /dev/null @@ -1,66 +0,0 @@ -<mk-donation> - <button class="close" onclick={ close }>閉じる x</button> - <div class="message"> - <p>利用者の皆さま、</p> - <p> - 今日は、日本の皆さまにお知らせがあります。 - Misskeyの援助をお願いいたします。 - 私は独立性を守るため、一切の広告を掲載いたしません。 - 平均で約¥1,500の寄付をいただき、運営しております。 - 援助をしてくださる利用者はほんの少数です。 - お願いいたします。 - 今日、利用者の皆さまが¥300ご援助くだされば、募金活動を一時間で終了することができます。 - コーヒー1杯ほどの金額です。 - Misskeyを活用しておられるのでしたら、広告を掲載せずにもう1年活動できるよう、どうか1分だけお時間をください。 - 私は小さな非営利個人ですが、サーバー、プログラム、人件費など、世界でトップクラスのウェブサイト同等のコストがかかります。 - 利用者は何億人といますが、他の大きなサイトに比べてほんの少額の費用で運営しているのです。 - 人間の可能性、自由、そして機会。知識こそ、これらの基盤を成すものです。 - 私は、誰もが無料かつ制限なく知識に触れられるべきだと信じています。 - 募金活動を終了し、Misskeyの改善に戻れるようご援助ください。 - よろしくお願いいたします。 - </p> - </div> - <style> - :scope - display block - color #fff - background #03072C - - > .close - position absolute - top 16px - right 16px - z-index 1 - - > .message - padding 32px - font-size 1.4em - font-family serif - - > p - display block - margin 0 auto - max-width 1200px - - > p:first-child - margin-bottom 16px - - </style> - <script> - this.mixin('i'); - this.mixin('api'); - - this.close = e => { - e.preventDefault(); - e.stopPropagation(); - - this.I.client_settings.show_donation = false; - this.I.update(); - this.api('i/update', { - show_donation: false - }); - - this.unmount(); - }; - </script> -</mk-donation> diff --git a/src/web/app/desktop/tags/drive/base-contextmenu.tag b/src/web/app/desktop/tags/drive/base-contextmenu.tag deleted file mode 100644 index b16dbf55d6..0000000000 --- a/src/web/app/desktop/tags/drive/base-contextmenu.tag +++ /dev/null @@ -1,44 +0,0 @@ -<mk-drive-browser-base-contextmenu> - <mk-contextmenu ref="ctx"> - <ul> - <li onclick={ parent.createFolder }> - <p>%fa:R folder%%i18n:desktop.tags.mk-drive-browser-base-contextmenu.create-folder%</p> - </li> - <li onclick={ parent.upload }> - <p>%fa:upload%%i18n:desktop.tags.mk-drive-browser-base-contextmenu.upload%</p> - </li> - <li onclick={ parent.urlUpload }> - <p>%fa:cloud-upload-alt%%i18n:desktop.tags.mk-drive-browser-base-contextmenu.url-upload%</p> - </li> - </ul> - </mk-contextmenu> - <script> - this.browser = this.opts.browser; - - this.on('mount', () => { - this.refs.ctx.on('closed', () => { - this.trigger('closed'); - this.unmount(); - }); - }); - - this.open = pos => { - this.refs.ctx.open(pos); - }; - - this.createFolder = () => { - this.browser.createFolder(); - this.refs.ctx.close(); - }; - - this.upload = () => { - this.browser.selectLocalFile(); - this.refs.ctx.close(); - }; - - this.urlUpload = () => { - this.browser.urlUpload(); - this.refs.ctx.close(); - }; - </script> -</mk-drive-browser-base-contextmenu> diff --git a/src/web/app/desktop/tags/drive/browser-window.tag b/src/web/app/desktop/tags/drive/browser-window.tag deleted file mode 100644 index 57042f0163..0000000000 --- a/src/web/app/desktop/tags/drive/browser-window.tag +++ /dev/null @@ -1,60 +0,0 @@ -<mk-drive-browser-window> - <mk-window ref="window" is-modal={ false } width={ '800px' } height={ '500px' } popout={ popout }> - <yield to="header"> - <p class="info" if={ parent.usage }><b>{ parent.usage.toFixed(1) }%</b> %i18n:desktop.tags.mk-drive-browser-window.used%</p> - %fa:cloud%%i18n:desktop.tags.mk-drive-browser-window.drive% - </yield> - <yield to="content"> - <mk-drive-browser multiple={ true } folder={ parent.folder } ref="browser"/> - </yield> - </mk-window> - <style> - :scope - > mk-window - [data-yield='header'] - > .info - position absolute - top 0 - left 16px - margin 0 - font-size 80% - - > [data-fa] - margin-right 4px - - [data-yield='content'] - > mk-drive-browser - height 100% - - </style> - <script> - this.mixin('api'); - - this.folder = this.opts.folder ? this.opts.folder : null; - - this.popout = () => { - const folder = this.refs.window.refs.browser.folder; - if (folder) { - return `${_URL_}/i/drive/folder/${folder.id}`; - } else { - return `${_URL_}/i/drive`; - } - }; - - this.on('mount', () => { - this.refs.window.on('closed', () => { - this.unmount(); - }); - - this.api('drive').then(info => { - this.update({ - usage: info.usage / info.capacity * 100 - }); - }); - }); - - this.close = () => { - this.refs.window.close(); - }; - </script> -</mk-drive-browser-window> diff --git a/src/web/app/desktop/tags/drive/browser.tag b/src/web/app/desktop/tags/drive/browser.tag deleted file mode 100644 index 901daabfd8..0000000000 --- a/src/web/app/desktop/tags/drive/browser.tag +++ /dev/null @@ -1,734 +0,0 @@ -<mk-drive-browser> - <nav> - <div class="path" oncontextmenu={ pathOncontextmenu }> - <mk-drive-browser-nav-folder class={ current: folder == null } folder={ null }/> - <virtual each={ folder in hierarchyFolders }> - <span class="separator">%fa:angle-right%</span> - <mk-drive-browser-nav-folder folder={ folder }/> - </virtual> - <span class="separator" if={ folder != null }>%fa:angle-right%</span> - <span class="folder current" if={ folder != null }>{ folder.name }</span> - </div> - <input class="search" type="search" placeholder=" %i18n:desktop.tags.mk-drive-browser.search%"/> - </nav> - <div class="main { uploading: uploads.length > 0, fetching: fetching }" ref="main" onmousedown={ onmousedown } ondragover={ ondragover } ondragenter={ ondragenter } ondragleave={ ondragleave } ondrop={ ondrop } oncontextmenu={ oncontextmenu }> - <div class="selection" ref="selection"></div> - <div class="contents" ref="contents"> - <div class="folders" ref="foldersContainer" if={ folders.length > 0 }> - <virtual each={ folder in folders }> - <mk-drive-browser-folder class="folder" folder={ folder }/> - </virtual> - <div class="padding" each={ folders }></div> - <button if={ moreFolders }>%i18n:desktop.tags.mk-drive-browser.load-more%</button> - </div> - <div class="files" ref="filesContainer" if={ files.length > 0 }> - <virtual each={ file in files }> - <mk-drive-browser-file class="file" file={ file }/> - </virtual> - <div class="padding" each={ files }></div> - <button if={ moreFiles } onclick={ fetchMoreFiles }>%i18n:desktop.tags.mk-drive-browser.load-more%</button> - </div> - <div class="empty" if={ files.length == 0 && folders.length == 0 && !fetching }> - <p if={ draghover }>%i18n:desktop.tags.mk-drive-browser.empty-draghover%</p> - <p if={ !draghover && folder == null }><strong>%i18n:desktop.tags.mk-drive-browser.empty-drive%</strong><br/>%i18n:desktop.tags.mk-drive-browser.empty-drive-description%</p> - <p if={ !draghover && folder != null }>%i18n:desktop.tags.mk-drive-browser.empty-folder%</p> - </div> - </div> - <div class="fetching" if={ fetching }> - <div class="spinner"> - <div class="dot1"></div> - <div class="dot2"></div> - </div> - </div> - </div> - <div class="dropzone" if={ draghover }></div> - <mk-uploader ref="uploader"/> - <input ref="fileInput" type="file" accept="*/*" multiple="multiple" tabindex="-1" onchange={ changeFileInput }/> - <style> - :scope - display block - - > nav - display block - z-index 2 - width 100% - overflow auto - font-size 0.9em - color #555 - background #fff - //border-bottom 1px solid #dfdfdf - box-shadow 0 1px 0 rgba(0, 0, 0, 0.05) - - &, * - user-select none - - > .path - display inline-block - vertical-align bottom - margin 0 - padding 0 8px - width calc(100% - 200px) - line-height 38px - white-space nowrap - - > * - display inline-block - margin 0 - padding 0 8px - line-height 38px - cursor pointer - - i - margin-right 4px - - * - pointer-events none - - &:hover - text-decoration underline - - &.current - font-weight bold - cursor default - - &:hover - text-decoration none - - &.separator - margin 0 - padding 0 - opacity 0.5 - cursor default - - > [data-fa] - margin 0 - - > .search - display inline-block - vertical-align bottom - user-select text - cursor auto - margin 0 - padding 0 18px - width 200px - font-size 1em - line-height 38px - background transparent - outline none - //border solid 1px #ddd - border none - border-radius 0 - box-shadow none - transition color 0.5s ease, border 0.5s ease - font-family FontAwesome, sans-serif - - &[data-active='true'] - background #fff - - &::-webkit-input-placeholder, - &:-ms-input-placeholder, - &:-moz-placeholder - color $ui-control-foreground-color - - > .main - padding 8px - height calc(100% - 38px) - overflow auto - - &, * - user-select none - - &.fetching - cursor wait !important - - * - pointer-events none - - > .contents - opacity 0.5 - - &.uploading - height calc(100% - 38px - 100px) - - > .selection - display none - position absolute - z-index 128 - top 0 - left 0 - border solid 1px $theme-color - background rgba($theme-color, 0.5) - pointer-events none - - > .contents - - > .folders - > .files - display flex - flex-wrap wrap - - > .folder - > .file - flex-grow 1 - width 144px - margin 4px - - > .padding - flex-grow 1 - pointer-events none - width 144px + 8px // 8px is margin - - > .empty - padding 16px - text-align center - color #999 - pointer-events none - - > p - margin 0 - - > .fetching - .spinner - margin 100px auto - width 40px - height 40px - text-align center - - animation sk-rotate 2.0s infinite linear - - .dot1, .dot2 - width 60% - height 60% - display inline-block - position absolute - top 0 - background-color rgba(0, 0, 0, 0.3) - border-radius 100% - - animation sk-bounce 2.0s infinite ease-in-out - - .dot2 - top auto - bottom 0 - animation-delay -1.0s - - @keyframes sk-rotate { 100% { transform: rotate(360deg); }} - - @keyframes sk-bounce { - 0%, 100% { - transform: scale(0.0); - } 50% { - transform: scale(1.0); - } - } - - > .dropzone - position absolute - left 0 - top 38px - width 100% - height calc(100% - 38px) - border dashed 2px rgba($theme-color, 0.5) - pointer-events none - - > mk-uploader - height 100px - padding 16px - background #fff - - > input - display none - - </style> - <script> - import contains from '../../../common/scripts/contains'; - import dialog from '../../scripts/dialog'; - import inputDialog from '../../scripts/input-dialog'; - - this.mixin('i'); - this.mixin('api'); - - this.mixin('drive-stream'); - this.connection = this.driveStream.getConnection(); - this.connectionId = this.driveStream.use(); - - this.files = []; - this.folders = []; - this.hierarchyFolders = []; - this.selectedFiles = []; - - this.uploads = []; - - // 現在の階層(フォルダ) - // * null でルートを表す - this.folder = null; - - this.multiple = this.opts.multiple != null ? this.opts.multiple : false; - - // ドロップされようとしているか - this.draghover = false; - - // 自信の所有するアイテムがドラッグをスタートさせたか - // (自分自身の階層にドロップできないようにするためのフラグ) - this.isDragSource = false; - - this.on('mount', () => { - this.refs.uploader.on('uploaded', file => { - this.addFile(file, true); - }); - - this.refs.uploader.on('change-uploads', uploads => { - this.update({ - uploads: uploads - }); - }); - - this.connection.on('file_created', this.onStreamDriveFileCreated); - this.connection.on('file_updated', this.onStreamDriveFileUpdated); - this.connection.on('folder_created', this.onStreamDriveFolderCreated); - this.connection.on('folder_updated', this.onStreamDriveFolderUpdated); - - if (this.opts.folder) { - this.move(this.opts.folder); - } else { - this.fetch(); - } - }); - - this.on('unmount', () => { - this.connection.off('file_created', this.onStreamDriveFileCreated); - this.connection.off('file_updated', this.onStreamDriveFileUpdated); - this.connection.off('folder_created', this.onStreamDriveFolderCreated); - this.connection.off('folder_updated', this.onStreamDriveFolderUpdated); - this.driveStream.dispose(this.connectionId); - }); - - this.onStreamDriveFileCreated = file => { - this.addFile(file, true); - }; - - this.onStreamDriveFileUpdated = file => { - const current = this.folder ? this.folder.id : null; - if (current != file.folder_id) { - this.removeFile(file); - } else { - this.addFile(file, true); - } - }; - - this.onStreamDriveFolderCreated = folder => { - this.addFolder(folder, true); - }; - - this.onStreamDriveFolderUpdated = folder => { - const current = this.folder ? this.folder.id : null; - if (current != folder.parent_id) { - this.removeFolder(folder); - } else { - this.addFolder(folder, true); - } - }; - - this.onmousedown = e => { - if (contains(this.refs.foldersContainer, e.target) || contains(this.refs.filesContainer, e.target)) return true; - - const rect = this.refs.main.getBoundingClientRect(); - - const left = e.pageX + this.refs.main.scrollLeft - rect.left - window.pageXOffset - const top = e.pageY + this.refs.main.scrollTop - rect.top - window.pageYOffset - - const move = e => { - this.refs.selection.style.display = 'block'; - - const cursorX = e.pageX + this.refs.main.scrollLeft - rect.left - window.pageXOffset; - const cursorY = e.pageY + this.refs.main.scrollTop - rect.top - window.pageYOffset; - const w = cursorX - left; - const h = cursorY - top; - - if (w > 0) { - this.refs.selection.style.width = w + 'px'; - this.refs.selection.style.left = left + 'px'; - } else { - this.refs.selection.style.width = -w + 'px'; - this.refs.selection.style.left = cursorX + 'px'; - } - - if (h > 0) { - this.refs.selection.style.height = h + 'px'; - this.refs.selection.style.top = top + 'px'; - } else { - this.refs.selection.style.height = -h + 'px'; - this.refs.selection.style.top = cursorY + 'px'; - } - }; - - const up = e => { - document.documentElement.removeEventListener('mousemove', move); - document.documentElement.removeEventListener('mouseup', up); - - this.refs.selection.style.display = 'none'; - }; - - document.documentElement.addEventListener('mousemove', move); - document.documentElement.addEventListener('mouseup', up); - }; - - this.pathOncontextmenu = e => { - e.preventDefault(); - e.stopImmediatePropagation(); - return false; - }; - - this.ondragover = e => { - e.preventDefault(); - e.stopPropagation(); - - // ドラッグ元が自分自身の所有するアイテムかどうか - if (!this.isDragSource) { - // ドラッグされてきたものがファイルだったら - e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move'; - this.draghover = true; - } else { - // 自分自身にはドロップさせない - e.dataTransfer.dropEffect = 'none'; - return false; - } - }; - - this.ondragenter = e => { - e.preventDefault(); - if (!this.isDragSource) this.draghover = true; - }; - - this.ondragleave = e => { - this.draghover = false; - }; - - this.ondrop = e => { - e.preventDefault(); - e.stopPropagation(); - - this.draghover = false; - - // ドロップされてきたものがファイルだったら - if (e.dataTransfer.files.length > 0) { - Array.from(e.dataTransfer.files).forEach(file => { - this.upload(file, this.folder); - }); - return false; - } - - // データ取得 - const data = e.dataTransfer.getData('text'); - if (data == null) return false; - - // パース - // TODO: JSONじゃなかったら中断 - const obj = JSON.parse(data); - - // (ドライブの)ファイルだったら - if (obj.type == 'file') { - const file = obj.id; - if (this.files.some(f => f.id == file)) return false; - this.removeFile(file); - this.api('drive/files/update', { - file_id: file, - folder_id: this.folder ? this.folder.id : null - }); - // (ドライブの)フォルダーだったら - } else if (obj.type == 'folder') { - const folder = obj.id; - // 移動先が自分自身ならreject - if (this.folder && folder == this.folder.id) return false; - if (this.folders.some(f => f.id == folder)) return false; - this.removeFolder(folder); - this.api('drive/folders/update', { - folder_id: folder, - parent_id: this.folder ? this.folder.id : null - }).then(() => { - // something - }).catch(err => { - switch (err) { - case 'detected-circular-definition': - dialog('%fa:exclamation-triangle%%i18n:desktop.tags.mk-drive-browser.unable-to-process%', - '%i18n:desktop.tags.mk-drive-browser.circular-reference-detected%', [{ - text: '%i18n:common.ok%' - }]); - break; - default: - alert('%i18n:desktop.tags.mk-drive-browser.unhandled-error% ' + err); - } - }); - } - - return false; - }; - - this.oncontextmenu = e => { - e.preventDefault(); - e.stopImmediatePropagation(); - - const ctx = riot.mount(document.body.appendChild(document.createElement('mk-drive-browser-base-contextmenu')), { - browser: this - })[0]; - ctx.open({ - x: e.pageX - window.pageXOffset, - y: e.pageY - window.pageYOffset - }); - - return false; - }; - - this.selectLocalFile = () => { - this.refs.fileInput.click(); - }; - - this.urlUpload = () => { - inputDialog('%i18n:desktop.tags.mk-drive-browser.url-upload%', - '%i18n:desktop.tags.mk-drive-browser.url-of-file%', null, url => { - - this.api('drive/files/upload_from_url', { - url: url, - folder_id: this.folder ? this.folder.id : undefined - }); - - dialog('%fa:check%%i18n:desktop.tags.mk-drive-browser.url-upload-requested%', - '%i18n:desktop.tags.mk-drive-browser.may-take-time%', [{ - text: '%i18n:common.ok%' - }]); - }); - }; - - this.createFolder = () => { - inputDialog('%i18n:desktop.tags.mk-drive-browser.create-folder%', - '%i18n:desktop.tags.mk-drive-browser.folder-name%', null, name => { - - this.api('drive/folders/create', { - name: name, - folder_id: this.folder ? this.folder.id : undefined - }).then(folder => { - this.addFolder(folder, true); - this.update(); - }); - }); - }; - - this.changeFileInput = () => { - Array.from(this.refs.fileInput.files).forEach(file => { - this.upload(file, this.folder); - }); - }; - - this.upload = (file, folder) => { - if (folder && typeof folder == 'object') folder = folder.id; - this.refs.uploader.upload(file, folder); - }; - - this.chooseFile = file => { - const isAlreadySelected = this.selectedFiles.some(f => f.id == file.id); - if (this.multiple) { - if (isAlreadySelected) { - this.selectedFiles = this.selectedFiles.filter(f => f.id != file.id); - } else { - this.selectedFiles.push(file); - } - this.update(); - this.trigger('change-selection', this.selectedFiles); - } else { - if (isAlreadySelected) { - this.trigger('selected', file); - } else { - this.selectedFiles = [file]; - this.trigger('change-selection', [file]); - } - } - }; - - this.newWindow = folderId => { - riot.mount(document.body.appendChild(document.createElement('mk-drive-browser-window')), { - folder: folderId - }); - }; - - this.move = target => { - if (target == null) { - this.goRoot(); - return; - } else if (typeof target == 'object') { - target = target.id; - } - - this.update({ - fetching: true - }); - - this.api('drive/folders/show', { - folder_id: target - }).then(folder => { - this.folder = folder; - this.hierarchyFolders = []; - - const dive = folder => { - this.hierarchyFolders.unshift(folder); - if (folder.parent) dive(folder.parent); - }; - - if (folder.parent) dive(folder.parent); - - this.update(); - this.trigger('open-folder', folder); - this.fetch(); - }); - }; - - this.addFolder = (folder, unshift = false) => { - const current = this.folder ? this.folder.id : null; - if (current != folder.parent_id) return; - - if (this.folders.some(f => f.id == folder.id)) { - const exist = this.folders.map(f => f.id).indexOf(folder.id); - this.folders[exist] = folder; - this.update(); - return; - } - - if (unshift) { - this.folders.unshift(folder); - } else { - this.folders.push(folder); - } - - this.update(); - }; - - this.addFile = (file, unshift = false) => { - const current = this.folder ? this.folder.id : null; - if (current != file.folder_id) return; - - if (this.files.some(f => f.id == file.id)) { - const exist = this.files.map(f => f.id).indexOf(file.id); - this.files[exist] = file; - this.update(); - return; - } - - if (unshift) { - this.files.unshift(file); - } else { - this.files.push(file); - } - - this.update(); - }; - - this.removeFolder = folder => { - if (typeof folder == 'object') folder = folder.id; - this.folders = this.folders.filter(f => f.id != folder); - this.update(); - }; - - this.removeFile = file => { - if (typeof file == 'object') file = file.id; - this.files = this.files.filter(f => f.id != file); - this.update(); - }; - - this.appendFile = file => this.addFile(file); - this.appendFolder = file => this.addFolder(file); - this.prependFile = file => this.addFile(file, true); - this.prependFolder = file => this.addFolder(file, true); - - this.goRoot = () => { - // 既にrootにいるなら何もしない - if (this.folder == null) return; - - this.update({ - folder: null, - hierarchyFolders: [] - }); - this.trigger('move-root'); - this.fetch(); - }; - - this.fetch = () => { - this.update({ - folders: [], - files: [], - moreFolders: false, - moreFiles: false, - fetching: true - }); - - let fetchedFolders = null; - let fetchedFiles = null; - - const foldersMax = 30; - const filesMax = 30; - - // フォルダ一覧取得 - this.api('drive/folders', { - folder_id: this.folder ? this.folder.id : null, - limit: foldersMax + 1 - }).then(folders => { - if (folders.length == foldersMax + 1) { - this.moreFolders = true; - folders.pop(); - } - fetchedFolders = folders; - complete(); - }); - - // ファイル一覧取得 - this.api('drive/files', { - folder_id: this.folder ? this.folder.id : null, - limit: filesMax + 1 - }).then(files => { - if (files.length == filesMax + 1) { - this.moreFiles = true; - files.pop(); - } - fetchedFiles = files; - complete(); - }); - - let flag = false; - const complete = () => { - if (flag) { - fetchedFolders.forEach(this.appendFolder); - fetchedFiles.forEach(this.appendFile); - this.update({ - fetching: false - }); - } else { - flag = true; - } - }; - }; - - this.fetchMoreFiles = () => { - this.update({ - fetching: true - }); - - const max = 30; - - // ファイル一覧取得 - this.api('drive/files', { - folder_id: this.folder ? this.folder.id : null, - limit: max + 1 - }).then(files => { - if (files.length == max + 1) { - this.moreFiles = true; - files.pop(); - } else { - this.moreFiles = false; - } - files.forEach(this.appendFile); - this.update({ - fetching: false - }); - }); - }; - - </script> -</mk-drive-browser> diff --git a/src/web/app/desktop/tags/drive/file-contextmenu.tag b/src/web/app/desktop/tags/drive/file-contextmenu.tag deleted file mode 100644 index 532417c757..0000000000 --- a/src/web/app/desktop/tags/drive/file-contextmenu.tag +++ /dev/null @@ -1,99 +0,0 @@ -<mk-drive-browser-file-contextmenu> - <mk-contextmenu ref="ctx"> - <ul> - <li onclick={ parent.rename }> - <p>%fa:i-cursor%%i18n:desktop.tags.mk-drive-browser-file-contextmenu.rename%</p> - </li> - <li onclick={ parent.copyUrl }> - <p>%fa:link%%i18n:desktop.tags.mk-drive-browser-file-contextmenu.copy-url%</p> - </li> - <li><a href={ parent.file.url + '?download' } download={ parent.file.name } onclick={ parent.download }>%fa:download%%i18n:desktop.tags.mk-drive-browser-file-contextmenu.download%</a></li> - <li class="separator"></li> - <li onclick={ parent.delete }> - <p>%fa:R trash-alt%%i18n:common.delete%</p> - </li> - <li class="separator"></li> - <li class="has-child"> - <p>%i18n:desktop.tags.mk-drive-browser-file-contextmenu.else-files%%fa:caret-right%</p> - <ul> - <li onclick={ parent.setAvatar }> - <p>%i18n:desktop.tags.mk-drive-browser-file-contextmenu.set-as-avatar%</p> - </li> - <li onclick={ parent.setBanner }> - <p>%i18n:desktop.tags.mk-drive-browser-file-contextmenu.set-as-banner%</p> - </li> - </ul> - </li> - <li class="has-child"> - <p>%i18n:desktop.tags.mk-drive-browser-file-contextmenu.open-in-app%...%fa:caret-right%</p> - <ul> - <li onclick={ parent.addApp }> - <p>%i18n:desktop.tags.mk-drive-browser-file-contextmenu.add-app%...</p> - </li> - </ul> - </li> - </ul> - </mk-contextmenu> - <script> - import copyToClipboard from '../../../common/scripts/copy-to-clipboard'; - import dialog from '../../scripts/dialog'; - import inputDialog from '../../scripts/input-dialog'; - import updateAvatar from '../../scripts/update-avatar'; - import NotImplementedException from '../../scripts/not-implemented-exception'; - - this.mixin('i'); - this.mixin('api'); - - this.browser = this.opts.browser; - this.file = this.opts.file; - - this.on('mount', () => { - this.refs.ctx.on('closed', () => { - this.trigger('closed'); - this.unmount(); - }); - }); - - this.open = pos => { - this.refs.ctx.open(pos); - }; - - this.rename = () => { - this.refs.ctx.close(); - - inputDialog('%i18n:desktop.tags.mk-drive-browser-file-contextmenu.rename-file%', '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.input-new-file-name%', this.file.name, name => { - this.api('drive/files/update', { - file_id: this.file.id, - name: name - }) - }); - }; - - this.copyUrl = () => { - copyToClipboard(this.file.url); - this.refs.ctx.close(); - dialog('%fa:check%%i18n:desktop.tags.mk-drive-browser-file-contextmenu.copied%', - '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.copied-url-to-clipboard%', [{ - text: '%i18n:common.ok%' - }]); - }; - - this.download = () => { - this.refs.ctx.close(); - }; - - this.setAvatar = () => { - this.refs.ctx.close(); - updateAvatar(this.I, null, this.file); - }; - - this.setBanner = () => { - this.refs.ctx.close(); - updateBanner(this.I, null, this.file); - }; - - this.addApp = () => { - NotImplementedException(); - }; - </script> -</mk-drive-browser-file-contextmenu> diff --git a/src/web/app/desktop/tags/drive/file.tag b/src/web/app/desktop/tags/drive/file.tag deleted file mode 100644 index 0f019d95bf..0000000000 --- a/src/web/app/desktop/tags/drive/file.tag +++ /dev/null @@ -1,203 +0,0 @@ -<mk-drive-browser-file data-is-selected={ isSelected } data-is-contextmenu-showing={ isContextmenuShowing.toString() } onclick={ onclick } oncontextmenu={ oncontextmenu } draggable="true" ondragstart={ ondragstart } ondragend={ ondragend } title={ title }> - <div class="label" if={ I.avatar_id == file.id }><img src="/assets/label.svg"/> - <p>%i18n:desktop.tags.mk-drive-browser-file.avatar%</p> - </div> - <div class="label" if={ I.banner_id == file.id }><img src="/assets/label.svg"/> - <p>%i18n:desktop.tags.mk-drive-browser-file.banner%</p> - </div> - <div class="thumbnail"><img src={ file.url + '?thumbnail&size=128' } alt=""/></div> - <p class="name"><span>{ file.name.lastIndexOf('.') != -1 ? file.name.substr(0, file.name.lastIndexOf('.')) : file.name }</span><span class="ext" if={ file.name.lastIndexOf('.') != -1 }>{ file.name.substr(file.name.lastIndexOf('.')) }</span></p> - <style> - :scope - display block - padding 8px 0 0 0 - height 180px - border-radius 4px - - &, * - cursor pointer - - &:hover - background rgba(0, 0, 0, 0.05) - - > .label - &:before - &:after - background #0b65a5 - - &:active - background rgba(0, 0, 0, 0.1) - - > .label - &:before - &:after - background #0b588c - - &[data-is-selected] - background $theme-color - - &:hover - background lighten($theme-color, 10%) - - &:active - background darken($theme-color, 10%) - - > .label - &:before - &:after - display none - - > .name - color $theme-color-foreground - - &[data-is-contextmenu-showing='true'] - &:after - content "" - pointer-events none - position absolute - top -4px - right -4px - bottom -4px - left -4px - border 2px dashed rgba($theme-color, 0.3) - border-radius 4px - - > .label - position absolute - top 0 - left 0 - pointer-events none - - &:before - content "" - display block - position absolute - z-index 1 - top 0 - left 57px - width 28px - height 8px - background #0c7ac9 - - &:after - content "" - display block - position absolute - z-index 1 - top 57px - left 0 - width 8px - height 28px - background #0c7ac9 - - > img - position absolute - z-index 2 - top 0 - left 0 - - > p - position absolute - z-index 3 - top 19px - left -28px - width 120px - margin 0 - text-align center - line-height 28px - color #fff - transform rotate(-45deg) - - > .thumbnail - width 128px - height 128px - margin auto - - > img - display block - position absolute - top 0 - left 0 - right 0 - bottom 0 - margin auto - max-width 128px - max-height 128px - pointer-events none - - > .name - display block - margin 4px 0 0 0 - font-size 0.8em - text-align center - word-break break-all - color #444 - overflow hidden - - > .ext - opacity 0.5 - - </style> - <script> - import bytesToSize from '../../../common/scripts/bytes-to-size'; - - this.mixin('i'); - - this.file = this.opts.file; - this.browser = this.parent; - this.title = `${this.file.name}\n${this.file.type} ${bytesToSize(this.file.datasize)}`; - this.isContextmenuShowing = false; - this.isSelected = this.browser.selectedFiles.some(f => f.id == this.file.id); - - this.browser.on('change-selection', selections => { - this.isSelected = selections.some(f => f.id == this.file.id); - this.update(); - }); - - this.onclick = () => { - this.browser.chooseFile(this.file); - }; - - this.oncontextmenu = e => { - e.preventDefault(); - e.stopImmediatePropagation(); - - this.update({ - isContextmenuShowing: true - }); - const ctx = riot.mount(document.body.appendChild(document.createElement('mk-drive-browser-file-contextmenu')), { - browser: this.browser, - file: this.file - })[0]; - ctx.open({ - x: e.pageX - window.pageXOffset, - y: e.pageY - window.pageYOffset - }); - ctx.on('closed', () => { - this.update({ - isContextmenuShowing: false - }); - }); - return false; - }; - - this.ondragstart = e => { - e.dataTransfer.effectAllowed = 'move'; - e.dataTransfer.setData('text', JSON.stringify({ - type: 'file', - id: this.file.id, - file: this.file - })); - this.isDragging = true; - - // 親ブラウザに対して、ドラッグが開始されたフラグを立てる - // (=あなたの子供が、ドラッグを開始しましたよ) - this.browser.isDragSource = true; - }; - - this.ondragend = e => { - this.isDragging = false; - this.browser.isDragSource = false; - }; - </script> -</mk-drive-browser-file> diff --git a/src/web/app/desktop/tags/drive/folder-contextmenu.tag b/src/web/app/desktop/tags/drive/folder-contextmenu.tag deleted file mode 100644 index c6a1ea3b84..0000000000 --- a/src/web/app/desktop/tags/drive/folder-contextmenu.tag +++ /dev/null @@ -1,63 +0,0 @@ -<mk-drive-browser-folder-contextmenu> - <mk-contextmenu ref="ctx"> - <ul> - <li onclick={ parent.move }> - <p>%fa:arrow-right%%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.move-to-this-folder%</p> - </li> - <li onclick={ parent.newWindow }> - <p>%fa:R window-restore%%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.show-in-new-window%</p> - </li> - <li class="separator"></li> - <li onclick={ parent.rename }> - <p>%fa:i-cursor%%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.rename%</p> - </li> - <li class="separator"></li> - <li onclick={ parent.delete }> - <p>%fa:R trash-alt%%i18n:common.delete%</p> - </li> - </ul> - </mk-contextmenu> - <script> - import inputDialog from '../../scripts/input-dialog'; - - this.mixin('api'); - - this.browser = this.opts.browser; - this.folder = this.opts.folder; - - this.open = pos => { - this.refs.ctx.open(pos); - - this.refs.ctx.on('closed', () => { - this.trigger('closed'); - this.unmount(); - }); - }; - - this.move = () => { - this.browser.move(this.folder.id); - this.refs.ctx.close(); - }; - - this.newWindow = () => { - this.browser.newWindow(this.folder.id); - this.refs.ctx.close(); - }; - - this.createFolder = () => { - this.browser.createFolder(); - this.refs.ctx.close(); - }; - - this.rename = () => { - this.refs.ctx.close(); - - inputDialog('%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.rename-folder%', '%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.input-new-folder-name%', this.folder.name, name => { - this.api('drive/folders/update', { - folder_id: this.folder.id, - name: name - }); - }); - }; - </script> -</mk-drive-browser-folder-contextmenu> diff --git a/src/web/app/desktop/tags/drive/folder.tag b/src/web/app/desktop/tags/drive/folder.tag deleted file mode 100644 index 0b7ee6e2d1..0000000000 --- a/src/web/app/desktop/tags/drive/folder.tag +++ /dev/null @@ -1,202 +0,0 @@ -<mk-drive-browser-folder data-is-contextmenu-showing={ isContextmenuShowing.toString() } data-draghover={ draghover.toString() } onclick={ onclick } onmouseover={ onmouseover } onmouseout={ onmouseout } ondragover={ ondragover } ondragenter={ ondragenter } ondragleave={ ondragleave } ondrop={ ondrop } oncontextmenu={ oncontextmenu } draggable="true" ondragstart={ ondragstart } ondragend={ ondragend } title={ title }> - <p class="name"><virtual if={ hover }>%fa:R folder-open .fw%</virtual><virtual if={ !hover }>%fa:R folder .fw%</virtual>{ folder.name }</p> - <style> - :scope - display block - padding 8px - height 64px - background lighten($theme-color, 95%) - border-radius 4px - - &, * - cursor pointer - - * - pointer-events none - - &:hover - background lighten($theme-color, 90%) - - &:active - background lighten($theme-color, 85%) - - &[data-is-contextmenu-showing='true'] - &[data-draghover='true'] - &:after - content "" - pointer-events none - position absolute - top -4px - right -4px - bottom -4px - left -4px - border 2px dashed rgba($theme-color, 0.3) - border-radius 4px - - &[data-draghover='true'] - background lighten($theme-color, 90%) - - > .name - margin 0 - font-size 0.9em - color darken($theme-color, 30%) - - > [data-fa] - margin-right 4px - margin-left 2px - text-align left - - </style> - <script> - import dialog from '../../scripts/dialog'; - - this.mixin('api'); - - this.folder = this.opts.folder; - this.browser = this.parent; - - this.title = this.folder.name; - this.hover = false; - this.draghover = false; - this.isContextmenuShowing = false; - - this.onclick = () => { - this.browser.move(this.folder); - }; - - this.onmouseover = () => { - this.hover = true; - }; - - this.onmouseout = () => { - this.hover = false - }; - - this.ondragover = e => { - e.preventDefault(); - e.stopPropagation(); - - // 自分自身がドラッグされていない場合 - if (!this.isDragging) { - // ドラッグされてきたものがファイルだったら - if (e.dataTransfer.effectAllowed === 'all') { - e.dataTransfer.dropEffect = 'copy'; - } else { - e.dataTransfer.dropEffect = 'move'; - } - } else { - // 自分自身にはドロップさせない - e.dataTransfer.dropEffect = 'none'; - } - return false; - }; - - this.ondragenter = e => { - e.preventDefault(); - if (!this.isDragging) this.draghover = true; - }; - - this.ondragleave = () => { - this.draghover = false; - }; - - this.ondrop = e => { - e.preventDefault(); - e.stopPropagation(); - this.draghover = false; - - // ファイルだったら - if (e.dataTransfer.files.length > 0) { - Array.from(e.dataTransfer.files).forEach(file => { - this.browser.upload(file, this.folder); - }); - return false; - }; - - // データ取得 - const data = e.dataTransfer.getData('text'); - if (data == null) return false; - - // パース - // TODO: Validate JSON - const obj = JSON.parse(data); - - // (ドライブの)ファイルだったら - if (obj.type == 'file') { - const file = obj.id; - this.browser.removeFile(file); - this.api('drive/files/update', { - file_id: file, - folder_id: this.folder.id - }); - // (ドライブの)フォルダーだったら - } else if (obj.type == 'folder') { - const folder = obj.id; - // 移動先が自分自身ならreject - if (folder == this.folder.id) return false; - this.browser.removeFolder(folder); - this.api('drive/folders/update', { - folder_id: folder, - parent_id: this.folder.id - }).then(() => { - // something - }).catch(err => { - switch (err) { - case 'detected-circular-definition': - dialog('%fa:exclamation-triangle%%i18n:desktop.tags.mk-drive-browser-folder.unable-to-process%', - '%i18n:desktop.tags.mk-drive-browser-folder.circular-reference-detected%', [{ - text: '%i18n:common.ok%' - }]); - break; - default: - alert('%i18n:desktop.tags.mk-drive-browser-folder.unhandled-error% ' + err); - } - }); - } - - return false; - }; - - this.ondragstart = e => { - e.dataTransfer.effectAllowed = 'move'; - e.dataTransfer.setData('text', JSON.stringify({ - type: 'folder', - id: this.folder.id - })); - this.isDragging = true; - - // 親ブラウザに対して、ドラッグが開始されたフラグを立てる - // (=あなたの子供が、ドラッグを開始しましたよ) - this.browser.isDragSource = true; - }; - - this.ondragend = e => { - this.isDragging = false; - this.browser.isDragSource = false; - }; - - this.oncontextmenu = e => { - e.preventDefault(); - e.stopImmediatePropagation(); - - this.update({ - isContextmenuShowing: true - }); - const ctx = riot.mount(document.body.appendChild(document.createElement('mk-drive-browser-folder-contextmenu')), { - browser: this.browser, - folder: this.folder - })[0]; - ctx.open({ - x: e.pageX - window.pageXOffset, - y: e.pageY - window.pageYOffset - }); - ctx.on('closed', () => { - this.update({ - isContextmenuShowing: false - }); - }); - - return false; - }; - </script> -</mk-drive-browser-folder> diff --git a/src/web/app/desktop/tags/drive/nav-folder.tag b/src/web/app/desktop/tags/drive/nav-folder.tag deleted file mode 100644 index 43a648b52b..0000000000 --- a/src/web/app/desktop/tags/drive/nav-folder.tag +++ /dev/null @@ -1,96 +0,0 @@ -<mk-drive-browser-nav-folder data-draghover={ draghover } onclick={ onclick } ondragover={ ondragover } ondragenter={ ondragenter } ondragleave={ ondragleave } ondrop={ ondrop }> - <virtual if={ folder == null }>%fa:cloud%</virtual><span>{ folder == null ? '%i18n:desktop.tags.mk-drive-browser-nav-folder.drive%' : folder.name }</span> - <style> - :scope - &[data-draghover] - background #eee - - </style> - <script> - this.mixin('api'); - - this.folder = this.opts.folder ? this.opts.folder : null; - this.browser = this.parent; - - this.hover = false; - - this.onclick = () => { - this.browser.move(this.folder); - }; - - this.onmouseover = () => { - this.hover = true - }; - - this.onmouseout = () => { - this.hover = false - }; - - this.ondragover = e => { - e.preventDefault(); - e.stopPropagation(); - - // このフォルダがルートかつカレントディレクトリならドロップ禁止 - if (this.folder == null && this.browser.folder == null) { - e.dataTransfer.dropEffect = 'none'; - // ドラッグされてきたものがファイルだったら - } else if (e.dataTransfer.effectAllowed == 'all') { - e.dataTransfer.dropEffect = 'copy'; - } else { - e.dataTransfer.dropEffect = 'move'; - } - return false; - }; - - this.ondragenter = () => { - if (this.folder || this.browser.folder) this.draghover = true; - }; - - this.ondragleave = () => { - if (this.folder || this.browser.folder) this.draghover = false; - }; - - this.ondrop = e => { - e.stopPropagation(); - this.draghover = false; - - // ファイルだったら - if (e.dataTransfer.files.length > 0) { - Array.from(e.dataTransfer.files).forEach(file => { - this.browser.upload(file, this.folder); - }); - return false; - }; - - // データ取得 - const data = e.dataTransfer.getData('text'); - if (data == null) return false; - - // パース - // TODO: Validate JSON - const obj = JSON.parse(data); - - // (ドライブの)ファイルだったら - if (obj.type == 'file') { - const file = obj.id; - this.browser.removeFile(file); - this.api('drive/files/update', { - file_id: file, - folder_id: this.folder ? this.folder.id : null - }); - // (ドライブの)フォルダーだったら - } else if (obj.type == 'folder') { - const folder = obj.id; - // 移動先が自分自身ならreject - if (this.folder && folder == this.folder.id) return false; - this.browser.removeFolder(folder); - this.api('drive/folders/update', { - folder_id: folder, - parent_id: this.folder ? this.folder.id : null - }); - } - - return false; - }; - </script> -</mk-drive-browser-nav-folder> diff --git a/src/web/app/desktop/tags/ellipsis-icon.tag b/src/web/app/desktop/tags/ellipsis-icon.tag deleted file mode 100644 index 8462bfc4af..0000000000 --- a/src/web/app/desktop/tags/ellipsis-icon.tag +++ /dev/null @@ -1,37 +0,0 @@ -<mk-ellipsis-icon> - <div></div> - <div></div> - <div></div> - <style> - :scope - display block - width 70px - margin 0 auto - text-align center - - > div - display inline-block - width 18px - height 18px - background-color rgba(0, 0, 0, 0.3) - border-radius 100% - animation bounce 1.4s infinite ease-in-out both - - &:nth-child(1) - animation-delay 0s - - &:nth-child(2) - margin 0 6px - animation-delay 0.16s - - &:nth-child(3) - animation-delay 0.32s - - @keyframes bounce - 0%, 80%, 100% - transform scale(0) - 40% - transform scale(1) - - </style> -</mk-ellipsis-icon> diff --git a/src/web/app/desktop/tags/follow-button.tag b/src/web/app/desktop/tags/follow-button.tag deleted file mode 100644 index ce6de3ac69..0000000000 --- a/src/web/app/desktop/tags/follow-button.tag +++ /dev/null @@ -1,150 +0,0 @@ -<mk-follow-button> - <button class={ wait: wait, follow: !user.is_following, unfollow: user.is_following } if={ !init } onclick={ onclick } disabled={ wait } title={ user.is_following ? 'フォロー解除' : 'フォローする' }> - <virtual if={ !wait && user.is_following }>%fa:minus%</virtual> - <virtual if={ !wait && !user.is_following }>%fa:plus%</virtual> - <virtual if={ wait }>%fa:spinner .pulse .fw%</virtual> - </button> - <div class="init" if={ init }>%fa:spinner .pulse .fw%</div> - <style> - :scope - display block - - > button - > .init - display block - cursor pointer - padding 0 - margin 0 - width 32px - height 32px - font-size 1em - outline none - border-radius 4px - - * - pointer-events none - - &:focus - &:after - content "" - pointer-events none - position absolute - top -5px - right -5px - bottom -5px - left -5px - border 2px solid rgba($theme-color, 0.3) - border-radius 8px - - &.follow - color #888 - background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%) - border solid 1px #e2e2e2 - - &:hover - background linear-gradient(to bottom, #f9f9f9 0%, #ececec 100%) - border-color #dcdcdc - - &:active - background #ececec - border-color #dcdcdc - - &.unfollow - color $theme-color-foreground - background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%) - border solid 1px lighten($theme-color, 15%) - - &:not(:disabled) - font-weight bold - - &:hover:not(:disabled) - background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%) - border-color $theme-color - - &:active:not(:disabled) - background $theme-color - border-color $theme-color - - &.wait - cursor wait !important - opacity 0.7 - - </style> - <script> - import isPromise from '../../common/scripts/is-promise'; - - this.mixin('i'); - this.mixin('api'); - - this.mixin('stream'); - this.connection = this.stream.getConnection(); - this.connectionId = this.stream.use(); - - this.user = null; - this.userPromise = isPromise(this.opts.user) - ? this.opts.user - : Promise.resolve(this.opts.user); - this.init = true; - this.wait = false; - - this.on('mount', () => { - this.userPromise.then(user => { - this.update({ - init: false, - user: user - }); - this.connection.on('follow', this.onStreamFollow); - this.connection.on('unfollow', this.onStreamUnfollow); - }); - }); - - this.on('unmount', () => { - this.connection.off('follow', this.onStreamFollow); - this.connection.off('unfollow', this.onStreamUnfollow); - this.stream.dispose(this.connectionId); - }); - - this.onStreamFollow = user => { - if (user.id == this.user.id) { - this.update({ - user: user - }); - } - }; - - this.onStreamUnfollow = user => { - if (user.id == this.user.id) { - this.update({ - user: user - }); - } - }; - - this.onclick = () => { - this.wait = true; - if (this.user.is_following) { - this.api('following/delete', { - user_id: this.user.id - }).then(() => { - this.user.is_following = false; - }).catch(err => { - console.error(err); - }).then(() => { - this.wait = false; - this.update(); - }); - } else { - this.api('following/create', { - user_id: this.user.id - }).then(() => { - this.user.is_following = true; - }).catch(err => { - console.error(err); - }).then(() => { - this.wait = false; - this.update(); - }); - } - }; - </script> -</mk-follow-button> diff --git a/src/web/app/desktop/tags/following-setuper.tag b/src/web/app/desktop/tags/following-setuper.tag deleted file mode 100644 index a51a38ccd5..0000000000 --- a/src/web/app/desktop/tags/following-setuper.tag +++ /dev/null @@ -1,169 +0,0 @@ -<mk-following-setuper> - <p class="title">気になるユーザーをフォロー:</p> - <div class="users" if={ !fetching && users.length > 0 }> - <div class="user" each={ users }><a class="avatar-anchor" href={ '/' + username }><img class="avatar" src={ avatar_url + '?thumbnail&size=42' } alt="" data-user-preview={ id }/></a> - <div class="body"><a class="name" href={ '/' + username } target="_blank" data-user-preview={ id }>{ name }</a> - <p class="username">@{ username }</p> - </div> - <mk-follow-button user={ this }/> - </div> - </div> - <p class="empty" if={ !fetching && users.length == 0 }>おすすめのユーザーは見つかりませんでした。</p> - <p class="fetching" if={ fetching }>%fa:spinner .pulse .fw%読み込んでいます<mk-ellipsis/></p> - <a class="refresh" onclick={ refresh }>もっと見る</a> - <button class="close" onclick={ close } title="閉じる">%fa:times%</button> - <style> - :scope - display block - padding 24px - - > .title - margin 0 0 12px 0 - font-size 1em - font-weight bold - color #888 - - > .users - &:after - content "" - display block - clear both - - > .user - padding 16px - width 238px - float left - - &:after - content "" - display block - clear both - - > .avatar-anchor - display block - float left - margin 0 12px 0 0 - - > .avatar - display block - width 42px - height 42px - margin 0 - border-radius 8px - vertical-align bottom - - > .body - float left - width calc(100% - 54px) - - > .name - margin 0 - font-size 16px - line-height 24px - color #555 - - > .username - margin 0 - font-size 15px - line-height 16px - color #ccc - - > mk-follow-button - position absolute - top 16px - right 16px - - > .empty - margin 0 - padding 16px - text-align center - color #aaa - - > .fetching - margin 0 - padding 16px - text-align center - color #aaa - - > [data-fa] - margin-right 4px - - > .refresh - display block - margin 0 8px 0 0 - text-align right - font-size 0.9em - color #999 - - > .close - cursor pointer - display block - position absolute - top 6px - right 6px - z-index 1 - margin 0 - padding 0 - font-size 1.2em - color #999 - border none - outline none - background transparent - - &:hover - color #555 - - &:active - color #222 - - > [data-fa] - padding 14px - - </style> - <script> - this.mixin('api'); - this.mixin('user-preview'); - - this.users = null; - this.fetching = true; - - this.limit = 6; - this.page = 0; - - this.on('mount', () => { - this.fetch(); - }); - - this.fetch = () => { - this.update({ - fetching: true, - users: null - }); - - this.api('users/recommendation', { - limit: this.limit, - offset: this.limit * this.page - }).then(users => { - this.fetching = false - this.users = users - this.update({ - fetching: false, - users: users - }); - }); - }; - - this.refresh = () => { - if (this.users.length < this.limit) { - this.page = 0; - } else { - this.page++; - } - this.fetch(); - }; - - this.close = () => { - this.unmount(); - }; - </script> -</mk-following-setuper> diff --git a/src/web/app/desktop/tags/home-widgets/access-log.tag b/src/web/app/desktop/tags/home-widgets/access-log.tag deleted file mode 100644 index 91a71022a7..0000000000 --- a/src/web/app/desktop/tags/home-widgets/access-log.tag +++ /dev/null @@ -1,95 +0,0 @@ -<mk-access-log-home-widget> - <virtual if={ data.design == 0 }> - <p class="title">%fa:server%%i18n:desktop.tags.mk-access-log-home-widget.title%</p> - </virtual> - <div ref="log"> - <p each={ requests }> - <span class="ip" style="color:{ fg }; background:{ bg }">{ ip }</span> - <span>{ method }</span> - <span>{ path }</span> - </p> - </div> - <style> - :scope - display block - overflow hidden - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - > .title - z-index 1 - margin 0 - padding 0 16px - line-height 42px - font-size 0.9em - font-weight bold - color #888 - box-shadow 0 1px rgba(0, 0, 0, 0.07) - - > [data-fa] - margin-right 4px - - > div - max-height 250px - overflow auto - - > p - margin 0 - padding 8px - font-size 0.8em - color #555 - - &:nth-child(odd) - background rgba(0, 0, 0, 0.025) - - > .ip - margin-right 4px - - </style> - <script> - import seedrandom from 'seedrandom'; - - this.data = { - design: 0 - }; - - this.mixin('widget'); - - this.mixin('requests-stream'); - this.connection = this.requestsStream.getConnection(); - this.connectionId = this.requestsStream.use(); - - this.requests = []; - - this.on('mount', () => { - this.connection.on('request', this.onRequest); - }); - - this.on('unmount', () => { - this.connection.off('request', this.onRequest); - this.requestsStream.dispose(this.connectionId); - }); - - this.onRequest = request => { - const random = seedrandom(request.ip); - const r = Math.floor(random() * 255); - const g = Math.floor(random() * 255); - const b = Math.floor(random() * 255); - const luma = (0.2126 * r) + (0.7152 * g) + (0.0722 * b); // SMPTE C, Rec. 709 weightings - request.bg = `rgb(${r}, ${g}, ${b})`; - request.fg = luma >= 165 ? '#000' : '#fff'; - - this.requests.push(request); - if (this.requests.length > 30) this.requests.shift(); - this.update(); - - this.refs.log.scrollTop = this.refs.log.scrollHeight; - }; - - this.func = () => { - if (++this.data.design == 2) this.data.design = 0; - this.save(); - }; - </script> -</mk-access-log-home-widget> diff --git a/src/web/app/desktop/tags/home-widgets/activity.tag b/src/web/app/desktop/tags/home-widgets/activity.tag deleted file mode 100644 index 2274e84162..0000000000 --- a/src/web/app/desktop/tags/home-widgets/activity.tag +++ /dev/null @@ -1,32 +0,0 @@ -<mk-activity-home-widget> - <mk-activity-widget design={ data.design } view={ data.view } user={ I } ref="activity"/> - <style> - :scope - display block - </style> - <script> - this.data = { - view: 0, - design: 0 - }; - - this.mixin('widget'); - - this.initializing = true; - - this.on('mount', () => { - this.refs.activity.on('view-changed', view => { - this.data.view = view; - this.save(); - }); - }); - - this.func = () => { - if (++this.data.design == 3) this.data.design = 0; - this.refs.activity.update({ - design: this.data.design - }); - this.save(); - }; - </script> -</mk-activity-home-widget> diff --git a/src/web/app/desktop/tags/home-widgets/broadcast.tag b/src/web/app/desktop/tags/home-widgets/broadcast.tag deleted file mode 100644 index 6f4bb0756d..0000000000 --- a/src/web/app/desktop/tags/home-widgets/broadcast.tag +++ /dev/null @@ -1,143 +0,0 @@ -<mk-broadcast-home-widget data-found={ broadcasts.length != 0 } data-melt={ data.design == 1 }> - <div class="icon"> - <svg height="32" version="1.1" viewBox="0 0 32 32" width="32"> - <path class="tower" d="M16.04,11.24c1.79,0,3.239-1.45,3.239-3.24S17.83,4.76,16.04,4.76c-1.79,0-3.24,1.45-3.24,3.24 C12.78,9.78,14.24,11.24,16.04,11.24z M16.04,13.84c-0.82,0-1.66-0.2-2.4-0.6L7.34,29.98h2.98l1.72-2h8l1.681,2H24.7L18.42,13.24 C17.66,13.64,16.859,13.84,16.04,13.84z M16.02,14.8l2.02,7.2h-4L16.02,14.8z M12.04,25.98l2-2h4l2,2H12.04z"></path> - <path class="wave a" d="M4.66,1.04c-0.508-0.508-1.332-0.508-1.84,0c-1.86,1.92-2.8,4.44-2.8,6.94c0,2.52,0.94,5.04,2.8,6.96 c0.5,0.52,1.32,0.52,1.82,0s0.5-1.36,0-1.88C3.28,11.66,2.6,9.82,2.6,7.98S3.28,4.3,4.64,2.9C5.157,2.391,5.166,1.56,4.66,1.04z"></path> - <path class="wave b" d="M9.58,12.22c0.5-0.5,0.5-1.34,0-1.84C8.94,9.72,8.62,8.86,8.62,8s0.32-1.72,0.96-2.38c0.5-0.52,0.5-1.34,0-1.84 C9.346,3.534,9.02,3.396,8.68,3.4c-0.32,0-0.66,0.12-0.9,0.38C6.64,4.94,6.08,6.48,6.08,8s0.58,3.06,1.7,4.22 C8.28,12.72,9.1,12.72,9.58,12.22z"></path> - <path class="wave c" d="M22.42,3.78c-0.5,0.5-0.5,1.34,0,1.84c0.641,0.66,0.96,1.52,0.96,2.38s-0.319,1.72-0.96,2.38c-0.5,0.52-0.5,1.34,0,1.84 c0.487,0.497,1.285,0.505,1.781,0.018c0.007-0.006,0.013-0.012,0.02-0.018c1.139-1.16,1.699-2.7,1.699-4.22s-0.561-3.06-1.699-4.22 c-0.494-0.497-1.297-0.5-1.794-0.007C22.424,3.775,22.422,3.778,22.42,3.78z"></path> - <path class="wave d" d="M29.18,1.06c-0.479-0.502-1.273-0.522-1.775-0.044c-0.016,0.015-0.029,0.029-0.045,0.044c-0.5,0.52-0.5,1.36,0,1.88 c1.361,1.4,2.041,3.24,2.041,5.08s-0.68,3.66-2.041,5.08c-0.5,0.52-0.5,1.36,0,1.88c0.509,0.508,1.332,0.508,1.841,0 c1.86-1.92,2.8-4.44,2.8-6.96C31.99,5.424,30.98,2.931,29.18,1.06z"></path> - </svg> - </div> - <p class="fetching" if={ fetching }>%i18n:desktop.tags.mk-broadcast-home-widget.fetching%<mk-ellipsis/></p> - <h1 if={ !fetching }>{ - broadcasts.length == 0 ? '%i18n:desktop.tags.mk-broadcast-home-widget.no-broadcasts%' : broadcasts[i].title - }</h1> - <p if={ !fetching }><mk-raw if={ broadcasts.length != 0 } content={ broadcasts[i].text }/><virtual if={ broadcasts.length == 0 }>%i18n:desktop.tags.mk-broadcast-home-widget.have-a-nice-day%</virtual></p> - <a if={ broadcasts.length > 1 } onclick={ next }>%i18n:desktop.tags.mk-broadcast-home-widget.next% >></a> - <style> - :scope - display block - padding 10px - border solid 1px #4078c0 - border-radius 6px - - &[data-melt] - border none - - &[data-found] - padding-left 50px - - > .icon - display block - - &:after - content "" - display block - clear both - - > .icon - display none - float left - margin-left -40px - - > svg - fill currentColor - color #4078c0 - - > .wave - opacity 1 - - &.a - animation wave 20s ease-in-out 2.1s infinite - &.b - animation wave 20s ease-in-out 2s infinite - &.c - animation wave 20s ease-in-out 2s infinite - &.d - animation wave 20s ease-in-out 2.1s infinite - - @keyframes wave - 0% - opacity 1 - 1.5% - opacity 0 - 3.5% - opacity 0 - 5% - opacity 1 - 6.5% - opacity 0 - 8.5% - opacity 0 - 10% - opacity 1 - - > h1 - margin 0 - font-size 0.95em - font-weight normal - color #4078c0 - - > p - display block - z-index 1 - margin 0 - font-size 0.7em - color #555 - - &.fetching - text-align center - - a - color #555 - text-decoration underline - - > a - display block - font-size 0.7em - - </style> - <script> - this.data = { - design: 0 - }; - - this.mixin('widget'); - this.mixin('os'); - - this.i = 0; - this.fetching = true; - this.broadcasts = []; - - this.on('mount', () => { - this.mios.getMeta().then(meta => { - let broadcasts = []; - if (meta.broadcasts) { - meta.broadcasts.forEach(broadcast => { - if (broadcast[_LANG_]) { - broadcasts.push(broadcast[_LANG_]); - } - }); - } - this.update({ - fetching: false, - broadcasts: broadcasts - }); - }); - }); - - this.next = () => { - if (this.i == this.broadcasts.length - 1) { - this.i = 0; - } else { - this.i++; - } - this.update(); - }; - - this.func = () => { - if (++this.data.design == 2) this.data.design = 0; - this.save(); - }; - </script> -</mk-broadcast-home-widget> diff --git a/src/web/app/desktop/tags/home-widgets/calendar.tag b/src/web/app/desktop/tags/home-widgets/calendar.tag deleted file mode 100644 index fded57e07a..0000000000 --- a/src/web/app/desktop/tags/home-widgets/calendar.tag +++ /dev/null @@ -1,167 +0,0 @@ -<mk-calendar-home-widget data-melt={ data.design == 1 } data-special={ special }> - <div class="calendar" data-is-holiday={ isHoliday }> - <p class="month-and-year"><span class="year">{ year }年</span><span class="month">{ month }月</span></p> - <p class="day">{ day }日</p> - <p class="week-day">{ weekDay }曜日</p> - </div> - <div class="info"> - <div> - <p>今日:<b>{ dayP.toFixed(1) }%</b></p> - <div class="meter"> - <div class="val" style={ 'width:' + dayP + '%' }></div> - </div> - </div> - <div> - <p>今月:<b>{ monthP.toFixed(1) }%</b></p> - <div class="meter"> - <div class="val" style={ 'width:' + monthP + '%' }></div> - </div> - </div> - <div> - <p>今年:<b>{ yearP.toFixed(1) }%</b></p> - <div class="meter"> - <div class="val" style={ 'width:' + yearP + '%' }></div> - </div> - </div> - </div> - <style> - :scope - display block - padding 16px 0 - color #777 - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - &[data-special='on-new-years-day'] - border-color #ef95a0 - - &[data-melt] - background transparent - border none - - &:after - content "" - display block - clear both - - > .calendar - float left - width 60% - text-align center - - &[data-is-holiday] - > .day - color #ef95a0 - - > p - margin 0 - line-height 18px - font-size 14px - - > span - margin 0 4px - - > .day - margin 10px 0 - line-height 32px - font-size 28px - - > .info - display block - float left - width 40% - padding 0 16px 0 0 - - > div - margin-bottom 8px - - &:last-child - margin-bottom 4px - - > p - margin 0 0 2px 0 - font-size 12px - line-height 18px - color #888 - - > b - margin-left 2px - - > .meter - width 100% - overflow hidden - background #eee - border-radius 8px - - > .val - height 4px - background $theme-color - - &:nth-child(1) - > .meter > .val - background #f7796c - - &:nth-child(2) - > .meter > .val - background #a1de41 - - &:nth-child(3) - > .meter > .val - background #41ddde - - </style> - <script> - this.data = { - design: 0 - }; - - this.mixin('widget'); - - this.draw = () => { - const now = new Date(); - const nd = now.getDate(); - const nm = now.getMonth(); - const ny = now.getFullYear(); - - this.year = ny; - this.month = nm + 1; - this.day = nd; - this.weekDay = ['日', '月', '火', '水', '木', '金', '土'][now.getDay()]; - - this.dayNumer = now - new Date(ny, nm, nd); - this.dayDenom = 1000/*ms*/ * 60/*s*/ * 60/*m*/ * 24/*h*/; - this.monthNumer = now - new Date(ny, nm, 1); - this.monthDenom = new Date(ny, nm + 1, 1) - new Date(ny, nm, 1); - this.yearNumer = now - new Date(ny, 0, 1); - this.yearDenom = new Date(ny + 1, 0, 1) - new Date(ny, 0, 1); - - this.dayP = this.dayNumer / this.dayDenom * 100; - this.monthP = this.monthNumer / this.monthDenom * 100; - this.yearP = this.yearNumer / this.yearDenom * 100; - - this.isHoliday = now.getDay() == 0 || now.getDay() == 6; - - this.special = - nm == 0 && nd == 1 ? 'on-new-years-day' : - false; - - this.update(); - }; - - this.draw(); - - this.on('mount', () => { - this.clock = setInterval(this.draw, 1000); - }); - - this.on('unmount', () => { - clearInterval(this.clock); - }); - - this.func = () => { - if (++this.data.design == 2) this.data.design = 0; - this.save(); - }; - </script> -</mk-calendar-home-widget> diff --git a/src/web/app/desktop/tags/home-widgets/channel.tag b/src/web/app/desktop/tags/home-widgets/channel.tag deleted file mode 100644 index 545bc38acf..0000000000 --- a/src/web/app/desktop/tags/home-widgets/channel.tag +++ /dev/null @@ -1,318 +0,0 @@ -<mk-channel-home-widget> - <virtual if={ !data.compact }> - <p class="title">%fa:tv%{ - channel ? channel.title : '%i18n:desktop.tags.mk-channel-home-widget.title%' - }</p> - <button onclick={ settings } title="%i18n:desktop.tags.mk-channel-home-widget.settings%">%fa:cog%</button> - </virtual> - <p class="get-started" if={ this.data.channel == null }>%i18n:desktop.tags.mk-channel-home-widget.get-started%</p> - <mk-channel ref="channel" show={ this.data.channel }/> - <style> - :scope - display block - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - overflow hidden - - > .title - z-index 2 - margin 0 - padding 0 16px - line-height 42px - font-size 0.9em - font-weight bold - color #888 - box-shadow 0 1px rgba(0, 0, 0, 0.07) - - > [data-fa] - margin-right 4px - - > button - position absolute - z-index 2 - top 0 - right 0 - padding 0 - width 42px - font-size 0.9em - line-height 42px - color #ccc - - &:hover - color #aaa - - &:active - color #999 - - > .get-started - margin 0 - padding 16px - text-align center - color #aaa - - > mk-channel - height 200px - - </style> - <script> - this.data = { - channel: null, - compact: false - }; - - this.mixin('widget'); - - this.on('mount', () => { - if (this.data.channel) { - this.zap(); - } - }); - - this.zap = () => { - this.update({ - fetching: true - }); - - this.api('channels/show', { - channel_id: this.data.channel - }).then(channel => { - this.update({ - fetching: false, - channel: channel - }); - - this.refs.channel.zap(channel); - }); - }; - - this.settings = () => { - const id = window.prompt('チャンネルID'); - if (!id) return; - this.data.channel = id; - this.zap(); - - // Save state - this.save(); - }; - - this.func = () => { - this.data.compact = !this.data.compact; - this.save(); - }; - </script> -</mk-channel-home-widget> - -<mk-channel> - <p if={ fetching }>読み込み中<mk-ellipsis/></p> - <div if={ !fetching } ref="posts"> - <p if={ posts.length == 0 }>まだ投稿がありません</p> - <mk-channel-post each={ post in posts.slice().reverse() } post={ post } form={ parent.refs.form }/> - </div> - <mk-channel-form ref="form"/> - <style> - :scope - display block - - > p - margin 0 - padding 16px - text-align center - color #aaa - - > div - height calc(100% - 38px) - overflow auto - font-size 0.9em - - > mk-channel-post - border-bottom solid 1px #eee - - &:last-child - border-bottom none - - > mk-channel-form - position absolute - left 0 - bottom 0 - - </style> - <script> - import ChannelStream from '../../../common/scripts/streaming/channel-stream'; - - this.mixin('api'); - - this.fetching = true; - this.channel = null; - this.posts = []; - - this.on('unmount', () => { - if (this.connection) { - this.connection.off('post', this.onPost); - this.connection.close(); - } - }); - - this.zap = channel => { - this.update({ - fetching: true, - channel: channel - }); - - this.api('channels/posts', { - channel_id: channel.id - }).then(posts => { - this.update({ - fetching: false, - posts: posts - }); - - this.scrollToBottom(); - - if (this.connection) { - this.connection.off('post', this.onPost); - this.connection.close(); - } - this.connection = new ChannelStream(this.channel.id); - this.connection.on('post', this.onPost); - }); - }; - - this.onPost = post => { - this.posts.unshift(post); - this.update(); - this.scrollToBottom(); - }; - - this.scrollToBottom = () => { - this.refs.posts.scrollTop = this.refs.posts.scrollHeight; - }; - </script> -</mk-channel> - -<mk-channel-post> - <header> - <a class="index" onclick={ reply }>{ post.index }:</a> - <a class="name" href={ _URL_ + '/' + post.user.username }><b>{ post.user.name }</b></a> - <span>ID:<i>{ post.user.username }</i></span> - </header> - <div> - <a if={ post.reply }>>>{ post.reply.index }</a> - { post.text } - <div class="media" if={ post.media }> - <virtual each={ file in post.media }> - <a href={ file.url } target="_blank"> - <img src={ file.url + '?thumbnail&size=512' } alt={ file.name } title={ file.name }/> - </a> - </virtual> - </div> - </div> - <style> - :scope - display block - margin 0 - padding 0 - color #444 - - > header - position -webkit-sticky - position sticky - z-index 1 - top 0 - padding 8px 4px 4px 16px - background rgba(255, 255, 255, 0.9) - - > .index - margin-right 0.25em - - > .name - margin-right 0.5em - color #008000 - - > div - padding 0 16px 16px 16px - - > .media - > a - display inline-block - - > img - max-width 100% - vertical-align bottom - - </style> - <script> - this.post = this.opts.post; - this.form = this.opts.form; - - this.reply = () => { - this.form.refs.text.value = `>>${ this.post.index } `; - }; - </script> -</mk-channel-post> - -<mk-channel-form> - <input ref="text" disabled={ wait } onkeydown={ onkeydown } placeholder="書いて"> - <style> - :scope - display block - width 100% - height 38px - padding 4px - border-top solid 1px #ddd - - > input - padding 0 8px - width 100% - height 100% - font-size 14px - color #55595c - border solid 1px #dadada - border-radius 4px - - &:hover - &:focus - border-color #aeaeae - - </style> - <script> - this.mixin('api'); - - this.clear = () => { - this.refs.text.value = ''; - }; - - this.onkeydown = e => { - if (e.which == 10 || e.which == 13) this.post(); - }; - - this.post = () => { - this.update({ - wait: true - }); - - let text = this.refs.text.value; - let reply = null; - - if (/^>>([0-9]+) /.test(text)) { - const index = text.match(/^>>([0-9]+) /)[1]; - reply = this.parent.posts.find(p => p.index.toString() == index); - text = text.replace(/^>>([0-9]+) /, ''); - } - - this.api('posts/create', { - text: text, - reply_id: reply ? reply.id : undefined, - channel_id: this.parent.channel.id - }).then(data => { - this.clear(); - }).catch(err => { - alert('失敗した'); - }).then(() => { - this.update({ - wait: false - }); - }); - }; - </script> -</mk-channel-form> diff --git a/src/web/app/desktop/tags/home-widgets/donation.tag b/src/web/app/desktop/tags/home-widgets/donation.tag deleted file mode 100644 index a51a7bebbb..0000000000 --- a/src/web/app/desktop/tags/home-widgets/donation.tag +++ /dev/null @@ -1,36 +0,0 @@ -<mk-donation-home-widget> - <article> - <h1>%fa:heart%%i18n:desktop.tags.mk-donation-home-widget.title%</h1> - <p>{'%i18n:desktop.tags.mk-donation-home-widget.text%'.substr(0, '%i18n:desktop.tags.mk-donation-home-widget.text%'.indexOf('{'))}<a href="/syuilo" data-user-preview="@syuilo">@syuilo</a>{'%i18n:desktop.tags.mk-donation-home-widget.text%'.substr('%i18n:desktop.tags.mk-donation-home-widget.text%'.indexOf('}') + 1)}</p> - </article> - <style> - :scope - display block - background #fff - border solid 1px #ead8bb - border-radius 6px - - > article - padding 20px - - > h1 - margin 0 0 5px 0 - font-size 1em - color #888 - - > [data-fa] - margin-right 0.25em - - > p - display block - z-index 1 - margin 0 - font-size 0.8em - color #999 - - </style> - <script> - this.mixin('widget'); - this.mixin('user-preview'); - </script> -</mk-donation-home-widget> diff --git a/src/web/app/desktop/tags/home-widgets/mentions.tag b/src/web/app/desktop/tags/home-widgets/mentions.tag deleted file mode 100644 index a48c7239a1..0000000000 --- a/src/web/app/desktop/tags/home-widgets/mentions.tag +++ /dev/null @@ -1,125 +0,0 @@ -<mk-mentions-home-widget> - <header><span data-is-active={ mode == 'all' } onclick={ setMode.bind(this, 'all') }>すべて</span><span data-is-active={ mode == 'following' } onclick={ setMode.bind(this, 'following') }>フォロー中</span></header> - <div class="loading" if={ isLoading }> - <mk-ellipsis-icon/> - </div> - <p class="empty" if={ isEmpty }>%fa:R comments%<span if={ mode == 'all' }>あなた宛ての投稿はありません。</span><span if={ mode == 'following' }>あなたがフォローしているユーザーからの言及はありません。</span></p> - <mk-timeline ref="timeline"> - <yield to="footer"> - <virtual if={ !parent.moreLoading }>%fa:moon%</virtual> - <virtual if={ parent.moreLoading }>%fa:spinner .pulse .fw%</virtual> - </yield/> - </mk-timeline> - <style> - :scope - display block - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - > header - padding 8px 16px - border-bottom solid 1px #eee - - > span - margin-right 16px - line-height 27px - font-size 18px - color #555 - - &:not([data-is-active]) - color $theme-color - cursor pointer - - &:hover - text-decoration underline - - > .loading - padding 64px 0 - - > .empty - display block - margin 0 auto - padding 32px - max-width 400px - text-align center - color #999 - - > [data-fa] - display block - margin-bottom 16px - font-size 3em - color #ccc - - </style> - <script> - this.mixin('i'); - this.mixin('api'); - - this.isLoading = true; - this.isEmpty = false; - this.moreLoading = false; - this.mode = 'all'; - - this.on('mount', () => { - document.addEventListener('keydown', this.onDocumentKeydown); - window.addEventListener('scroll', this.onScroll); - - this.fetch(() => this.trigger('loaded')); - }); - - this.on('unmount', () => { - document.removeEventListener('keydown', this.onDocumentKeydown); - window.removeEventListener('scroll', this.onScroll); - }); - - this.onDocumentKeydown = e => { - if (e.target.tagName != 'INPUT' && tag != 'TEXTAREA') { - if (e.which == 84) { // t - this.refs.timeline.focus(); - } - } - }; - - this.fetch = cb => { - this.api('posts/mentions', { - following: this.mode == 'following' - }).then(posts => { - this.update({ - isLoading: false, - isEmpty: posts.length == 0 - }); - this.refs.timeline.setPosts(posts); - if (cb) cb(); - }); - }; - - this.more = () => { - if (this.moreLoading || this.isLoading || this.refs.timeline.posts.length == 0) return; - this.update({ - moreLoading: true - }); - this.api('posts/mentions', { - following: this.mode == 'following', - max_id: this.refs.timeline.tail().id - }).then(posts => { - this.update({ - moreLoading: false - }); - this.refs.timeline.prependPosts(posts); - }); - }; - - this.onScroll = () => { - const current = window.scrollY + window.innerHeight; - if (current > document.body.offsetHeight - 8) this.more(); - }; - - this.setMode = mode => { - this.update({ - mode: mode - }); - this.fetch(); - }; - </script> -</mk-mentions-home-widget> diff --git a/src/web/app/desktop/tags/home-widgets/messaging.tag b/src/web/app/desktop/tags/home-widgets/messaging.tag deleted file mode 100644 index f2c7c35896..0000000000 --- a/src/web/app/desktop/tags/home-widgets/messaging.tag +++ /dev/null @@ -1,52 +0,0 @@ -<mk-messaging-home-widget> - <virtual if={ data.design == 0 }> - <p class="title">%fa:comments%%i18n:desktop.tags.mk-messaging-home-widget.title%</p> - </virtual> - <mk-messaging ref="index" compact={ true }/> - <style> - :scope - display block - overflow hidden - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - > .title - z-index 2 - margin 0 - padding 0 16px - line-height 42px - font-size 0.9em - font-weight bold - color #888 - box-shadow 0 1px rgba(0, 0, 0, 0.07) - - > [data-fa] - margin-right 4px - - > mk-messaging - max-height 250px - overflow auto - - </style> - <script> - this.data = { - design: 0 - }; - - this.mixin('widget'); - - this.on('mount', () => { - this.refs.index.on('navigate-user', user => { - riot.mount(document.body.appendChild(document.createElement('mk-messaging-room-window')), { - user: user - }); - }); - }); - - this.func = () => { - if (++this.data.design == 2) this.data.design = 0; - this.save(); - }; - </script> -</mk-messaging-home-widget> diff --git a/src/web/app/desktop/tags/home-widgets/nav.tag b/src/web/app/desktop/tags/home-widgets/nav.tag deleted file mode 100644 index 61c0b4cb55..0000000000 --- a/src/web/app/desktop/tags/home-widgets/nav.tag +++ /dev/null @@ -1,23 +0,0 @@ -<mk-nav-home-widget> - <mk-nav-links/> - <style> - :scope - display block - padding 16px - font-size 12px - color #aaa - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - a - color #999 - - i - color #ccc - - </style> - <script> - this.mixin('widget'); - </script> -</mk-nav-home-widget> diff --git a/src/web/app/desktop/tags/home-widgets/notifications.tag b/src/web/app/desktop/tags/home-widgets/notifications.tag deleted file mode 100644 index 0ccd832d71..0000000000 --- a/src/web/app/desktop/tags/home-widgets/notifications.tag +++ /dev/null @@ -1,66 +0,0 @@ -<mk-notifications-home-widget> - <virtual if={ !data.compact }> - <p class="title">%fa:R bell%%i18n:desktop.tags.mk-notifications-home-widget.title%</p> - <button onclick={ settings } title="%i18n:desktop.tags.mk-notifications-home-widget.settings%">%fa:cog%</button> - </virtual> - <mk-notifications/> - <style> - :scope - display block - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - > .title - z-index 1 - margin 0 - padding 0 16px - line-height 42px - font-size 0.9em - font-weight bold - color #888 - box-shadow 0 1px rgba(0, 0, 0, 0.07) - - > [data-fa] - margin-right 4px - - > button - position absolute - z-index 2 - top 0 - right 0 - padding 0 - width 42px - font-size 0.9em - line-height 42px - color #ccc - - &:hover - color #aaa - - &:active - color #999 - - > mk-notifications - max-height 300px - overflow auto - - </style> - <script> - this.data = { - compact: false - }; - - this.mixin('widget'); - - this.settings = () => { - const w = riot.mount(document.body.appendChild(document.createElement('mk-settings-window')))[0]; - w.switch('notification'); - }; - - this.func = () => { - this.data.compact = !this.data.compact; - this.save(); - }; - </script> -</mk-notifications-home-widget> diff --git a/src/web/app/desktop/tags/home-widgets/photo-stream.tag b/src/web/app/desktop/tags/home-widgets/photo-stream.tag deleted file mode 100644 index e3bf3a988c..0000000000 --- a/src/web/app/desktop/tags/home-widgets/photo-stream.tag +++ /dev/null @@ -1,118 +0,0 @@ -<mk-photo-stream-home-widget data-melt={ data.design == 2 }> - <virtual if={ data.design == 0 }> - <p class="title">%fa:camera%%i18n:desktop.tags.mk-photo-stream-home-widget.title%</p> - </virtual> - <p class="initializing" if={ initializing }>%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p> - <div class="stream" if={ !initializing && images.length > 0 }> - <virtual each={ image in images }> - <div class="img" style={ 'background-image: url(' + image.url + '?thumbnail&size=256)' }></div> - </virtual> - </div> - <p class="empty" if={ !initializing && images.length == 0 }>%i18n:desktop.tags.mk-photo-stream-home-widget.no-photos%</p> - <style> - :scope - display block - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - &[data-melt] - background transparent !important - border none !important - - > .stream - padding 0 - - > .img - border solid 4px transparent - border-radius 8px - - > .title - z-index 1 - margin 0 - padding 0 16px - line-height 42px - font-size 0.9em - font-weight bold - color #888 - box-shadow 0 1px rgba(0, 0, 0, 0.07) - - > [data-fa] - margin-right 4px - - > .stream - display -webkit-flex - display -moz-flex - display -ms-flex - display flex - justify-content center - flex-wrap wrap - padding 8px - - > .img - flex 1 1 33% - width 33% - height 80px - background-position center center - background-size cover - border solid 2px transparent - border-radius 4px - - > .initializing - > .empty - margin 0 - padding 16px - text-align center - color #aaa - - > [data-fa] - margin-right 4px - - </style> - <script> - this.data = { - design: 0 - }; - - this.mixin('widget'); - - this.mixin('stream'); - this.connection = this.stream.getConnection(); - this.connectionId = this.stream.use(); - - this.images = []; - this.initializing = true; - - this.on('mount', () => { - this.connection.on('drive_file_created', this.onStreamDriveFileCreated); - - this.api('drive/stream', { - type: 'image/*', - limit: 9 - }).then(images => { - this.update({ - initializing: false, - images: images - }); - }); - }); - - this.on('unmount', () => { - this.connection.off('drive_file_created', this.onStreamDriveFileCreated); - this.stream.dispose(this.connectionId); - }); - - this.onStreamDriveFileCreated = file => { - if (/^image\/.+$/.test(file.type)) { - this.images.unshift(file); - if (this.images.length > 9) this.images.pop(); - this.update(); - } - }; - - this.func = () => { - if (++this.data.design == 3) this.data.design = 0; - this.save(); - }; - </script> -</mk-photo-stream-home-widget> diff --git a/src/web/app/desktop/tags/home-widgets/post-form.tag b/src/web/app/desktop/tags/home-widgets/post-form.tag deleted file mode 100644 index c8ccc5a30e..0000000000 --- a/src/web/app/desktop/tags/home-widgets/post-form.tag +++ /dev/null @@ -1,103 +0,0 @@ -<mk-post-form-home-widget> - <mk-post-form if={ place == 'main' }/> - <virtual if={ place != 'main' }> - <virtual if={ data.design == 0 }> - <p class="title">%fa:pencil-alt%%i18n:desktop.tags.mk-post-form-home-widget.title%</p> - </virtual> - <textarea disabled={ posting } ref="text" onkeydown={ onkeydown } placeholder="%i18n:desktop.tags.mk-post-form-home-widget.placeholder%"></textarea> - <button onclick={ post } disabled={ posting }>%i18n:desktop.tags.mk-post-form-home-widget.post%</button> - </virtual> - <style> - :scope - display block - background #fff - overflow hidden - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - > .title - z-index 1 - margin 0 - padding 0 16px - line-height 42px - font-size 0.9em - font-weight bold - color #888 - box-shadow 0 1px rgba(0, 0, 0, 0.07) - - > [data-fa] - margin-right 4px - - > textarea - display block - width 100% - max-width 100% - min-width 100% - padding 16px - margin-bottom 28px + 16px - border none - border-bottom solid 1px #eee - - > button - display block - position absolute - bottom 8px - right 8px - margin 0 - padding 0 10px - height 28px - color $theme-color-foreground - background $theme-color !important - outline none - border none - border-radius 4px - transition background 0.1s ease - cursor pointer - - &:hover - background lighten($theme-color, 10%) !important - - &:active - background darken($theme-color, 10%) !important - transition background 0s ease - - </style> - <script> - this.data = { - design: 0 - }; - - this.mixin('widget'); - - this.func = () => { - if (++this.data.design == 2) this.data.design = 0; - this.save(); - }; - - this.onkeydown = e => { - if ((e.which == 10 || e.which == 13) && (e.ctrlKey || e.metaKey)) this.post(); - }; - - this.post = () => { - this.update({ - posting: true - }); - - this.api('posts/create', { - text: this.refs.text.value - }).then(data => { - this.clear(); - }).catch(err => { - alert('失敗した'); - }).then(() => { - this.update({ - posting: false - }); - }); - }; - - this.clear = () => { - this.refs.text.value = ''; - }; - </script> -</mk-post-form-home-widget> diff --git a/src/web/app/desktop/tags/home-widgets/profile.tag b/src/web/app/desktop/tags/home-widgets/profile.tag deleted file mode 100644 index eb8ba52e84..0000000000 --- a/src/web/app/desktop/tags/home-widgets/profile.tag +++ /dev/null @@ -1,116 +0,0 @@ -<mk-profile-home-widget data-compact={ data.design == 1 || data.design == 2 } data-melt={ data.design == 2 }> - <div class="banner" style={ I.banner_url ? 'background-image: url(' + I.banner_url + '?thumbnail&size=256)' : '' } title="クリックでバナー編集" onclick={ setBanner }></div> - <img class="avatar" src={ I.avatar_url + '?thumbnail&size=96' } onclick={ setAvatar } alt="avatar" title="クリックでアバター編集" data-user-preview={ I.id }/> - <a class="name" href={ '/' + I.username }>{ I.name }</a> - <p class="username">@{ I.username }</p> - <style> - :scope - display block - overflow hidden - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - &[data-compact] - > .banner:before - content "" - display block - width 100% - height 100% - background rgba(0, 0, 0, 0.5) - - > .avatar - top ((100px - 58px) / 2) - left ((100px - 58px) / 2) - border none - border-radius 100% - box-shadow 0 0 16px rgba(0, 0, 0, 0.5) - - > .name - position absolute - top 0 - left 92px - margin 0 - line-height 100px - color #fff - text-shadow 0 0 8px rgba(0, 0, 0, 0.5) - - > .username - display none - - &[data-melt] - background transparent !important - border none !important - - > .banner - visibility hidden - - > .avatar - box-shadow none - - > .name - color #666 - text-shadow none - - > .banner - height 100px - background-color #f5f5f5 - background-size cover - background-position center - cursor pointer - - > .avatar - display block - position absolute - top 76px - left 16px - width 58px - height 58px - margin 0 - border solid 3px #fff - border-radius 8px - vertical-align bottom - cursor pointer - - > .name - display block - margin 10px 0 0 84px - line-height 16px - font-weight bold - color #555 - - > .username - display block - margin 4px 0 8px 84px - line-height 16px - font-size 0.9em - color #999 - - </style> - <script> - import inputDialog from '../../scripts/input-dialog'; - import updateAvatar from '../../scripts/update-avatar'; - import updateBanner from '../../scripts/update-banner'; - - this.data = { - design: 0 - }; - - this.mixin('widget'); - - this.mixin('user-preview'); - - this.setAvatar = () => { - updateAvatar(this.I); - }; - - this.setBanner = () => { - updateBanner(this.I); - }; - - this.func = () => { - if (++this.data.design == 3) this.data.design = 0; - this.save(); - }; - </script> -</mk-profile-home-widget> diff --git a/src/web/app/desktop/tags/home-widgets/recommended-polls.tag b/src/web/app/desktop/tags/home-widgets/recommended-polls.tag deleted file mode 100644 index 776f666015..0000000000 --- a/src/web/app/desktop/tags/home-widgets/recommended-polls.tag +++ /dev/null @@ -1,119 +0,0 @@ -<mk-recommended-polls-home-widget> - <virtual if={ !data.compact }> - <p class="title">%fa:chart-pie%%i18n:desktop.tags.mk-recommended-polls-home-widget.title%</p> - <button onclick={ fetch } title="%i18n:desktop.tags.mk-recommended-polls-home-widget.refresh%">%fa:sync%</button> - </virtual> - <div class="poll" if={ !loading && poll != null }> - <p if={ poll.text }><a href="/{ poll.user.username }/{ poll.id }">{ poll.text }</a></p> - <p if={ !poll.text }><a href="/{ poll.user.username }/{ poll.id }">%fa:link%</a></p> - <mk-poll post={ poll }/> - </div> - <p class="empty" if={ !loading && poll == null }>%i18n:desktop.tags.mk-recommended-polls-home-widget.nothing%</p> - <p class="loading" if={ loading }>%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p> - <style> - :scope - display block - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - > .title - margin 0 - padding 0 16px - line-height 42px - font-size 0.9em - font-weight bold - color #888 - border-bottom solid 1px #eee - - > [data-fa] - margin-right 4px - - > button - position absolute - z-index 2 - top 0 - right 0 - padding 0 - width 42px - font-size 0.9em - line-height 42px - color #ccc - - &:hover - color #aaa - - &:active - color #999 - - > .poll - padding 16px - font-size 12px - color #555 - - > p - margin 0 0 8px 0 - - > a - color inherit - - > .empty - margin 0 - padding 16px - text-align center - color #aaa - - > .loading - margin 0 - padding 16px - text-align center - color #aaa - - > [data-fa] - margin-right 4px - - </style> - <script> - this.data = { - compact: false - }; - - this.mixin('widget'); - - this.poll = null; - this.loading = true; - - this.offset = 0; - - this.on('mount', () => { - this.fetch(); - }); - - this.fetch = () => { - this.update({ - loading: true, - poll: null - }); - this.api('posts/polls/recommendation', { - limit: 1, - offset: this.offset - }).then(posts => { - const poll = posts ? posts[0] : null; - if (poll == null) { - this.offset = 0; - } else { - this.offset++; - } - this.update({ - loading: false, - poll: poll - }); - }); - }; - - this.func = () => { - this.data.compact = !this.data.compact; - this.save(); - }; - </script> -</mk-recommended-polls-home-widget> diff --git a/src/web/app/desktop/tags/home-widgets/rss-reader.tag b/src/web/app/desktop/tags/home-widgets/rss-reader.tag deleted file mode 100644 index a927693ce8..0000000000 --- a/src/web/app/desktop/tags/home-widgets/rss-reader.tag +++ /dev/null @@ -1,109 +0,0 @@ -<mk-rss-reader-home-widget> - <virtual if={ !data.compact }> - <p class="title">%fa:rss-square%RSS</p> - <button onclick={ settings } title="設定">%fa:cog%</button> - </virtual> - <div class="feed" if={ !initializing }> - <virtual each={ item in items }><a href={ item.link } target="_blank">{ item.title }</a></virtual> - </div> - <p class="initializing" if={ initializing }>%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p> - <style> - :scope - display block - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - > .title - margin 0 - padding 0 16px - line-height 42px - font-size 0.9em - font-weight bold - color #888 - box-shadow 0 1px rgba(0, 0, 0, 0.07) - - > [data-fa] - margin-right 4px - - > button - position absolute - top 0 - right 0 - padding 0 - width 42px - font-size 0.9em - line-height 42px - color #ccc - - &:hover - color #aaa - - &:active - color #999 - - > .feed - padding 12px 16px - font-size 0.9em - - > a - display block - padding 4px 0 - color #666 - border-bottom dashed 1px #eee - - &:last-child - border-bottom none - - > .initializing - margin 0 - padding 16px - text-align center - color #aaa - - > [data-fa] - margin-right 4px - - </style> - <script> - this.data = { - compact: false - }; - - this.mixin('widget'); - - this.url = 'http://news.yahoo.co.jp/pickup/rss.xml'; - this.items = []; - this.initializing = true; - - this.on('mount', () => { - this.fetch(); - this.clock = setInterval(this.fetch, 60000); - }); - - this.on('unmount', () => { - clearInterval(this.clock); - }); - - this.fetch = () => { - fetch(`https://api.rss2json.com/v1/api.json?rss_url=${this.url}`, { - cache: 'no-cache' - }).then(res => { - res.json().then(feed => { - this.update({ - initializing: false, - items: feed.items - }); - }); - }); - }; - - this.settings = () => { - }; - - this.func = () => { - this.data.compact = !this.data.compact; - this.save(); - }; - </script> -</mk-rss-reader-home-widget> diff --git a/src/web/app/desktop/tags/home-widgets/server.tag b/src/web/app/desktop/tags/home-widgets/server.tag deleted file mode 100644 index b9b191c181..0000000000 --- a/src/web/app/desktop/tags/home-widgets/server.tag +++ /dev/null @@ -1,533 +0,0 @@ -<mk-server-home-widget data-melt={ data.design == 2 }> - <virtual if={ data.design == 0 }> - <p class="title">%fa:server%%i18n:desktop.tags.mk-server-home-widget.title%</p> - <button onclick={ toggle } title="%i18n:desktop.tags.mk-server-home-widget.toggle%">%fa:sort%</button> - </virtual> - <p class="initializing" if={ initializing }>%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p> - <mk-server-home-widget-cpu-and-memory-usage if={ !initializing } show={ data.view == 0 } connection={ connection }/> - <mk-server-home-widget-cpu if={ !initializing } show={ data.view == 1 } connection={ connection } meta={ meta }/> - <mk-server-home-widget-memory if={ !initializing } show={ data.view == 2 } connection={ connection }/> - <mk-server-home-widget-disk if={ !initializing } show={ data.view == 3 } connection={ connection }/> - <mk-server-home-widget-uptimes if={ !initializing } show={ data.view == 4 } connection={ connection }/> - <mk-server-home-widget-info if={ !initializing } show={ data.view == 5 } connection={ connection } meta={ meta }/> - <style> - :scope - display block - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - &[data-melt] - background transparent !important - border none !important - - > .title - z-index 1 - margin 0 - padding 0 16px - line-height 42px - font-size 0.9em - font-weight bold - color #888 - box-shadow 0 1px rgba(0, 0, 0, 0.07) - - > [data-fa] - margin-right 4px - - > button - position absolute - z-index 2 - top 0 - right 0 - padding 0 - width 42px - font-size 0.9em - line-height 42px - color #ccc - - &:hover - color #aaa - - &:active - color #999 - - > .initializing - margin 0 - padding 16px - text-align center - color #aaa - - > [data-fa] - margin-right 4px - - </style> - <script> - this.mixin('os'); - - this.data = { - view: 0, - design: 0 - }; - - this.mixin('widget'); - - this.mixin('server-stream'); - this.connection = this.serverStream.getConnection(); - this.connectionId = this.serverStream.use(); - - this.initializing = true; - - this.on('mount', () => { - this.mios.getMeta().then(meta => { - this.update({ - initializing: false, - meta - }); - }); - }); - - this.on('unmount', () => { - this.serverStream.dispose(this.connectionId); - }); - - this.toggle = () => { - this.data.view++; - if (this.data.view == 6) this.data.view = 0; - - // Save widget state - this.save(); - }; - - this.func = () => { - if (++this.data.design == 3) this.data.design = 0; - this.save(); - }; - </script> -</mk-server-home-widget> - -<mk-server-home-widget-cpu-and-memory-usage> - <svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none"> - <defs> - <linearGradient id={ cpuGradientId } x1="0" x2="0" y1="1" y2="0"> - <stop offset="0%" stop-color="hsl(180, 80%, 70%)"></stop> - <stop offset="33%" stop-color="hsl(120, 80%, 70%)"></stop> - <stop offset="66%" stop-color="hsl(60, 80%, 70%)"></stop> - <stop offset="100%" stop-color="hsl(0, 80%, 70%)"></stop> - </linearGradient> - <mask id={ cpuMaskId } x="0" y="0" riot-width={ viewBoxX } riot-height={ viewBoxY }> - <polygon - riot-points={ cpuPolygonPoints } - fill="#fff" - fill-opacity="0.5"/> - <polyline - riot-points={ cpuPolylinePoints } - fill="none" - stroke="#fff" - stroke-width="1"/> - </mask> - </defs> - <rect - x="-1" y="-1" - riot-width={ viewBoxX + 2 } riot-height={ viewBoxY + 2 } - style="stroke: none; fill: url(#{ cpuGradientId }); mask: url(#{ cpuMaskId })"/> - <text x="1" y="5">CPU <tspan>{ cpuP }%</tspan></text> - </svg> - <svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none"> - <defs> - <linearGradient id={ memGradientId } x1="0" x2="0" y1="1" y2="0"> - <stop offset="0%" stop-color="hsl(180, 80%, 70%)"></stop> - <stop offset="33%" stop-color="hsl(120, 80%, 70%)"></stop> - <stop offset="66%" stop-color="hsl(60, 80%, 70%)"></stop> - <stop offset="100%" stop-color="hsl(0, 80%, 70%)"></stop> - </linearGradient> - <mask id={ memMaskId } x="0" y="0" riot-width={ viewBoxX } riot-height={ viewBoxY }> - <polygon - riot-points={ memPolygonPoints } - fill="#fff" - fill-opacity="0.5"/> - <polyline - riot-points={ memPolylinePoints } - fill="none" - stroke="#fff" - stroke-width="1"/> - </mask> - </defs> - <rect - x="-1" y="-1" - riot-width={ viewBoxX + 2 } riot-height={ viewBoxY + 2 } - style="stroke: none; fill: url(#{ memGradientId }); mask: url(#{ memMaskId })"/> - <text x="1" y="5">MEM <tspan>{ memP }%</tspan></text> - </svg> - <style> - :scope - display block - - > svg - display block - padding 10px - width 50% - float left - - &:first-child - padding-right 5px - - &:last-child - padding-left 5px - - > text - font-size 5px - fill rgba(0, 0, 0, 0.55) - - > tspan - opacity 0.5 - - &:after - content "" - display block - clear both - </style> - <script> - import uuid from 'uuid'; - - this.viewBoxX = 50; - this.viewBoxY = 30; - this.stats = []; - this.connection = this.opts.connection; - this.cpuGradientId = uuid(); - this.cpuMaskId = uuid(); - this.memGradientId = uuid(); - this.memMaskId = uuid(); - - this.on('mount', () => { - this.connection.on('stats', this.onStats); - }); - - this.on('unmount', () => { - this.connection.off('stats', this.onStats); - }); - - this.onStats = stats => { - stats.mem.used = stats.mem.total - stats.mem.free; - this.stats.push(stats); - if (this.stats.length > 50) this.stats.shift(); - - const cpuPolylinePoints = this.stats.map((s, i) => `${this.viewBoxX - ((this.stats.length - 1) - i)},${(1 - s.cpu_usage) * this.viewBoxY}`).join(' '); - const memPolylinePoints = this.stats.map((s, i) => `${this.viewBoxX - ((this.stats.length - 1) - i)},${(1 - (s.mem.used / s.mem.total)) * this.viewBoxY}`).join(' '); - - const cpuPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${ this.viewBoxY } ${ cpuPolylinePoints } ${ this.viewBoxX },${ this.viewBoxY }`; - const memPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${ this.viewBoxY } ${ memPolylinePoints } ${ this.viewBoxX },${ this.viewBoxY }`; - - const cpuP = (stats.cpu_usage * 100).toFixed(0); - const memP = (stats.mem.used / stats.mem.total * 100).toFixed(0); - - this.update({ - cpuPolylinePoints, - memPolylinePoints, - cpuPolygonPoints, - memPolygonPoints, - cpuP, - memP - }); - }; - </script> -</mk-server-home-widget-cpu-and-memory-usage> - -<mk-server-home-widget-cpu> - <mk-server-home-widget-pie ref="pie"/> - <div> - <p>%fa:microchip%CPU</p> - <p>{ cores } Cores</p> - <p>{ model }</p> - </div> - <style> - :scope - display block - - > mk-server-home-widget-pie - padding 10px - height 100px - float left - - > div - float left - width calc(100% - 100px) - padding 10px 10px 10px 0 - - > p - margin 0 - font-size 12px - color #505050 - - &:first-child - font-weight bold - - > [data-fa] - margin-right 4px - - &:after - content "" - display block - clear both - - </style> - <script> - this.cores = this.opts.meta.cpu.cores; - this.model = this.opts.meta.cpu.model; - this.connection = this.opts.connection; - - this.on('mount', () => { - this.connection.on('stats', this.onStats); - }); - - this.on('unmount', () => { - this.connection.off('stats', this.onStats); - }); - - this.onStats = stats => { - this.refs.pie.render(stats.cpu_usage); - }; - </script> -</mk-server-home-widget-cpu> - -<mk-server-home-widget-memory> - <mk-server-home-widget-pie ref="pie"/> - <div> - <p>%fa:flask%Memory</p> - <p>Total: { bytesToSize(total, 1) }</p> - <p>Used: { bytesToSize(used, 1) }</p> - <p>Free: { bytesToSize(free, 1) }</p> - </div> - <style> - :scope - display block - - > mk-server-home-widget-pie - padding 10px - height 100px - float left - - > div - float left - width calc(100% - 100px) - padding 10px 10px 10px 0 - - > p - margin 0 - font-size 12px - color #505050 - - &:first-child - font-weight bold - - > [data-fa] - margin-right 4px - - &:after - content "" - display block - clear both - - </style> - <script> - import bytesToSize from '../../../common/scripts/bytes-to-size'; - - this.connection = this.opts.connection; - this.bytesToSize = bytesToSize; - - this.on('mount', () => { - this.connection.on('stats', this.onStats); - }); - - this.on('unmount', () => { - this.connection.off('stats', this.onStats); - }); - - this.onStats = stats => { - stats.mem.used = stats.mem.total - stats.mem.free; - this.refs.pie.render(stats.mem.used / stats.mem.total); - - this.update({ - total: stats.mem.total, - used: stats.mem.used, - free: stats.mem.free - }); - }; - </script> -</mk-server-home-widget-memory> - -<mk-server-home-widget-disk> - <mk-server-home-widget-pie ref="pie"/> - <div> - <p>%fa:R hdd%Storage</p> - <p>Total: { bytesToSize(total, 1) }</p> - <p>Available: { bytesToSize(available, 1) }</p> - <p>Used: { bytesToSize(used, 1) }</p> - </div> - <style> - :scope - display block - - > mk-server-home-widget-pie - padding 10px - height 100px - float left - - > div - float left - width calc(100% - 100px) - padding 10px 10px 10px 0 - - > p - margin 0 - font-size 12px - color #505050 - - &:first-child - font-weight bold - - > [data-fa] - margin-right 4px - - &:after - content "" - display block - clear both - - </style> - <script> - import bytesToSize from '../../../common/scripts/bytes-to-size'; - - this.connection = this.opts.connection; - this.bytesToSize = bytesToSize; - - this.on('mount', () => { - this.connection.on('stats', this.onStats); - }); - - this.on('unmount', () => { - this.connection.off('stats', this.onStats); - }); - - this.onStats = stats => { - stats.disk.used = stats.disk.total - stats.disk.free; - - this.refs.pie.render(stats.disk.used / stats.disk.total); - - this.update({ - total: stats.disk.total, - used: stats.disk.used, - available: stats.disk.available - }); - }; - </script> -</mk-server-home-widget-disk> - -<mk-server-home-widget-uptimes> - <p>Uptimes</p> - <p>Process: { process ? process.toFixed(0) : '---' }s</p> - <p>OS: { os ? os.toFixed(0) : '---' }s</p> - <style> - :scope - display block - padding 10px 14px - - > p - margin 0 - font-size 12px - color #505050 - - &:first-child - font-weight bold - - </style> - <script> - this.connection = this.opts.connection; - - this.on('mount', () => { - this.connection.on('stats', this.onStats); - }); - - this.on('unmount', () => { - this.connection.off('stats', this.onStats); - }); - - this.onStats = stats => { - this.update({ - process: stats.process_uptime, - os: stats.os_uptime - }); - }; - </script> -</mk-server-home-widget-uptimes> - -<mk-server-home-widget-info> - <p>Maintainer: <b>{ meta.maintainer }</b></p> - <p>Machine: { meta.machine }</p> - <p>Node: { meta.node }</p> - <style> - :scope - display block - padding 10px 14px - - > p - margin 0 - font-size 12px - color #505050 - - </style> - <script> - this.meta = this.opts.meta; - </script> -</mk-server-home-widget-info> - -<mk-server-home-widget-pie> - <svg viewBox="0 0 1 1" preserveAspectRatio="none"> - <circle - riot-r={ r } - cx="50%" cy="50%" - fill="none" - stroke-width="0.1" - stroke="rgba(0, 0, 0, 0.05)"/> - <circle - riot-r={ r } - cx="50%" cy="50%" - riot-stroke-dasharray={ Math.PI * (r * 2) } - riot-stroke-dashoffset={ strokeDashoffset } - fill="none" - stroke-width="0.1" - riot-stroke={ color }/> - <text x="50%" y="50%" dy="0.05" text-anchor="middle">{ (p * 100).toFixed(0) }%</text> - </svg> - <style> - :scope - display block - - > svg - display block - height 100% - - > circle - transform-origin center - transform rotate(-90deg) - transition stroke-dashoffset 0.5s ease - - > text - font-size 0.15px - fill rgba(0, 0, 0, 0.6) - - </style> - <script> - this.r = 0.4; - - this.render = p => { - const color = `hsl(${180 - (p * 180)}, 80%, 70%)`; - const strokeDashoffset = (1 - p) * (Math.PI * (this.r * 2)); - - this.update({ - p, - color, - strokeDashoffset - }); - }; - </script> -</mk-server-home-widget-pie> diff --git a/src/web/app/desktop/tags/home-widgets/slideshow.tag b/src/web/app/desktop/tags/home-widgets/slideshow.tag deleted file mode 100644 index 53fe047000..0000000000 --- a/src/web/app/desktop/tags/home-widgets/slideshow.tag +++ /dev/null @@ -1,151 +0,0 @@ -<mk-slideshow-home-widget> - <div onclick={ choose }> - <p if={ data.folder === undefined }>クリックしてフォルダを指定してください</p> - <p if={ data.folder !== undefined && images.length == 0 && !fetching }>このフォルダには画像がありません</p> - <div ref="slideA" class="slide a"></div> - <div ref="slideB" class="slide b"></div> - </div> - <button onclick={ resize }>%fa:expand%</button> - <style> - :scope - display block - overflow hidden - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - &:hover > button - display block - - > button - position absolute - left 0 - bottom 0 - display none - padding 4px - font-size 24px - color #fff - text-shadow 0 0 8px #000 - - > div - width 100% - height 100% - cursor pointer - - > * - pointer-events none - - > .slide - position absolute - top 0 - left 0 - width 100% - height 100% - background-size cover - background-position center - - &.b - opacity 0 - - </style> - <script> - import anime from 'animejs'; - - this.data = { - folder: undefined, - size: 0 - }; - - this.mixin('widget'); - - this.images = []; - this.fetching = true; - - this.on('mount', () => { - this.applySize(); - - if (this.data.folder !== undefined) { - this.fetch(); - } - - this.clock = setInterval(this.change, 10000); - }); - - this.on('unmount', () => { - clearInterval(this.clock); - }); - - this.applySize = () => { - let h; - - if (this.data.size == 1) { - h = 250; - } else { - h = 170; - } - - this.root.style.height = `${h}px`; - }; - - this.resize = () => { - this.data.size++; - if (this.data.size == 2) this.data.size = 0; - - this.applySize(); - this.save(); - }; - - this.change = () => { - if (this.images.length == 0) return; - - const index = Math.floor(Math.random() * this.images.length); - const img = `url(${ this.images[index].url }?thumbnail&size=1024)`; - - this.refs.slideB.style.backgroundImage = img; - - anime({ - targets: this.refs.slideB, - opacity: 1, - duration: 1000, - easing: 'linear', - complete: () => { - this.refs.slideA.style.backgroundImage = img; - anime({ - targets: this.refs.slideB, - opacity: 0, - duration: 0 - }); - } - }); - }; - - this.fetch = () => { - this.update({ - fetching: true - }); - - this.api('drive/files', { - folder_id: this.data.folder, - type: 'image/*', - limit: 100 - }).then(images => { - this.update({ - fetching: false, - images: images - }); - this.refs.slideA.style.backgroundImage = ''; - this.refs.slideB.style.backgroundImage = ''; - this.change(); - }); - }; - - this.choose = () => { - const i = riot.mount(document.body.appendChild(document.createElement('mk-select-folder-from-drive-window')))[0]; - i.one('selected', folder => { - this.data.folder = folder ? folder.id : null; - this.fetch(); - this.save(); - }); - }; - </script> -</mk-slideshow-home-widget> diff --git a/src/web/app/desktop/tags/home-widgets/timeline.tag b/src/web/app/desktop/tags/home-widgets/timeline.tag deleted file mode 100644 index 4c58aa4aa8..0000000000 --- a/src/web/app/desktop/tags/home-widgets/timeline.tag +++ /dev/null @@ -1,143 +0,0 @@ -<mk-timeline-home-widget> - <mk-following-setuper if={ noFollowing }/> - <div class="loading" if={ isLoading }> - <mk-ellipsis-icon/> - </div> - <p class="empty" if={ isEmpty && !isLoading }>%fa:R comments%自分の投稿や、自分がフォローしているユーザーの投稿が表示されます。</p> - <mk-timeline ref="timeline" hide={ isLoading }> - <yield to="footer"> - <virtual if={ !parent.moreLoading }>%fa:moon%</virtual> - <virtual if={ parent.moreLoading }>%fa:spinner .pulse .fw%</virtual> - </yield/> - </mk-timeline> - <style> - :scope - display block - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - > mk-following-setuper - border-bottom solid 1px #eee - - > .loading - padding 64px 0 - - > .empty - display block - margin 0 auto - padding 32px - max-width 400px - text-align center - color #999 - - > [data-fa] - display block - margin-bottom 16px - font-size 3em - color #ccc - - </style> - <script> - this.mixin('i'); - this.mixin('api'); - - this.mixin('stream'); - this.connection = this.stream.getConnection(); - this.connectionId = this.stream.use(); - - this.isLoading = true; - this.isEmpty = false; - this.moreLoading = false; - this.noFollowing = this.I.following_count == 0; - - this.on('mount', () => { - this.connection.on('post', this.onStreamPost); - this.connection.on('follow', this.onStreamFollow); - this.connection.on('unfollow', this.onStreamUnfollow); - - document.addEventListener('keydown', this.onDocumentKeydown); - window.addEventListener('scroll', this.onScroll); - - this.load(() => this.trigger('loaded')); - }); - - this.on('unmount', () => { - this.connection.off('post', this.onStreamPost); - this.connection.off('follow', this.onStreamFollow); - this.connection.off('unfollow', this.onStreamUnfollow); - this.stream.dispose(this.connectionId); - - document.removeEventListener('keydown', this.onDocumentKeydown); - window.removeEventListener('scroll', this.onScroll); - }); - - this.onDocumentKeydown = e => { - if (e.target.tagName != 'INPUT' && e.target.tagName != 'TEXTAREA') { - if (e.which == 84) { // t - this.refs.timeline.focus(); - } - } - }; - - this.load = (cb) => { - this.update({ - isLoading: true - }); - - this.api('posts/timeline', { - max_date: this.date ? this.date.getTime() : undefined - }).then(posts => { - this.update({ - isLoading: false, - isEmpty: posts.length == 0 - }); - this.refs.timeline.setPosts(posts); - if (cb) cb(); - }); - }; - - this.more = () => { - if (this.moreLoading || this.isLoading || this.refs.timeline.posts.length == 0) return; - this.update({ - moreLoading: true - }); - this.api('posts/timeline', { - max_id: this.refs.timeline.tail().id - }).then(posts => { - this.update({ - moreLoading: false - }); - this.refs.timeline.prependPosts(posts); - }); - }; - - this.onStreamPost = post => { - this.update({ - isEmpty: false - }); - this.refs.timeline.addPost(post); - }; - - this.onStreamFollow = () => { - this.load(); - }; - - this.onStreamUnfollow = () => { - this.load(); - }; - - this.onScroll = () => { - const current = window.scrollY + window.innerHeight; - if (current > document.body.offsetHeight - 8) this.more(); - }; - - this.warp = date => { - this.update({ - date: date - }); - - this.load(); - }; - </script> -</mk-timeline-home-widget> diff --git a/src/web/app/desktop/tags/home-widgets/timemachine.tag b/src/web/app/desktop/tags/home-widgets/timemachine.tag deleted file mode 100644 index 3cddf53551..0000000000 --- a/src/web/app/desktop/tags/home-widgets/timemachine.tag +++ /dev/null @@ -1,23 +0,0 @@ -<mk-timemachine-home-widget> - <mk-calendar-widget design={ data.design } warp={ warp }/> - <style> - :scope - display block - </style> - <script> - this.data = { - design: 0 - }; - - this.mixin('widget'); - - this.warp = date => { - this.opts.tl.warp(date); - }; - - this.func = () => { - if (++this.data.design == 6) this.data.design = 0; - this.save(); - }; - </script> -</mk-timemachine-home-widget> diff --git a/src/web/app/desktop/tags/home-widgets/tips.tag b/src/web/app/desktop/tags/home-widgets/tips.tag deleted file mode 100644 index 53b61dca91..0000000000 --- a/src/web/app/desktop/tags/home-widgets/tips.tag +++ /dev/null @@ -1,94 +0,0 @@ -<mk-tips-home-widget> - <p ref="tip">%fa:R lightbulb%<span ref="text"></span></p> - <style> - :scope - display block - overflow visible !important - - > p - display block - margin 0 - padding 0 12px - text-align center - font-size 0.7em - color #999 - - > [data-fa] - margin-right 4px - - kbd - display inline - padding 0 6px - margin 0 2px - font-size 1em - font-family inherit - border solid 1px #999 - border-radius 2px - - </style> - <script> - import anime from 'animejs'; - - this.mixin('widget'); - - this.tips = [ - '<kbd>t</kbd>でタイムラインにフォーカスできます', - '<kbd>p</kbd>または<kbd>n</kbd>で投稿フォームを開きます', - '投稿フォームにはファイルをドラッグ&ドロップできます', - '投稿フォームにクリップボードにある画像データをペーストできます', - 'ドライブにファイルをドラッグ&ドロップしてアップロードできます', - 'ドライブでファイルをドラッグしてフォルダ移動できます', - 'ドライブでフォルダをドラッグしてフォルダ移動できます', - 'ホームは設定からカスタマイズできます', - 'MisskeyはMIT Licenseです', - 'タイムマシンウィジェットを利用すると、簡単に過去のタイムラインに遡れます', - '投稿の ... をクリックして、投稿をユーザーページにピン留めできます', - 'ドライブの容量は(デフォルトで)1GBです', - '投稿に添付したファイルは全てドライブに保存されます', - 'ホームのカスタマイズ中、ウィジェットを右クリックしてデザインを変更できます', - 'タイムライン上部にもウィジェットを設置できます', - '投稿をダブルクリックすると詳細が見れます', - '「**」でテキストを囲むと**強調表示**されます', - 'チャンネルウィジェットを利用すると、よく利用するチャンネルを素早く確認できます', - 'いくつかのウィンドウはブラウザの外に切り離すことができます', - 'カレンダーウィジェットのパーセンテージは、経過の割合を示しています', - 'APIを利用してbotの開発なども行えます', - 'MisskeyはLINEを通じてでも利用できます', - 'まゆかわいいよまゆ', - 'Misskeyは2014年にサービスを開始しました', - '対応ブラウザではMisskeyを開いていなくても通知を受け取れます' - ] - - this.on('mount', () => { - this.set(); - this.clock = setInterval(this.change, 20000); - }); - - this.on('unmount', () => { - clearInterval(this.clock); - }); - - this.set = () => { - this.refs.text.innerHTML = this.tips[Math.floor(Math.random() * this.tips.length)]; - }; - - this.change = () => { - anime({ - targets: this.refs.tip, - opacity: 0, - duration: 500, - easing: 'linear', - complete: this.set - }); - - setTimeout(() => { - anime({ - targets: this.refs.tip, - opacity: 1, - duration: 500, - easing: 'linear' - }); - }, 500); - }; - </script> -</mk-tips-home-widget> diff --git a/src/web/app/desktop/tags/home-widgets/trends.tag b/src/web/app/desktop/tags/home-widgets/trends.tag deleted file mode 100644 index 3a2304111b..0000000000 --- a/src/web/app/desktop/tags/home-widgets/trends.tag +++ /dev/null @@ -1,125 +0,0 @@ -<mk-trends-home-widget> - <virtual if={ !data.compact }> - <p class="title">%fa:fire%%i18n:desktop.tags.mk-trends-home-widget.title%</p> - <button onclick={ fetch } title="%i18n:desktop.tags.mk-trends-home-widget.refresh%">%fa:sync%</button> - </virtual> - <div class="post" if={ !loading && post != null }> - <p class="text"><a href="/{ post.user.username }/{ post.id }">{ post.text }</a></p> - <p class="author">―<a href="/{ post.user.username }">@{ post.user.username }</a></p> - </div> - <p class="empty" if={ !loading && post == null }>%i18n:desktop.tags.mk-trends-home-widget.nothing%</p> - <p class="loading" if={ loading }>%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p> - <style> - :scope - display block - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - > .title - margin 0 - padding 0 16px - line-height 42px - font-size 0.9em - font-weight bold - color #888 - border-bottom solid 1px #eee - - > [data-fa] - margin-right 4px - - > button - position absolute - z-index 2 - top 0 - right 0 - padding 0 - width 42px - font-size 0.9em - line-height 42px - color #ccc - - &:hover - color #aaa - - &:active - color #999 - - > .post - padding 16px - font-size 12px - font-style oblique - color #555 - - > p - margin 0 - - > .text, - > .author - > a - color inherit - - > .empty - margin 0 - padding 16px - text-align center - color #aaa - - > .loading - margin 0 - padding 16px - text-align center - color #aaa - - > [data-fa] - margin-right 4px - - </style> - <script> - this.data = { - compact: false - }; - - this.mixin('widget'); - - this.post = null; - this.loading = true; - - this.offset = 0; - - this.on('mount', () => { - this.fetch(); - }); - - this.fetch = () => { - this.update({ - loading: true, - post: null - }); - this.api('posts/trend', { - limit: 1, - offset: this.offset, - repost: false, - reply: false, - media: false, - poll: false - }).then(posts => { - const post = posts ? posts[0] : null; - if (post == null) { - this.offset = 0; - } else { - this.offset++; - } - this.update({ - loading: false, - post: post - }); - }); - }; - - this.func = () => { - this.data.compact = !this.data.compact; - this.save(); - }; - </script> -</mk-trends-home-widget> diff --git a/src/web/app/desktop/tags/home-widgets/user-recommendation.tag b/src/web/app/desktop/tags/home-widgets/user-recommendation.tag deleted file mode 100644 index a1af7a5c49..0000000000 --- a/src/web/app/desktop/tags/home-widgets/user-recommendation.tag +++ /dev/null @@ -1,165 +0,0 @@ -<mk-user-recommendation-home-widget> - <virtual if={ !data.compact }> - <p class="title">%fa:users%%i18n:desktop.tags.mk-user-recommendation-home-widget.title%</p> - <button onclick={ refresh } title="%i18n:desktop.tags.mk-user-recommendation-home-widget.refresh%">%fa:sync%</button> - </virtual> - <div class="user" if={ !loading && users.length != 0 } each={ _user in users }> - <a class="avatar-anchor" href={ '/' + _user.username }> - <img class="avatar" src={ _user.avatar_url + '?thumbnail&size=42' } alt="" data-user-preview={ _user.id }/> - </a> - <div class="body"> - <a class="name" href={ '/' + _user.username } data-user-preview={ _user.id }>{ _user.name }</a> - <p class="username">@{ _user.username }</p> - </div> - <mk-follow-button user={ _user }/> - </div> - <p class="empty" if={ !loading && users.length == 0 }>%i18n:desktop.tags.mk-user-recommendation-home-widget.no-one%</p> - <p class="loading" if={ loading }>%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p> - <style> - :scope - display block - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - > .title - margin 0 - padding 0 16px - line-height 42px - font-size 0.9em - font-weight bold - color #888 - border-bottom solid 1px #eee - - > [data-fa] - margin-right 4px - - > button - position absolute - z-index 2 - top 0 - right 0 - padding 0 - width 42px - font-size 0.9em - line-height 42px - color #ccc - - &:hover - color #aaa - - &:active - color #999 - - > .user - padding 16px - border-bottom solid 1px #eee - - &:last-child - border-bottom none - - &:after - content "" - display block - clear both - - > .avatar-anchor - display block - float left - margin 0 12px 0 0 - - > .avatar - display block - width 42px - height 42px - margin 0 - border-radius 8px - vertical-align bottom - - > .body - float left - width calc(100% - 54px) - - > .name - margin 0 - font-size 16px - line-height 24px - color #555 - - > .username - display block - margin 0 - font-size 15px - line-height 16px - color #ccc - - > mk-follow-button - position absolute - top 16px - right 16px - - > .empty - margin 0 - padding 16px - text-align center - color #aaa - - > .loading - margin 0 - padding 16px - text-align center - color #aaa - - > [data-fa] - margin-right 4px - - </style> - <script> - this.data = { - compact: false - }; - - this.mixin('widget'); - this.mixin('user-preview'); - - this.users = null; - this.loading = true; - - this.limit = 3; - this.page = 0; - - this.on('mount', () => { - this.fetch(); - }); - - this.fetch = () => { - this.update({ - loading: true, - users: null - }); - this.api('users/recommendation', { - limit: this.limit, - offset: this.limit * this.page - }).then(users => { - this.update({ - loading: false, - users: users - }); - }); - }; - - this.refresh = () => { - if (this.users.length < this.limit) { - this.page = 0; - } else { - this.page++; - } - this.fetch(); - }; - - this.func = () => { - this.data.compact = !this.data.compact; - this.save(); - }; - </script> -</mk-user-recommendation-home-widget> diff --git a/src/web/app/desktop/tags/home-widgets/version.tag b/src/web/app/desktop/tags/home-widgets/version.tag deleted file mode 100644 index 2b66b0490e..0000000000 --- a/src/web/app/desktop/tags/home-widgets/version.tag +++ /dev/null @@ -1,20 +0,0 @@ -<mk-version-home-widget> - <p>ver { _VERSION_ } (葵 aoi)</p> - <style> - :scope - display block - overflow visible !important - - > p - display block - margin 0 - padding 0 12px - text-align center - font-size 0.7em - color #aaa - - </style> - <script> - this.mixin('widget'); - </script> -</mk-version-home-widget> diff --git a/src/web/app/desktop/tags/home.tag b/src/web/app/desktop/tags/home.tag deleted file mode 100644 index 50f6c84604..0000000000 --- a/src/web/app/desktop/tags/home.tag +++ /dev/null @@ -1,388 +0,0 @@ -<mk-home data-customize={ opts.customize }> - <div class="customize" if={ opts.customize }> - <a href="/">%fa:check%完了</a> - <div> - <div class="adder"> - <p>ウィジェットを追加:</p> - <select ref="widgetSelector"> - <option value="profile">プロフィール</option> - <option value="calendar">カレンダー</option> - <option value="timemachine">カレンダー(タイムマシン)</option> - <option value="activity">アクティビティ</option> - <option value="rss-reader">RSSリーダー</option> - <option value="trends">トレンド</option> - <option value="photo-stream">フォトストリーム</option> - <option value="slideshow">スライドショー</option> - <option value="version">バージョン</option> - <option value="broadcast">ブロードキャスト</option> - <option value="notifications">通知</option> - <option value="user-recommendation">おすすめユーザー</option> - <option value="recommended-polls">投票</option> - <option value="post-form">投稿フォーム</option> - <option value="messaging">メッセージ</option> - <option value="channel">チャンネル</option> - <option value="access-log">アクセスログ</option> - <option value="server">サーバー情報</option> - <option value="donation">寄付のお願い</option> - <option value="nav">ナビゲーション</option> - <option value="tips">ヒント</option> - </select> - <button onclick={ addWidget }>追加</button> - </div> - <div class="trash"> - <div ref="trash"></div> - <p>ゴミ箱</p> - </div> - </div> - </div> - <div class="main"> - <div class="left"> - <div ref="left" data-place="left"></div> - </div> - <main ref="main"> - <div class="maintop" ref="maintop" data-place="main" if={ opts.customize }></div> - <mk-timeline-home-widget ref="tl" if={ mode == 'timeline' }/> - <mk-mentions-home-widget ref="tl" if={ mode == 'mentions' }/> - </main> - <div class="right"> - <div ref="right" data-place="right"></div> - </div> - </div> - <style> - :scope - display block - - &[data-customize] - padding-top 48px - background-image url('/assets/desktop/grid.svg') - - > .main > main > *:not(.maintop) - cursor not-allowed - - > * - pointer-events none - - &:not([data-customize]) - > .main > *:empty - display none - - > .customize - position fixed - z-index 1000 - top 0 - left 0 - width 100% - height 48px - background #f7f7f7 - box-shadow 0 1px 1px rgba(0, 0, 0, 0.075) - - > a - display block - position absolute - z-index 1001 - top 0 - right 0 - padding 0 16px - line-height 48px - text-decoration none - color $theme-color-foreground - background $theme-color - transition background 0.1s ease - - &:hover - background lighten($theme-color, 10%) - - &:active - background darken($theme-color, 10%) - transition background 0s ease - - > [data-fa] - margin-right 8px - - > div - display flex - margin 0 auto - max-width 1200px - 32px - - > div - width 50% - - &.adder - > p - display inline - line-height 48px - - &.trash - border-left solid 1px #ddd - - > div - width 100% - height 100% - - > p - position absolute - top 0 - left 0 - width 100% - line-height 48px - margin 0 - text-align center - pointer-events none - - > .main - display flex - justify-content center - margin 0 auto - max-width 1200px - - > * - .customize-container - cursor move - - > * - pointer-events none - - > main - padding 16px - width calc(100% - 275px * 2) - - > *:not(.maintop):not(:last-child) - > .maintop > *:not(:last-child) - margin-bottom 16px - - > .maintop - min-height 64px - margin-bottom 16px - - > *:not(main) - width 275px - - > * - padding 16px 0 16px 0 - - > *:not(:last-child) - margin-bottom 16px - - > .left - padding-left 16px - - > .right - padding-right 16px - - @media (max-width 1100px) - > *:not(main) - display none - - > main - float none - width 100% - max-width 700px - margin 0 auto - - </style> - <script> - import uuid from 'uuid'; - import Sortable from 'sortablejs'; - import dialog from '../scripts/dialog'; - import ScrollFollower from '../scripts/scroll-follower'; - - this.mixin('i'); - this.mixin('api'); - - this.mode = this.opts.mode || 'timeline'; - - this.home = []; - - this.bakeHomeData = () => JSON.stringify(this.I.client_settings.home); - this.bakedHomeData = this.bakeHomeData(); - - this.on('mount', () => { - this.refs.tl.on('loaded', () => { - this.trigger('loaded'); - }); - - this.I.on('refreshed', this.onMeRefreshed); - - this.I.client_settings.home.forEach(widget => { - try { - this.setWidget(widget); - } catch (e) { - // noop - } - }); - - if (!this.opts.customize) { - if (this.refs.left.children.length == 0) { - this.refs.left.parentNode.removeChild(this.refs.left); - } - if (this.refs.right.children.length == 0) { - this.refs.right.parentNode.removeChild(this.refs.right); - } - } - - if (this.opts.customize) { - dialog('%fa:info-circle%カスタマイズのヒント', - '<p>ホームのカスタマイズでは、ウィジェットを追加/削除したり、ドラッグ&ドロップして並べ替えたりすることができます。</p>' + - '<p>一部のウィジェットは、<strong><strong>右</strong>クリック</strong>することで表示を変更することができます。</p>' + - '<p>ウィジェットを削除するには、ヘッダーの<strong>「ゴミ箱」</strong>と書かれたエリアにウィジェットをドラッグ&ドロップします。</p>' + - '<p>カスタマイズを終了するには、右上の「完了」をクリックします。</p>', - [{ - text: 'Got it!' - }]); - - const sortableOption = { - group: 'kyoppie', - animation: 150, - onMove: evt => { - const id = evt.dragged.getAttribute('data-widget-id'); - this.home.find(tag => tag.id == id).update({ place: evt.to.getAttribute('data-place') }); - }, - onSort: () => { - this.saveHome(); - } - }; - - new Sortable(this.refs.left, sortableOption); - new Sortable(this.refs.right, sortableOption); - new Sortable(this.refs.maintop, sortableOption); - new Sortable(this.refs.trash, Object.assign({}, sortableOption, { - onAdd: evt => { - const el = evt.item; - const id = el.getAttribute('data-widget-id'); - el.parentNode.removeChild(el); - this.I.client_settings.home = this.I.client_settings.home.filter(w => w.id != id); - this.saveHome(); - } - })); - } - - if (!this.opts.customize) { - this.scrollFollowerLeft = this.refs.left.parentNode ? new ScrollFollower(this.refs.left, this.root.getBoundingClientRect().top) : null; - this.scrollFollowerRight = this.refs.right.parentNode ? new ScrollFollower(this.refs.right, this.root.getBoundingClientRect().top) : null; - } - }); - - this.on('unmount', () => { - this.I.off('refreshed', this.onMeRefreshed); - - this.home.forEach(widget => { - widget.unmount(); - }); - - if (!this.opts.customize) { - if (this.scrollFollowerLeft) this.scrollFollowerLeft.dispose(); - if (this.scrollFollowerRight) this.scrollFollowerRight.dispose(); - } - }); - - this.onMeRefreshed = () => { - if (this.bakedHomeData != this.bakeHomeData()) { - alert('別の場所でホームが編集されました。ページを再度読み込みすると編集が反映されます。'); - } - }; - - this.setWidget = (widget, prepend = false) => { - const el = document.createElement(`mk-${widget.name}-home-widget`); - - let actualEl; - - if (this.opts.customize) { - const container = document.createElement('div'); - container.classList.add('customize-container'); - container.setAttribute('data-widget-id', widget.id); - container.appendChild(el); - actualEl = container; - } else { - actualEl = el; - } - - switch (widget.place) { - case 'left': - if (prepend) { - this.refs.left.insertBefore(actualEl, this.refs.left.firstChild); - } else { - this.refs.left.appendChild(actualEl); - } - break; - case 'right': - if (prepend) { - this.refs.right.insertBefore(actualEl, this.refs.right.firstChild); - } else { - this.refs.right.appendChild(actualEl); - } - break; - case 'main': - if (this.opts.customize) { - this.refs.maintop.appendChild(actualEl); - } else { - this.refs.main.insertBefore(actualEl, this.refs.tl.root); - } - break; - } - - const tag = riot.mount(el, { - id: widget.id, - data: widget.data, - place: widget.place, - tl: this.refs.tl - })[0]; - - this.home.push(tag); - - if (this.opts.customize) { - actualEl.oncontextmenu = e => { - e.preventDefault(); - e.stopImmediatePropagation(); - if (tag.func) tag.func(); - return false; - }; - } - }; - - this.addWidget = () => { - const widget = { - name: this.refs.widgetSelector.options[this.refs.widgetSelector.selectedIndex].value, - id: uuid(), - place: 'left', - data: {} - }; - - this.I.client_settings.home.unshift(widget); - - this.setWidget(widget, true); - - this.saveHome(); - }; - - this.saveHome = () => { - const data = []; - - Array.from(this.refs.left.children).forEach(el => { - const id = el.getAttribute('data-widget-id'); - const widget = this.I.client_settings.home.find(w => w.id == id); - widget.place = 'left'; - data.push(widget); - }); - - Array.from(this.refs.right.children).forEach(el => { - const id = el.getAttribute('data-widget-id'); - const widget = this.I.client_settings.home.find(w => w.id == id); - widget.place = 'right'; - data.push(widget); - }); - - Array.from(this.refs.maintop.children).forEach(el => { - const id = el.getAttribute('data-widget-id'); - const widget = this.I.client_settings.home.find(w => w.id == id); - widget.place = 'main'; - data.push(widget); - }); - - this.api('i/update_home', { - home: data - }).then(() => { - this.I.update(); - }); - }; - </script> -</mk-home> diff --git a/src/web/app/desktop/tags/image-dialog.tag b/src/web/app/desktop/tags/image-dialog.tag deleted file mode 100644 index 39d16ca139..0000000000 --- a/src/web/app/desktop/tags/image-dialog.tag +++ /dev/null @@ -1,61 +0,0 @@ -<mk-image-dialog> - <div class="bg" ref="bg" onclick={ close }></div><img ref="img" src={ image.url } alt={ image.name } title={ image.name } onclick={ close }/> - <style> - :scope - display block - position fixed - z-index 2048 - top 0 - left 0 - width 100% - height 100% - opacity 0 - - > .bg - display block - position fixed - z-index 1 - top 0 - left 0 - width 100% - height 100% - background rgba(0, 0, 0, 0.7) - - > img - position fixed - z-index 2 - top 0 - right 0 - bottom 0 - left 0 - max-width 100% - max-height 100% - margin auto - cursor zoom-out - - </style> - <script> - import anime from 'animejs'; - - this.image = this.opts.image; - - this.on('mount', () => { - anime({ - targets: this.root, - opacity: 1, - duration: 100, - easing: 'linear' - }); - }); - - this.close = () => { - anime({ - targets: this.root, - opacity: 0, - duration: 100, - easing: 'linear', - complete: () => this.unmount() - }); - }; - </script> -</mk-image-dialog> diff --git a/src/web/app/desktop/tags/images-viewer.tag b/src/web/app/desktop/tags/images-viewer.tag deleted file mode 100644 index 44a61cb747..0000000000 --- a/src/web/app/desktop/tags/images-viewer.tag +++ /dev/null @@ -1,45 +0,0 @@ -<mk-images-viewer> - <div class="image" ref="view" onmousemove={ mousemove } style={ 'background-image: url(' + image.url + '?thumbnail' } onclick={ click }><img src={ image.url + '?thumbnail&size=512' } alt={ image.name } title={ image.name }/></div> - <style> - :scope - display block - overflow hidden - border-radius 4px - - > .image - cursor zoom-in - - > img - display block - max-height 256px - max-width 100% - margin 0 auto - - &:hover - > img - visibility hidden - - &:not(:hover) - background-image none !important - - </style> - <script> - this.images = this.opts.images; - this.image = this.images[0]; - - this.mousemove = e => { - const rect = this.refs.view.getBoundingClientRect(); - const mouseX = e.clientX - rect.left; - const mouseY = e.clientY - rect.top; - const xp = mouseX / this.refs.view.offsetWidth * 100; - const yp = mouseY / this.refs.view.offsetHeight * 100; - this.refs.view.style.backgroundPosition = xp + '% ' + yp + '%'; - }; - - this.click = () => { - riot.mount(document.body.appendChild(document.createElement('mk-image-dialog')), { - image: this.image - }); - }; - </script> -</mk-images-viewer> diff --git a/src/web/app/desktop/tags/index.ts b/src/web/app/desktop/tags/index.ts deleted file mode 100644 index 3ec1d108aa..0000000000 --- a/src/web/app/desktop/tags/index.ts +++ /dev/null @@ -1,90 +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('./home-widgets/access-log.tag'); -require('./home-widgets/messaging.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-timeline.tag'); -require('./user.tag'); -require('./big-follow-button.tag'); -require('./pages/entrance.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'); -require('./widgets/calendar.tag'); -require('./widgets/activity.tag'); diff --git a/src/web/app/desktop/tags/input-dialog.tag b/src/web/app/desktop/tags/input-dialog.tag deleted file mode 100644 index f175277547..0000000000 --- a/src/web/app/desktop/tags/input-dialog.tag +++ /dev/null @@ -1,172 +0,0 @@ -<mk-input-dialog> - <mk-window ref="window" is-modal={ true } width={ '500px' }> - <yield to="header"> - %fa:i-cursor%{ parent.title } - </yield> - <yield to="content"> - <div class="body"> - <input ref="text" type={ parent.type } oninput={ parent.onInput } onkeydown={ parent.onKeydown } placeholder={ parent.placeholder }/> - </div> - <div class="action"> - <button class="cancel" onclick={ parent.cancel }>キャンセル</button> - <button class="ok" disabled={ !parent.allowEmpty && refs.text.value.length == 0 } onclick={ parent.ok }>決定</button> - </div> - </yield> - </mk-window> - <style> - :scope - display block - - > mk-window - [data-yield='header'] - > [data-fa] - margin-right 4px - - [data-yield='content'] - > .body - padding 16px - - > input - display block - padding 8px - margin 0 - width 100% - max-width 100% - min-width 100% - font-size 1em - color #333 - background #fff - outline none - border solid 1px rgba($theme-color, 0.1) - border-radius 4px - transition border-color .3s ease - - &:hover - border-color rgba($theme-color, 0.2) - transition border-color .1s ease - - &:focus - color $theme-color - border-color rgba($theme-color, 0.5) - transition border-color 0s ease - - &::-webkit-input-placeholder - color rgba($theme-color, 0.3) - - > .action - height 72px - background lighten($theme-color, 95%) - - .ok - .cancel - display block - position absolute - bottom 16px - cursor pointer - padding 0 - margin 0 - width 120px - height 40px - font-size 1em - outline none - border-radius 4px - - &:focus - &:after - content "" - pointer-events none - position absolute - top -5px - right -5px - bottom -5px - left -5px - border 2px solid rgba($theme-color, 0.3) - border-radius 8px - - &:disabled - opacity 0.7 - cursor default - - .ok - right 16px - color $theme-color-foreground - background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%) - border solid 1px lighten($theme-color, 15%) - - &:not(:disabled) - font-weight bold - - &:hover:not(:disabled) - background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%) - border-color $theme-color - - &:active:not(:disabled) - background $theme-color - border-color $theme-color - - .cancel - right 148px - color #888 - background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%) - border solid 1px #e2e2e2 - - &:hover - background linear-gradient(to bottom, #f9f9f9 0%, #ececec 100%) - border-color #dcdcdc - - &:active - background #ececec - border-color #dcdcdc - - </style> - <script> - this.done = false; - - this.title = this.opts.title; - this.placeholder = this.opts.placeholder; - this.default = this.opts.default; - this.allowEmpty = this.opts.allowEmpty != null ? this.opts.allowEmpty : true; - this.type = this.opts.type ? this.opts.type : 'text'; - - this.on('mount', () => { - this.text = this.refs.window.refs.text; - if (this.default) this.text.value = this.default; - this.text.focus(); - - this.refs.window.on('closing', () => { - if (this.done) { - this.opts.onOk(this.text.value); - } else { - if (this.opts.onCancel) this.opts.onCancel(); - } - }); - - this.refs.window.on('closed', () => { - this.unmount(); - }); - }); - - this.cancel = () => { - this.done = false; - this.refs.window.close(); - }; - - this.ok = () => { - if (!this.allowEmpty && this.text.value == '') return; - this.done = true; - this.refs.window.close(); - }; - - this.onInput = () => { - this.update(); - }; - - this.onKeydown = e => { - if (e.which == 13) { // Enter - e.preventDefault(); - e.stopPropagation(); - this.ok(); - } - }; - </script> -</mk-input-dialog> diff --git a/src/web/app/desktop/tags/list-user.tag b/src/web/app/desktop/tags/list-user.tag deleted file mode 100644 index 91a6de0a0d..0000000000 --- a/src/web/app/desktop/tags/list-user.tag +++ /dev/null @@ -1,93 +0,0 @@ -<mk-list-user> - <a class="avatar-anchor" href={ '/' + user.username }> - <img class="avatar" src={ user.avatar_url + '?thumbnail&size=64' } alt="avatar"/> - </a> - <div class="main"> - <header> - <a class="name" href={ '/' + user.username }>{ user.name }</a> - <span class="username">@{ user.username }</span> - </header> - <div class="body"> - <p class="followed" if={ user.is_followed }>フォローされています</p> - <div class="description">{ user.description }</div> - </div> - </div> - <mk-follow-button user={ user }/> - <style> - :scope - display block - margin 0 - padding 16px - font-size 16px - - &:after - content "" - display block - clear both - - > .avatar-anchor - display block - float left - margin 0 16px 0 0 - - > .avatar - display block - width 58px - height 58px - margin 0 - border-radius 8px - vertical-align bottom - - > .main - float left - width calc(100% - 74px) - - > header - margin-bottom 2px - - > .name - display inline - margin 0 - padding 0 - color #777 - font-size 1em - font-weight 700 - text-align left - text-decoration none - - &:hover - text-decoration underline - - > .username - text-align left - margin 0 0 0 8px - color #ccc - - > .body - > .followed - display inline-block - margin 0 0 4px 0 - padding 2px 8px - vertical-align top - font-size 10px - color #71afc7 - background #eefaff - border-radius 4px - - > .description - cursor default - display block - margin 0 - padding 0 - overflow-wrap break-word - font-size 1.1em - color #717171 - - > mk-follow-button - position absolute - top 16px - right 16px - - </style> - <script>this.user = this.opts.user</script> -</mk-list-user> diff --git a/src/web/app/desktop/tags/messaging/room-window.tag b/src/web/app/desktop/tags/messaging/room-window.tag deleted file mode 100644 index 7c0bb0d76e..0000000000 --- a/src/web/app/desktop/tags/messaging/room-window.tag +++ /dev/null @@ -1,32 +0,0 @@ -<mk-messaging-room-window> - <mk-window ref="window" is-modal={ false } width={ '500px' } height={ '560px' } popout={ popout }> - <yield to="header">%fa:comments%メッセージ: { parent.user.name }</yield> - <yield to="content"> - <mk-messaging-room user={ parent.user }/> - </yield> - </mk-window> - <style> - :scope - > mk-window - [data-yield='header'] - > [data-fa] - margin-right 4px - - [data-yield='content'] - > mk-messaging-room - height 100% - overflow auto - - </style> - <script> - this.user = this.opts.user; - - this.popout = `${_URL_}/i/messaging/${this.user.username}`; - - this.on('mount', () => { - this.refs.window.on('closed', () => { - this.unmount(); - }); - }); - </script> -</mk-messaging-room-window> diff --git a/src/web/app/desktop/tags/messaging/window.tag b/src/web/app/desktop/tags/messaging/window.tag deleted file mode 100644 index 529db11af1..0000000000 --- a/src/web/app/desktop/tags/messaging/window.tag +++ /dev/null @@ -1,34 +0,0 @@ -<mk-messaging-window> - <mk-window ref="window" is-modal={ false } width={ '500px' } height={ '560px' }> - <yield to="header">%fa:comments%メッセージ</yield> - <yield to="content"> - <mk-messaging ref="index"/> - </yield> - </mk-window> - <style> - :scope - > mk-window - [data-yield='header'] - > [data-fa] - margin-right 4px - - [data-yield='content'] - > mk-messaging - height 100% - overflow auto - - </style> - <script> - this.on('mount', () => { - this.refs.window.on('closed', () => { - this.unmount(); - }); - - this.refs.window.refs.index.on('navigate-user', user => { - riot.mount(document.body.appendChild(document.createElement('mk-messaging-room-window')), { - user: user - }); - }); - }); - </script> -</mk-messaging-window> diff --git a/src/web/app/desktop/tags/notifications.tag b/src/web/app/desktop/tags/notifications.tag deleted file mode 100644 index 3218c00f6a..0000000000 --- a/src/web/app/desktop/tags/notifications.tag +++ /dev/null @@ -1,301 +0,0 @@ -<mk-notifications> - <div class="notifications" if={ notifications.length != 0 }> - <virtual each={ notification, i in notifications }> - <div class="notification { notification.type }"> - <mk-time time={ notification.created_at }/> - <virtual if={ notification.type == 'reaction' }> - <a class="avatar-anchor" href={ '/' + notification.user.username } data-user-preview={ notification.user.id }> - <img class="avatar" src={ notification.user.avatar_url + '?thumbnail&size=48' } alt="avatar"/> - </a> - <div class="text"> - <p><mk-reaction-icon reaction={ notification.reaction }/><a href={ '/' + notification.user.username } data-user-preview={ notification.user.id }>{ notification.user.name }</a></p> - <a class="post-ref" href={ '/' + notification.post.user.username + '/' + notification.post.id }> - %fa:quote-left%{ getPostSummary(notification.post) }%fa:quote-right% - </a> - </div> - </virtual> - <virtual if={ notification.type == 'repost' }> - <a class="avatar-anchor" href={ '/' + notification.post.user.username } data-user-preview={ notification.post.user_id }> - <img class="avatar" src={ notification.post.user.avatar_url + '?thumbnail&size=48' } alt="avatar"/> - </a> - <div class="text"> - <p>%fa:retweet%<a href={ '/' + notification.post.user.username } data-user-preview={ notification.post.user_id }>{ notification.post.user.name }</a></p> - <a class="post-ref" href={ '/' + notification.post.user.username + '/' + notification.post.id }> - %fa:quote-left%{ getPostSummary(notification.post.repost) }%fa:quote-right% - </a> - </div> - </virtual> - <virtual if={ notification.type == 'quote' }> - <a class="avatar-anchor" href={ '/' + notification.post.user.username } data-user-preview={ notification.post.user_id }> - <img class="avatar" src={ notification.post.user.avatar_url + '?thumbnail&size=48' } alt="avatar"/> - </a> - <div class="text"> - <p>%fa:quote-left%<a href={ '/' + notification.post.user.username } data-user-preview={ notification.post.user_id }>{ notification.post.user.name }</a></p> - <a class="post-preview" href={ '/' + notification.post.user.username + '/' + notification.post.id }>{ getPostSummary(notification.post) }</a> - </div> - </virtual> - <virtual if={ notification.type == 'follow' }> - <a class="avatar-anchor" href={ '/' + notification.user.username } data-user-preview={ notification.user.id }> - <img class="avatar" src={ notification.user.avatar_url + '?thumbnail&size=48' } alt="avatar"/> - </a> - <div class="text"> - <p>%fa:user-plus%<a href={ '/' + notification.user.username } data-user-preview={ notification.user.id }>{ notification.user.name }</a></p> - </div> - </virtual> - <virtual if={ notification.type == 'reply' }> - <a class="avatar-anchor" href={ '/' + notification.post.user.username } data-user-preview={ notification.post.user_id }> - <img class="avatar" src={ notification.post.user.avatar_url + '?thumbnail&size=48' } alt="avatar"/> - </a> - <div class="text"> - <p>%fa:reply%<a href={ '/' + notification.post.user.username } data-user-preview={ notification.post.user_id }>{ notification.post.user.name }</a></p> - <a class="post-preview" href={ '/' + notification.post.user.username + '/' + notification.post.id }>{ getPostSummary(notification.post) }</a> - </div> - </virtual> - <virtual if={ notification.type == 'mention' }> - <a class="avatar-anchor" href={ '/' + notification.post.user.username } data-user-preview={ notification.post.user_id }> - <img class="avatar" src={ notification.post.user.avatar_url + '?thumbnail&size=48' } alt="avatar"/> - </a> - <div class="text"> - <p>%fa:at%<a href={ '/' + notification.post.user.username } data-user-preview={ notification.post.user_id }>{ notification.post.user.name }</a></p> - <a class="post-preview" href={ '/' + notification.post.user.username + '/' + notification.post.id }>{ getPostSummary(notification.post) }</a> - </div> - </virtual> - <virtual if={ notification.type == 'poll_vote' }> - <a class="avatar-anchor" href={ '/' + notification.user.username } data-user-preview={ notification.user.id }> - <img class="avatar" src={ notification.user.avatar_url + '?thumbnail&size=48' } alt="avatar"/> - </a> - <div class="text"> - <p>%fa:chart-pie%<a href={ '/' + notification.user.username } data-user-preview={ notification.user.id }>{ notification.user.name }</a></p> - <a class="post-ref" href={ '/' + notification.post.user.username + '/' + notification.post.id }> - %fa:quote-left%{ getPostSummary(notification.post) }%fa:quote-right% - </a> - </div> - </virtual> - </div> - <p class="date" if={ i != notifications.length - 1 && notification._date != notifications[i + 1]._date }> - <span>%fa:angle-up%{ notification._datetext }</span> - <span>%fa:angle-down%{ notifications[i + 1]._datetext }</span> - </p> - </virtual> - </div> - <button class="more { fetching: fetchingMoreNotifications }" if={ moreNotifications } onclick={ fetchMoreNotifications } disabled={ fetchingMoreNotifications }> - <virtual if={ fetchingMoreNotifications }>%fa:spinner .pulse .fw%</virtual>{ fetchingMoreNotifications ? '%i18n:common.loading%' : '%i18n:desktop.tags.mk-notifications.more%' } - </button> - <p class="empty" if={ notifications.length == 0 && !loading }>ありません!</p> - <p class="loading" if={ loading }>%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p> - <style> - :scope - display block - - > .notifications - > .notification - margin 0 - padding 16px - overflow-wrap break-word - font-size 0.9em - border-bottom solid 1px rgba(0, 0, 0, 0.05) - - &:last-child - border-bottom none - - > mk-time - display inline - position absolute - top 16px - right 12px - vertical-align top - color rgba(0, 0, 0, 0.6) - font-size small - - &:after - content "" - display block - clear both - - > .avatar-anchor - display block - float left - position -webkit-sticky - position sticky - top 16px - - > img - display block - min-width 36px - min-height 36px - max-width 36px - max-height 36px - border-radius 6px - - > .text - float right - width calc(100% - 36px) - padding-left 8px - - p - margin 0 - - i, mk-reaction-icon - margin-right 4px - - .post-preview - color rgba(0, 0, 0, 0.7) - - .post-ref - color rgba(0, 0, 0, 0.7) - - [data-fa] - font-size 1em - font-weight normal - font-style normal - display inline-block - margin-right 3px - - &.repost, &.quote - .text p i - color #77B255 - - &.follow - .text p i - color #53c7ce - - &.reply, &.mention - .text p i - color #555 - - > .date - display block - margin 0 - line-height 32px - text-align center - font-size 0.8em - color #aaa - background #fdfdfd - border-bottom solid 1px rgba(0, 0, 0, 0.05) - - span - margin 0 16px - - [data-fa] - margin-right 8px - - > .more - display block - width 100% - padding 16px - color #555 - border-top solid 1px rgba(0, 0, 0, 0.05) - - &:hover - background rgba(0, 0, 0, 0.025) - - &:active - background rgba(0, 0, 0, 0.05) - - &.fetching - cursor wait - - > [data-fa] - margin-right 4px - - > .empty - margin 0 - padding 16px - text-align center - color #aaa - - > .loading - margin 0 - padding 16px - text-align center - color #aaa - - > [data-fa] - margin-right 4px - - </style> - <script> - import getPostSummary from '../../../../common/get-post-summary.ts'; - this.getPostSummary = getPostSummary; - - this.mixin('i'); - this.mixin('api'); - this.mixin('user-preview'); - - this.mixin('stream'); - this.connection = this.stream.getConnection(); - this.connectionId = this.stream.use(); - - this.notifications = []; - this.loading = true; - - this.on('mount', () => { - const max = 10; - - this.api('i/notifications', { - limit: max + 1 - }).then(notifications => { - if (notifications.length == max + 1) { - this.moreNotifications = true; - notifications.pop(); - } - - this.update({ - loading: false, - notifications: notifications - }); - }); - - this.connection.on('notification', this.onNotification); - }); - - this.on('unmount', () => { - this.connection.off('notification', this.onNotification); - this.stream.dispose(this.connectionId); - }); - - this.on('update', () => { - this.notifications.forEach(notification => { - const date = new Date(notification.created_at).getDate(); - const month = new Date(notification.created_at).getMonth() + 1; - notification._date = date; - notification._datetext = `${month}月 ${date}日`; - }); - }); - - this.onNotification = notification => { - // TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない - this.connection.send({ - type: 'read_notification', - id: notification.id - }); - - this.notifications.unshift(notification); - this.update(); - }; - - this.fetchMoreNotifications = () => { - this.update({ - fetchingMoreNotifications: true - }); - - const max = 30; - - this.api('i/notifications', { - limit: max + 1, - max_id: this.notifications[this.notifications.length - 1].id - }).then(notifications => { - if (notifications.length == max + 1) { - this.moreNotifications = true; - notifications.pop(); - } else { - this.moreNotifications = false; - } - this.update({ - notifications: this.notifications.concat(notifications), - fetchingMoreNotifications: false - }); - }); - }; - </script> -</mk-notifications> diff --git a/src/web/app/desktop/tags/pages/drive.tag b/src/web/app/desktop/tags/pages/drive.tag deleted file mode 100644 index 9f3e75ab21..0000000000 --- a/src/web/app/desktop/tags/pages/drive.tag +++ /dev/null @@ -1,37 +0,0 @@ -<mk-drive-page> - <mk-drive-browser ref="browser" folder={ opts.folder }/> - <style> - :scope - display block - position fixed - width 100% - height 100% - background #fff - - > mk-drive-browser - height 100% - </style> - <script> - this.on('mount', () => { - document.title = 'Misskey Drive'; - - this.refs.browser.on('move-root', () => { - const title = 'Misskey Drive'; - - // Rewrite URL - history.pushState(null, title, '/i/drive'); - - document.title = title; - }); - - this.refs.browser.on('open-folder', folder => { - const title = folder.name + ' | Misskey Drive'; - - // Rewrite URL - history.pushState(null, title, '/i/drive/folder/' + folder.id); - - document.title = title; - }); - }); - </script> -</mk-drive-page> diff --git a/src/web/app/desktop/tags/pages/entrance.tag b/src/web/app/desktop/tags/pages/entrance.tag deleted file mode 100644 index 44548e4183..0000000000 --- a/src/web/app/desktop/tags/pages/entrance.tag +++ /dev/null @@ -1,342 +0,0 @@ -<mk-entrance> - <main> - <div> - <h1>どこにいても、ここにあります</h1> - <p>ようこそ! MisskeyはTwitter風ミニブログSNSです――思ったこと、共有したいことをシンプルに書き残せます。タイムラインを見れば、皆の反応や皆がどう思っているのかもすぐにわかります。</p> - <p if={ stats }>これまでに{ stats.posts_count }投稿されました</p> - </div> - <div> - <mk-entrance-signin if={ mode == 'signin' }/> - <mk-entrance-signup if={ mode == 'signup' }/> - <div class="introduction" if={ mode == 'introduction' }> - <mk-introduction/> - <button onclick={ signin }>わかった</button> - </div> - </div> - </main> - <mk-forkit/> - <footer> - <div> - <mk-nav-links/> - <mk-copyright/> - </div> - </footer> - <!-- ↓ https://github.com/riot/riot/issues/2134 (将来的)--> - <style data-disable-scope="data-disable-scope"> - #wait { - right: auto; - left: 15px; - } - </style> - <style> - :scope - $width = 1000px - - display block - - &:before - content "" - display block - position fixed - width 100% - height 100% - background rgba(0, 0, 0, 0.3) - - > main - display block - max-width $width - margin 0 auto - padding 64px 0 0 0 - padding-bottom 16px - - &:after - content "" - display block - clear both - - > div:first-child - position absolute - top 64px - left 0 - width calc(100% - 500px) - color #fff - text-shadow 0 0 32px rgba(0, 0, 0, 0.5) - font-weight bold - - > p:last-child - padding 1em 0 0 0 - border-top solid 1px #fff - - > div:last-child - float right - - > .introduction - max-width 360px - margin 0 auto - color #777 - - > mk-introduction - padding 32px - background #fff - box-shadow 0 4px 16px rgba(0, 0, 0, 0.2) - - > button - display block - margin 16px auto 0 auto - color #666 - - &:hover - text-decoration underline - - > footer - * - color #fff !important - text-shadow 0 0 8px #000 - font-weight bold - - > div - max-width $width - margin 0 auto - padding 16px 0 - text-align center - border-top solid 1px #fff - - > mk-copyright - margin 0 - line-height 64px - font-size 10px - - </style> - <script> - this.mixin('api'); - - this.mode = 'signin'; - - this.on('mount', () => { - document.documentElement.style.backgroundColor = '#444'; - - this.api('meta').then(meta => { - const img = meta.top_image ? meta.top_image : '/assets/desktop/index.jpg'; - document.documentElement.style.backgroundImage = `url("${ img }")`; - document.documentElement.style.backgroundSize = 'cover'; - document.documentElement.style.backgroundPosition = 'center'; - }); - - this.api('stats').then(stats => { - this.update({ - stats - }); - }); - }); - - this.signup = () => { - this.update({ - mode: 'signup' - }); - }; - - this.signin = () => { - this.update({ - mode: 'signin' - }); - }; - - this.introduction = () => { - this.update({ - mode: 'introduction' - }); - }; - </script> -</mk-entrance> - -<mk-entrance-signin> - <a class="help" href={ _ABOUT_URL_ + '/help' } title="お困りですか?">%fa:question%</a> - <div class="form"> - <h1><img if={ user } src={ user.avatar_url + '?thumbnail&size=32' }/> - <p>{ user ? user.name : 'アカウント' }</p> - </h1> - <mk-signin ref="signin"/> - </div> - <a href={ _API_URL_ + '/signin/twitter' }>Twitterでサインイン</a> - <div class="divider"><span>or</span></div> - <button class="signup" onclick={ parent.signup }>新規登録</button><a class="introduction" onclick={ introduction }>Misskeyについて</a> - <style> - :scope - display block - width 290px - margin 0 auto - text-align center - - &:hover - > .help - opacity 1 - - > .help - cursor pointer - display block - position absolute - top 0 - right 0 - z-index 1 - margin 0 - padding 0 - font-size 1.2em - color #999 - border none - outline none - background transparent - opacity 0 - transition opacity 0.1s ease - - &:hover - color #444 - - &:active - color #222 - - > [data-fa] - padding 14px - - > .form - padding 10px 28px 16px 28px - background #fff - box-shadow 0px 4px 16px rgba(0, 0, 0, 0.2) - - > h1 - display block - margin 0 - padding 0 - height 54px - line-height 54px - text-align center - text-transform uppercase - font-size 1em - font-weight bold - color rgba(0, 0, 0, 0.5) - border-bottom solid 1px rgba(0, 0, 0, 0.1) - - > p - display inline - margin 0 - padding 0 - - > img - display inline-block - top 10px - width 32px - height 32px - margin-right 8px - border-radius 100% - - &[src=''] - display none - - > .divider - padding 16px 0 - text-align center - - &:before - &:after - content "" - display block - position absolute - top 50% - width 45% - height 1px - border-top solid 1px rgba(0, 0, 0, 0.1) - - &:before - left 0 - - &:after - right 0 - - > * - z-index 1 - padding 0 8px - color #fff - text-shadow 0 0 8px rgba(0, 0, 0, 0.5) - - > .signup - width 100% - line-height 56px - font-size 1em - color #fff - background $theme-color - border-radius 64px - - &:hover - background lighten($theme-color, 5%) - - &:active - background darken($theme-color, 5%) - - > .introduction - display inline-block - margin-top 16px - font-size 12px - color #666 - - </style> - <script> - this.on('mount', () => { - this.refs.signin.on('user', user => { - this.update({ - user: user - }); - }); - }); - - this.introduction = () => { - this.parent.introduction(); - }; - </script> -</mk-entrance-signin> - -<mk-entrance-signup> - <mk-signup/> - <button class="cancel" type="button" onclick={ parent.signin } title="キャンセル">%fa:times%</button> - <style> - :scope - display block - width 368px - margin 0 auto - - &:hover - > .cancel - opacity 1 - - > mk-signup - padding 18px 32px 0 32px - background #fff - box-shadow 0px 4px 16px rgba(0, 0, 0, 0.2) - - > .cancel - cursor pointer - display block - position absolute - top 0 - right 0 - z-index 1 - margin 0 - padding 0 - font-size 1.2em - color #999 - border none - outline none - box-shadow none - background transparent - opacity 0 - transition opacity 0.1s ease - - &:hover - color #555 - - &:active - color #222 - - > [data-fa] - padding 14px - - </style> -</mk-entrance-signup> diff --git a/src/web/app/desktop/tags/pages/home-customize.tag b/src/web/app/desktop/tags/pages/home-customize.tag deleted file mode 100644 index 457b8390e7..0000000000 --- a/src/web/app/desktop/tags/pages/home-customize.tag +++ /dev/null @@ -1,12 +0,0 @@ -<mk-home-customize-page> - <mk-home ref="home" mode="timeline" customize={ true }/> - <style> - :scope - display block - </style> - <script> - this.on('mount', () => { - document.title = 'Misskey - ホームのカスタマイズ'; - }); - </script> -</mk-home-customize-page> diff --git a/src/web/app/desktop/tags/pages/home.tag b/src/web/app/desktop/tags/pages/home.tag deleted file mode 100644 index 3c8f4ec570..0000000000 --- a/src/web/app/desktop/tags/pages/home.tag +++ /dev/null @@ -1,54 +0,0 @@ -<mk-home-page> - <mk-ui ref="ui" page={ page }> - <mk-home ref="home" mode={ parent.opts.mode }/> - </mk-ui> - <style> - :scope - display block - </style> - <script> - import Progress from '../../../common/scripts/loading'; - import getPostSummary from '../../../../../common/get-post-summary.ts'; - - this.mixin('i'); - this.mixin('api'); - - this.mixin('stream'); - this.connection = this.stream.getConnection(); - this.connectionId = this.stream.use(); - - this.unreadCount = 0; - this.page = this.opts.mode || 'timeline'; - - this.on('mount', () => { - this.refs.ui.refs.home.on('loaded', () => { - Progress.done(); - }); - document.title = 'Misskey'; - Progress.start(); - - this.connection.on('post', this.onStreamPost); - document.addEventListener('visibilitychange', this.windowOnVisibilitychange, false); - }); - - this.on('unmount', () => { - this.connection.off('post', this.onStreamPost); - this.stream.dispose(this.connectionId); - document.removeEventListener('visibilitychange', this.windowOnVisibilitychange); - }); - - this.onStreamPost = post => { - if (document.hidden && post.user_id != this.I.id) { - this.unreadCount++; - document.title = `(${this.unreadCount}) ${getPostSummary(post)}`; - } - }; - - this.windowOnVisibilitychange = () => { - if (!document.hidden) { - this.unreadCount = 0; - document.title = 'Misskey'; - } - }; - </script> -</mk-home-page> diff --git a/src/web/app/desktop/tags/pages/messaging-room.tag b/src/web/app/desktop/tags/pages/messaging-room.tag deleted file mode 100644 index 3c21b97501..0000000000 --- a/src/web/app/desktop/tags/pages/messaging-room.tag +++ /dev/null @@ -1,37 +0,0 @@ -<mk-messaging-room-page> - <mk-messaging-room if={ user } user={ user } is-naked={ true }/> - - <style> - :scope - display block - background #fff - - </style> - <script> - import Progress from '../../../common/scripts/loading'; - - this.mixin('api'); - - this.fetching = true; - this.user = null; - - this.on('mount', () => { - Progress.start(); - - document.documentElement.style.background = '#fff'; - - this.api('users/show', { - username: this.opts.user - }).then(user => { - this.update({ - fetching: false, - user: user - }); - - document.title = 'メッセージ: ' + this.user.name; - - Progress.done(); - }); - }); - </script> -</mk-messaging-room-page> diff --git a/src/web/app/desktop/tags/pages/not-found.tag b/src/web/app/desktop/tags/pages/not-found.tag deleted file mode 100644 index e62ea11008..0000000000 --- a/src/web/app/desktop/tags/pages/not-found.tag +++ /dev/null @@ -1,11 +0,0 @@ -<mk-not-found> - <mk-ui> - <main> - <h1>Not Found</h1> - </main> - </mk-ui> - <style> - :scope - display block - </style> -</mk-not-found> diff --git a/src/web/app/desktop/tags/pages/post.tag b/src/web/app/desktop/tags/pages/post.tag deleted file mode 100644 index 6d3b030e05..0000000000 --- a/src/web/app/desktop/tags/pages/post.tag +++ /dev/null @@ -1,58 +0,0 @@ -<mk-post-page> - <mk-ui ref="ui"> - <main if={ !parent.fetching }> - <a if={ parent.post.next } href={ parent.post.next }>%fa:angle-up%%i18n:desktop.tags.mk-post-page.next%</a> - <mk-post-detail ref="detail" post={ parent.post }/> - <a if={ parent.post.prev } href={ parent.post.prev }>%fa:angle-down%%i18n:desktop.tags.mk-post-page.prev%</a> - </main> - </mk-ui> - <style> - :scope - display block - - main - padding 16px - text-align center - - > a - display inline-block - - &:first-child - margin-bottom 4px - - &:last-child - margin-top 4px - - > [data-fa] - margin-right 4px - - > mk-post-detail - margin 0 auto - width 640px - - </style> - <script> - import Progress from '../../../common/scripts/loading'; - - this.mixin('api'); - - this.fetching = true; - this.post = null; - - this.on('mount', () => { - Progress.start(); - - this.api('posts/show', { - post_id: this.opts.post - }).then(post => { - - this.update({ - fetching: false, - post: post - }); - - Progress.done(); - }); - }); - </script> -</mk-post-page> diff --git a/src/web/app/desktop/tags/pages/search.tag b/src/web/app/desktop/tags/pages/search.tag deleted file mode 100644 index 4f5867bdb9..0000000000 --- a/src/web/app/desktop/tags/pages/search.tag +++ /dev/null @@ -1,20 +0,0 @@ -<mk-search-page> - <mk-ui ref="ui"> - <mk-search ref="search" query={ parent.opts.query }/> - </mk-ui> - <style> - :scope - display block - </style> - <script> - import Progress from '../../../common/scripts/loading'; - - this.on('mount', () => { - Progress.start(); - - this.refs.ui.refs.search.on('loaded', () => { - Progress.done(); - }); - }); - </script> -</mk-search-page> diff --git a/src/web/app/desktop/tags/pages/selectdrive.tag b/src/web/app/desktop/tags/pages/selectdrive.tag deleted file mode 100644 index 123977e905..0000000000 --- a/src/web/app/desktop/tags/pages/selectdrive.tag +++ /dev/null @@ -1,161 +0,0 @@ -<mk-selectdrive-page> - <mk-drive-browser ref="browser" multiple={ multiple }/> - <div> - <button class="upload" title="%i18n:desktop.tags.mk-selectdrive-page.upload%" onclick={ upload }>%fa:upload%</button> - <button class="cancel" onclick={ close }>%i18n:desktop.tags.mk-selectdrive-page.cancel%</button> - <button class="ok" onclick={ ok }>%i18n:desktop.tags.mk-selectdrive-page.ok%</button> - </div> - - <style> - :scope - display block - position fixed - width 100% - height 100% - background #fff - - > mk-drive-browser - height calc(100% - 72px) - - > div - position fixed - bottom 0 - left 0 - width 100% - height 72px - background lighten($theme-color, 95%) - - .upload - display inline-block - position absolute - top 8px - left 16px - cursor pointer - padding 0 - margin 8px 4px 0 0 - width 40px - height 40px - font-size 1em - color rgba($theme-color, 0.5) - background transparent - outline none - border solid 1px transparent - border-radius 4px - - &:hover - background transparent - border-color rgba($theme-color, 0.3) - - &:active - color rgba($theme-color, 0.6) - background transparent - border-color rgba($theme-color, 0.5) - box-shadow 0 2px 4px rgba(darken($theme-color, 50%), 0.15) inset - - &:focus - &:after - content "" - pointer-events none - position absolute - top -5px - right -5px - bottom -5px - left -5px - border 2px solid rgba($theme-color, 0.3) - border-radius 8px - - .ok - .cancel - display block - position absolute - bottom 16px - cursor pointer - padding 0 - margin 0 - width 120px - height 40px - font-size 1em - outline none - border-radius 4px - - &:focus - &:after - content "" - pointer-events none - position absolute - top -5px - right -5px - bottom -5px - left -5px - border 2px solid rgba($theme-color, 0.3) - border-radius 8px - - &:disabled - opacity 0.7 - cursor default - - .ok - right 16px - color $theme-color-foreground - background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%) - border solid 1px lighten($theme-color, 15%) - - &:not(:disabled) - font-weight bold - - &:hover:not(:disabled) - background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%) - border-color $theme-color - - &:active:not(:disabled) - background $theme-color - border-color $theme-color - - .cancel - right 148px - color #888 - background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%) - border solid 1px #e2e2e2 - - &:hover - background linear-gradient(to bottom, #f9f9f9 0%, #ececec 100%) - border-color #dcdcdc - - &:active - background #ececec - border-color #dcdcdc - - </style> - <script> - const q = (new URL(location)).searchParams; - this.multiple = q.get('multiple') == 'true' ? true : false; - - this.on('mount', () => { - document.title = '%i18n:desktop.tags.mk-selectdrive-page.title%'; - - this.refs.browser.on('selected', file => { - this.files = [file]; - this.ok(); - }); - - this.refs.browser.on('change-selection', files => { - this.update({ - files: files - }); - }); - }); - - this.upload = () => { - this.refs.browser.selectLocalFile(); - }; - - this.close = () => { - window.close(); - }; - - this.ok = () => { - window.opener.cb(this.multiple ? this.files : this.files[0]); - window.close(); - }; - </script> -</mk-selectdrive-page> diff --git a/src/web/app/desktop/tags/pages/user.tag b/src/web/app/desktop/tags/pages/user.tag deleted file mode 100644 index 811ca5c0fd..0000000000 --- a/src/web/app/desktop/tags/pages/user.tag +++ /dev/null @@ -1,27 +0,0 @@ -<mk-user-page> - <mk-ui ref="ui"> - <mk-user ref="user" user={ parent.user } page={ parent.opts.page }/> - </mk-ui> - <style> - :scope - display block - </style> - <script> - import Progress from '../../../common/scripts/loading'; - - this.user = this.opts.user; - - this.on('mount', () => { - Progress.start(); - - this.refs.ui.refs.user.on('user-fetched', user => { - Progress.set(0.5); - document.title = user.name + ' | Misskey'; - }); - - this.refs.ui.refs.user.on('loaded', () => { - Progress.done(); - }); - }); - </script> -</mk-user-page> diff --git a/src/web/app/desktop/tags/post-detail-sub.tag b/src/web/app/desktop/tags/post-detail-sub.tag deleted file mode 100644 index e22386df91..0000000000 --- a/src/web/app/desktop/tags/post-detail-sub.tag +++ /dev/null @@ -1,156 +0,0 @@ -<mk-post-detail-sub title={ title }> - <a class="avatar-anchor" href={ '/' + post.user.username }> - <img class="avatar" src={ post.user.avatar_url + '?thumbnail&size=64' } alt="avatar" data-user-preview={ post.user_id }/> - </a> - <div class="main"> - <header> - <div class="left"> - <a class="name" href={ '/' + post.user.username } data-user-preview={ post.user_id }>{ post.user.name }</a> - <span class="username">@{ post.user.username }</span> - </div> - <div class="right"> - <a class="time" href={ '/' + this.post.user.username + '/' + this.post.id }> - <mk-time time={ post.created_at }/> - </a> - </div> - </header> - <div class="body"> - <div class="text" ref="text"></div> - <div class="media" if={ post.media }> - <virtual each={ file in post.media }> - <img src={ file.url + '?thumbnail&size=512' } alt={ file.name } title={ file.name }/> - </virtual> - </div> - </div> - </div> - <style> - :scope - display block - margin 0 - padding 20px 32px - background #fdfdfd - - &:after - content "" - display block - clear both - - &:hover - > .main > footer > button - color #888 - - > .avatar-anchor - display block - float left - margin 0 16px 0 0 - - > .avatar - display block - width 44px - height 44px - margin 0 - border-radius 4px - vertical-align bottom - - > .main - float left - width calc(100% - 60px) - - > header - margin-bottom 4px - white-space nowrap - - &:after - content "" - display block - clear both - - > .left - float left - - > .name - display inline - margin 0 - padding 0 - color #777 - font-size 1em - font-weight 700 - text-align left - text-decoration none - - &:hover - text-decoration underline - - > .username - text-align left - margin 0 0 0 8px - color #ccc - - > .right - float right - - > .time - font-size 0.9em - color #c0c0c0 - - > .body - - > .text - cursor default - display block - margin 0 - padding 0 - overflow-wrap break-word - font-size 1em - color #717171 - - > mk-url-preview - margin-top 8px - - > .media - > img - display block - max-width 100% - - </style> - <script> - import compile from '../../common/scripts/text-compiler'; - import dateStringify from '../../common/scripts/date-stringify'; - - this.mixin('api'); - this.mixin('user-preview'); - - this.post = this.opts.post; - this.title = dateStringify(this.post.created_at); - - this.on('mount', () => { - if (this.post.text) { - const tokens = this.post.ast; - - this.refs.text.innerHTML = compile(tokens); - - Array.from(this.refs.text.children).forEach(e => { - if (e.tagName == 'MK-URL') riot.mount(e); - }); - } - }); - - this.like = () => { - if (this.post.is_liked) { - this.api('posts/likes/delete', { - post_id: this.post.id - }).then(() => { - this.post.is_liked = false; - this.update(); - }); - } else { - this.api('posts/likes/create', { - post_id: this.post.id - }).then(() => { - this.post.is_liked = true; - this.update(); - }); - } - }; - </script> -</mk-post-detail-sub> diff --git a/src/web/app/desktop/tags/post-detail.tag b/src/web/app/desktop/tags/post-detail.tag deleted file mode 100644 index 37f90a6ffb..0000000000 --- a/src/web/app/desktop/tags/post-detail.tag +++ /dev/null @@ -1,333 +0,0 @@ -<mk-post-detail title={ title }> - <div class="main"> - <button class="read-more" if={ p.reply && p.reply.reply_id && context == null } title="会話をもっと読み込む" onclick={ loadContext } disabled={ contextFetching }> - <virtual if={ !contextFetching }>%fa:ellipsis-v%</virtual> - <virtual if={ contextFetching }>%fa:spinner .pulse%</virtual> - </button> - <div class="context"> - <virtual each={ post in context }> - <mk-post-detail-sub post={ post }/> - </virtual> - </div> - <div class="reply-to" if={ p.reply }> - <mk-post-detail-sub post={ p.reply }/> - </div> - <div class="repost" if={ isRepost }> - <p> - <a class="avatar-anchor" href={ '/' + post.user.username } data-user-preview={ post.user_id }> - <img class="avatar" src={ post.user.avatar_url + '?thumbnail&size=32' } alt="avatar"/> - </a> - %fa:retweet%<a class="name" href={ '/' + post.user.username }> - { post.user.name } - </a> - がRepost - </p> - </div> - <article> - <a class="avatar-anchor" href={ '/' + p.user.username }> - <img class="avatar" src={ p.user.avatar_url + '?thumbnail&size=64' } alt="avatar" data-user-preview={ p.user.id }/> - </a> - <header> - <a class="name" href={ '/' + p.user.username } data-user-preview={ p.user.id }>{ p.user.name }</a> - <span class="username">@{ p.user.username }</span> - <a class="time" href={ '/' + p.user.username + '/' + p.id }> - <mk-time time={ p.created_at }/> - </a> - </header> - <div class="body"> - <div class="text" ref="text"></div> - <div class="media" if={ p.media }> - <virtual each={ file in p.media }><img src={ file.url + '?thumbnail&size=512' } alt={ file.name } title={ file.name }/></virtual> - </div> - <mk-poll if={ p.poll } post={ p }/> - </div> - <footer> - <mk-reactions-viewer post={ p }/> - <button onclick={ reply } title="返信"> - %fa:reply%<p class="count" if={ p.replies_count > 0 }>{ p.replies_count }</p> - </button> - <button onclick={ repost } title="Repost"> - %fa:retweet%<p class="count" if={ p.repost_count > 0 }>{ p.repost_count }</p> - </button> - <button class={ reacted: p.my_reaction != null } onclick={ react } ref="reactButton" title="リアクション"> - %fa:plus%<p class="count" if={ p.reactions_count > 0 }>{ p.reactions_count }</p> - </button> - <button onclick={ menu } ref="menuButton"> - %fa:ellipsis-h% - </button> - </footer> - </article> - <div class="replies" if={ !compact }> - <virtual each={ post in replies }> - <mk-post-detail-sub post={ post }/> - </virtual> - </div> - </div> - <style> - :scope - display block - margin 0 - padding 0 - overflow hidden - text-align left - background #fff - border solid 1px rgba(0, 0, 0, 0.1) - border-radius 8px - - > .main - - > .read-more - display block - margin 0 - padding 10px 0 - width 100% - font-size 1em - text-align center - color #999 - cursor pointer - background #fafafa - outline none - border none - border-bottom solid 1px #eef0f2 - border-radius 6px 6px 0 0 - - &:hover - background #f6f6f6 - - &:active - background #f0f0f0 - - &:disabled - color #ccc - - > .context - > * - border-bottom 1px solid #eef0f2 - - > .repost - color #9dbb00 - background linear-gradient(to bottom, #edfde2 0%, #fff 100%) - - > p - margin 0 - padding 16px 32px - - .avatar-anchor - display inline-block - - .avatar - vertical-align bottom - min-width 28px - min-height 28px - max-width 28px - max-height 28px - margin 0 8px 0 0 - border-radius 6px - - [data-fa] - margin-right 4px - - .name - font-weight bold - - & + article - padding-top 8px - - > .reply-to - border-bottom 1px solid #eef0f2 - - > article - padding 28px 32px 18px 32px - - &:after - content "" - display block - clear both - - &:hover - > .main > footer > button - color #888 - - > .avatar-anchor - display block - width 60px - height 60px - - > .avatar - display block - width 60px - height 60px - margin 0 - border-radius 8px - vertical-align bottom - - > header - position absolute - top 28px - left 108px - width calc(100% - 108px) - - > .name - display inline-block - margin 0 - line-height 24px - color #777 - font-size 18px - font-weight 700 - text-align left - text-decoration none - - &:hover - text-decoration underline - - > .username - display block - text-align left - margin 0 - color #ccc - - > .time - position absolute - top 0 - right 32px - font-size 1em - color #c0c0c0 - - > .body - padding 8px 0 - - > .text - cursor default - display block - margin 0 - padding 0 - overflow-wrap break-word - font-size 1.5em - color #717171 - - > mk-url-preview - margin-top 8px - - > .media - > img - display block - max-width 100% - - > footer - font-size 1.2em - - > button - margin 0 28px 0 0 - padding 8px - background transparent - border none - font-size 1em - color #ddd - cursor pointer - - &:hover - color #666 - - > .count - display inline - margin 0 0 0 8px - color #999 - - &.reacted - color $theme-color - - > .replies - > * - border-top 1px solid #eef0f2 - - </style> - <script> - import compile from '../../common/scripts/text-compiler'; - import dateStringify from '../../common/scripts/date-stringify'; - - this.mixin('api'); - this.mixin('user-preview'); - - this.compact = this.opts.compact; - this.contextFetching = false; - this.context = null; - this.post = this.opts.post; - this.isRepost = this.post.repost != null; - this.p = this.isRepost ? this.post.repost : this.post; - this.p.reactions_count = this.p.reaction_counts ? Object.keys(this.p.reaction_counts).map(key => this.p.reaction_counts[key]).reduce((a, b) => a + b) : 0; - this.title = dateStringify(this.p.created_at); - - this.on('mount', () => { - if (this.p.text) { - const tokens = this.p.ast; - - this.refs.text.innerHTML = compile(tokens); - - Array.from(this.refs.text.children).forEach(e => { - if (e.tagName == 'MK-URL') riot.mount(e); - }); - - // URLをプレビュー - tokens - .filter(t => (t.type == 'url' || t.type == 'link') && !t.silent) - .map(t => { - riot.mount(this.refs.text.appendChild(document.createElement('mk-url-preview')), { - url: t.url - }); - }); - } - - // Get replies - if (!this.compact) { - this.api('posts/replies', { - post_id: this.p.id, - limit: 8 - }).then(replies => { - this.update({ - replies: replies - }); - }); - } - }); - - this.reply = () => { - riot.mount(document.body.appendChild(document.createElement('mk-post-form-window')), { - reply: this.p - }); - }; - - this.repost = () => { - riot.mount(document.body.appendChild(document.createElement('mk-repost-form-window')), { - post: this.p - }); - }; - - this.react = () => { - riot.mount(document.body.appendChild(document.createElement('mk-reaction-picker')), { - source: this.refs.reactButton, - post: this.p - }); - }; - - this.menu = () => { - riot.mount(document.body.appendChild(document.createElement('mk-post-menu')), { - source: this.refs.menuButton, - post: this.p - }); - }; - - this.loadContext = () => { - this.contextFetching = true; - - // Fetch context - this.api('posts/context', { - post_id: this.p.reply_id - }).then(context => { - this.update({ - contextFetching: false, - context: context.reverse() - }); - }); - }; - </script> -</mk-post-detail> diff --git a/src/web/app/desktop/tags/post-form-window.tag b/src/web/app/desktop/tags/post-form-window.tag deleted file mode 100644 index 05a09b7803..0000000000 --- a/src/web/app/desktop/tags/post-form-window.tag +++ /dev/null @@ -1,68 +0,0 @@ -<mk-post-form-window> - <mk-window ref="window" is-modal={ true }> - <yield to="header"> - <span if={ !parent.opts.reply }>%i18n:desktop.tags.mk-post-form-window.post%</span> - <span if={ parent.opts.reply }>%i18n:desktop.tags.mk-post-form-window.reply%</span> - <span class="files" if={ parent.files.length != 0 }>{ '%i18n:desktop.tags.mk-post-form-window.attaches%'.replace('{}', parent.files.length) }</span> - <span class="uploading-files" if={ parent.uploadingFiles.length != 0 }>{ '%i18n:desktop.tags.mk-post-form-window.uploading-media%'.replace('{}', parent.uploadingFiles.length) }<mk-ellipsis/></span> - </yield> - <yield to="content"> - <div class="ref" if={ parent.opts.reply }> - <mk-post-preview post={ parent.opts.reply }/> - </div> - <div class="body"> - <mk-post-form ref="form" reply={ parent.opts.reply }/> - </div> - </yield> - </mk-window> - <style> - :scope - > mk-window - - [data-yield='header'] - > .files - > .uploading-files - margin-left 8px - opacity 0.8 - - &:before - content '(' - - &:after - content ')' - - [data-yield='content'] - > .ref - > mk-post-preview - margin 16px 22px - - </style> - <script> - this.uploadingFiles = []; - this.files = []; - - this.on('mount', () => { - this.refs.window.refs.form.focus(); - - this.refs.window.on('closed', () => { - this.unmount(); - }); - - this.refs.window.refs.form.on('post', () => { - this.refs.window.close(); - }); - - this.refs.window.refs.form.on('change-uploading-files', files => { - this.update({ - uploadingFiles: files || [] - }); - }); - - this.refs.window.refs.form.on('change-files', files => { - this.update({ - files: files || [] - }); - }); - }); - </script> -</mk-post-form-window> diff --git a/src/web/app/desktop/tags/post-form.tag b/src/web/app/desktop/tags/post-form.tag deleted file mode 100644 index 8e5171c83e..0000000000 --- a/src/web/app/desktop/tags/post-form.tag +++ /dev/null @@ -1,549 +0,0 @@ -<mk-post-form ondragover={ ondragover } ondragenter={ ondragenter } ondragleave={ ondragleave } ondrop={ ondrop }> - <div class="content"> - <textarea class={ with: (files.length != 0 || poll) } ref="text" disabled={ wait } oninput={ update } onkeydown={ onkeydown } onpaste={ onpaste } placeholder={ placeholder }></textarea> - <div class="medias { with: poll }" if={ files.length != 0 }> - <ul> - <li each={ files }> - <div class="img" style="background-image: url({ url + '?thumbnail&size=64' })" title={ name }></div> - <img class="remove" onclick={ removeFile } src="/assets/desktop/remove.png" title="%i18n:desktop.tags.mk-post-form.attach-cancel%" alt=""/> - </li> - <li class="add" if={ files.length < 4 } title="%i18n:desktop.tags.mk-post-form.attach-media-from-local%" onclick={ selectFile }>%fa:plus%</li> - </ul> - <p class="remain">{ 4 - files.length }/4</p> - </div> - <mk-poll-editor if={ poll } ref="poll" ondestroy={ onPollDestroyed }/> - </div> - <mk-uploader ref="uploader"/> - <button ref="upload" title="%i18n:desktop.tags.mk-post-form.attach-media-from-local%" onclick={ selectFile }>%fa:upload%</button> - <button ref="drive" title="%i18n:desktop.tags.mk-post-form.attach-media-from-drive%" onclick={ selectFileFromDrive }>%fa:cloud%</button> - <button class="kao" title="%i18n:desktop.tags.mk-post-form.insert-a-kao%" onclick={ kao }>%fa:R smile%</button> - <button class="poll" title="%i18n:desktop.tags.mk-post-form.create-poll%" onclick={ addPoll }>%fa:chart-pie%</button> - <p class="text-count { over: refs.text.value.length > 1000 }">{ '%i18n:desktop.tags.mk-post-form.text-remain%'.replace('{}', 1000 - refs.text.value.length) }</p> - <button class={ wait: wait } ref="submit" disabled={ wait || (refs.text.value.length == 0 && files.length == 0 && !poll && !repost) } onclick={ post }> - { wait ? '%i18n:desktop.tags.mk-post-form.posting%' : submitText }<mk-ellipsis if={ wait }/> - </button> - <input ref="file" type="file" accept="image/*" multiple="multiple" tabindex="-1" onchange={ changeFile }/> - <div class="dropzone" if={ draghover }></div> - <style> - :scope - display block - padding 16px - background lighten($theme-color, 95%) - - &:after - content "" - display block - clear both - - > .content - - [ref='text'] - display block - padding 12px - margin 0 - width 100% - max-width 100% - min-width 100% - min-height calc(16px + 12px + 12px) - font-size 16px - color #333 - background #fff - outline none - border solid 1px rgba($theme-color, 0.1) - border-radius 4px - transition border-color .3s ease - - &:hover - border-color rgba($theme-color, 0.2) - transition border-color .1s ease - - & + * - & + * + * - border-color rgba($theme-color, 0.2) - transition border-color .1s ease - - &:focus - color $theme-color - border-color rgba($theme-color, 0.5) - transition border-color 0s ease - - & + * - & + * + * - border-color rgba($theme-color, 0.5) - transition border-color 0s ease - - &:disabled - opacity 0.5 - - &::-webkit-input-placeholder - color rgba($theme-color, 0.3) - - &.with - border-bottom solid 1px rgba($theme-color, 0.1) !important - border-radius 4px 4px 0 0 - - > .medias - margin 0 - padding 0 - background lighten($theme-color, 98%) - border solid 1px rgba($theme-color, 0.1) - border-top none - border-radius 0 0 4px 4px - transition border-color .3s ease - - &.with - border-bottom solid 1px rgba($theme-color, 0.1) !important - border-radius 0 - - > .remain - display block - position absolute - top 8px - right 8px - margin 0 - padding 0 - color rgba($theme-color, 0.4) - - > ul - display block - margin 0 - padding 4px - list-style none - - &:after - content "" - display block - clear both - - > li - display block - float left - margin 4px - padding 0 - cursor move - - &:hover > .remove - display block - - > .img - width 64px - height 64px - background-size cover - background-position center center - - > .remove - display none - position absolute - top -6px - right -6px - width 16px - height 16px - cursor pointer - - > .add - display block - float left - margin 4px - padding 0 - border dashed 2px rgba($theme-color, 0.2) - cursor pointer - - &:hover - border-color rgba($theme-color, 0.3) - - > i - color rgba($theme-color, 0.4) - - > i - display block - width 60px - height 60px - line-height 60px - text-align center - font-size 1.2em - color rgba($theme-color, 0.2) - - > mk-poll-editor - background lighten($theme-color, 98%) - border solid 1px rgba($theme-color, 0.1) - border-top none - border-radius 0 0 4px 4px - transition border-color .3s ease - - > mk-uploader - margin 8px 0 0 0 - padding 8px - border solid 1px rgba($theme-color, 0.2) - border-radius 4px - - [ref='file'] - display none - - .text-count - pointer-events none - display block - position absolute - bottom 16px - right 138px - margin 0 - line-height 40px - color rgba($theme-color, 0.5) - - &.over - color #ec3828 - - [ref='submit'] - display block - position absolute - bottom 16px - right 16px - cursor pointer - padding 0 - margin 0 - width 110px - height 40px - font-size 1em - color $theme-color-foreground - background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%) - outline none - border solid 1px lighten($theme-color, 15%) - border-radius 4px - - &:not(:disabled) - font-weight bold - - &:hover:not(:disabled) - background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%) - border-color $theme-color - - &:active:not(:disabled) - background $theme-color - border-color $theme-color - - &:focus - &:after - content "" - pointer-events none - position absolute - top -5px - right -5px - bottom -5px - left -5px - border 2px solid rgba($theme-color, 0.3) - border-radius 8px - - &:disabled - opacity 0.7 - cursor default - - &.wait - background linear-gradient( - 45deg, - darken($theme-color, 10%) 25%, - $theme-color 25%, - $theme-color 50%, - darken($theme-color, 10%) 50%, - darken($theme-color, 10%) 75%, - $theme-color 75%, - $theme-color - ) - background-size 32px 32px - animation stripe-bg 1.5s linear infinite - opacity 0.7 - cursor wait - - @keyframes stripe-bg - from {background-position: 0 0;} - to {background-position: -64px 32px;} - - [ref='upload'] - [ref='drive'] - .kao - .poll - display inline-block - cursor pointer - padding 0 - margin 8px 4px 0 0 - width 40px - height 40px - font-size 1em - color rgba($theme-color, 0.5) - background transparent - outline none - border solid 1px transparent - border-radius 4px - - &:hover - background transparent - border-color rgba($theme-color, 0.3) - - &:active - color rgba($theme-color, 0.6) - background linear-gradient(to bottom, lighten($theme-color, 80%) 0%, lighten($theme-color, 90%) 100%) - border-color rgba($theme-color, 0.5) - box-shadow 0 2px 4px rgba(0, 0, 0, 0.15) inset - - &:focus - &:after - content "" - pointer-events none - position absolute - top -5px - right -5px - bottom -5px - left -5px - border 2px solid rgba($theme-color, 0.3) - border-radius 8px - - > .dropzone - position absolute - left 0 - top 0 - width 100% - height 100% - border dashed 2px rgba($theme-color, 0.5) - pointer-events none - - </style> - <script> - import getKao from '../../common/scripts/get-kao'; - import notify from '../scripts/notify'; - import Autocomplete from '../scripts/autocomplete'; - - this.mixin('api'); - - this.wait = false; - this.uploadings = []; - this.files = []; - this.autocomplete = null; - this.poll = false; - - this.inReplyToPost = this.opts.reply; - - this.repost = this.opts.repost; - - this.placeholder = this.repost - ? '%i18n:desktop.tags.mk-post-form.quote-placeholder%' - : this.inReplyToPost - ? '%i18n:desktop.tags.mk-post-form.reply-placeholder%' - : '%i18n:desktop.tags.mk-post-form.post-placeholder%'; - - this.submitText = this.repost - ? '%i18n:desktop.tags.mk-post-form.repost%' - : this.inReplyToPost - ? '%i18n:desktop.tags.mk-post-form.reply%' - : '%i18n:desktop.tags.mk-post-form.post%'; - - this.draftId = this.repost - ? 'repost:' + this.repost.id - : this.inReplyToPost - ? 'reply:' + this.inReplyToPost.id - : 'post'; - - this.on('mount', () => { - this.refs.uploader.on('uploaded', file => { - this.addFile(file); - }); - - this.refs.uploader.on('change-uploads', uploads => { - this.trigger('change-uploading-files', uploads); - }); - - this.autocomplete = new Autocomplete(this.refs.text); - this.autocomplete.attach(); - - // 書きかけの投稿を復元 - const draft = JSON.parse(localStorage.getItem('drafts') || '{}')[this.draftId]; - if (draft) { - this.refs.text.value = draft.data.text; - this.files = draft.data.files; - if (draft.data.poll) { - this.poll = true; - this.update(); - this.refs.poll.set(draft.data.poll); - } - this.trigger('change-files', this.files); - this.update(); - } - }); - - this.on('unmount', () => { - this.autocomplete.detach(); - }); - - this.focus = () => { - this.refs.text.focus(); - }; - - this.clear = () => { - this.refs.text.value = ''; - this.files = []; - this.poll = false; - this.trigger('change-files'); - this.update(); - }; - - this.ondragover = e => { - e.preventDefault(); - e.stopPropagation(); - this.draghover = true; - e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move'; - }; - - this.ondragenter = e => { - this.draghover = true; - }; - - this.ondragleave = e => { - this.draghover = false; - }; - - this.ondrop = e => { - e.preventDefault(); - e.stopPropagation(); - this.draghover = false; - - // ファイルだったら - if (e.dataTransfer.files.length > 0) { - Array.from(e.dataTransfer.files).forEach(this.upload); - return; - } - - // データ取得 - const data = e.dataTransfer.getData('text'); - if (data == null) return false; - - // パース - // TODO: Validate JSON - const obj = JSON.parse(data); - - // (ドライブの)ファイルだったら - if (obj.type == 'file') { - this.files.push(obj.file); - this.update(); - } - }; - - this.onkeydown = e => { - if ((e.which == 10 || e.which == 13) && (e.ctrlKey || e.metaKey)) this.post(); - }; - - this.onpaste = e => { - Array.from(e.clipboardData.items).forEach(item => { - if (item.kind == 'file') { - this.upload(item.getAsFile()); - } - }); - }; - - this.selectFile = () => { - this.refs.file.click(); - }; - - this.selectFileFromDrive = () => { - const i = riot.mount(document.body.appendChild(document.createElement('mk-select-file-from-drive-window')), { - multiple: true - })[0]; - i.one('selected', files => { - files.forEach(this.addFile); - }); - }; - - this.changeFile = () => { - Array.from(this.refs.file.files).forEach(this.upload); - }; - - this.upload = file => { - this.refs.uploader.upload(file); - }; - - this.addFile = file => { - this.files.push(file); - this.trigger('change-files', this.files); - this.update(); - }; - - this.removeFile = e => { - const file = e.item; - this.files = this.files.filter(x => x.id != file.id); - this.trigger('change-files', this.files); - this.update(); - }; - - this.addPoll = () => { - this.poll = true; - }; - - this.onPollDestroyed = () => { - this.update({ - poll: false - }); - }; - - this.post = e => { - this.wait = true; - - const files = this.files && this.files.length > 0 - ? this.files.map(f => f.id) - : undefined; - - this.api('posts/create', { - text: this.refs.text.value == '' ? undefined : this.refs.text.value, - media_ids: files, - reply_id: this.inReplyToPost ? this.inReplyToPost.id : undefined, - repost_id: this.repost ? this.repost.id : undefined, - poll: this.poll ? this.refs.poll.get() : undefined - }).then(data => { - this.clear(); - this.removeDraft(); - this.trigger('post'); - notify(this.repost - ? '%i18n:desktop.tags.mk-post-form.reposted%' - : this.inReplyToPost - ? '%i18n:desktop.tags.mk-post-form.replied%' - : '%i18n:desktop.tags.mk-post-form.posted%'); - }).catch(err => { - notify(this.repost - ? '%i18n:desktop.tags.mk-post-form.repost-failed%' - : this.inReplyToPost - ? '%i18n:desktop.tags.mk-post-form.reply-failed%' - : '%i18n:desktop.tags.mk-post-form.post-failed%'); - }).then(() => { - this.update({ - wait: false - }); - }); - }; - - this.kao = () => { - this.refs.text.value += getKao(); - }; - - this.on('update', () => { - this.saveDraft(); - }); - - this.saveDraft = () => { - const data = JSON.parse(localStorage.getItem('drafts') || '{}'); - - data[this.draftId] = { - updated_at: new Date(), - data: { - text: this.refs.text.value, - files: this.files, - poll: this.poll && this.refs.poll ? this.refs.poll.get() : undefined - } - } - - localStorage.setItem('drafts', JSON.stringify(data)); - }; - - this.removeDraft = () => { - const data = JSON.parse(localStorage.getItem('drafts') || '{}'); - - delete data[this.draftId]; - - localStorage.setItem('drafts', JSON.stringify(data)); - }; - </script> -</mk-post-form> diff --git a/src/web/app/desktop/tags/post-preview.tag b/src/web/app/desktop/tags/post-preview.tag deleted file mode 100644 index 9a7db5ffa3..0000000000 --- a/src/web/app/desktop/tags/post-preview.tag +++ /dev/null @@ -1,94 +0,0 @@ -<mk-post-preview title={ title }> - <article><a class="avatar-anchor" href={ '/' + post.user.username }><img class="avatar" src={ post.user.avatar_url + '?thumbnail&size=64' } alt="avatar" data-user-preview={ post.user_id }/></a> - <div class="main"> - <header><a class="name" href={ '/' + post.user.username } data-user-preview={ post.user_id }>{ post.user.name }</a><span class="username">@{ post.user.username }</span><a class="time" href={ '/' + post.user.username + '/' + post.id }> - <mk-time time={ post.created_at }/></a></header> - <div class="body"> - <mk-sub-post-content class="text" post={ post }/> - </div> - </div> - </article> - <style> - :scope - display block - margin 0 - padding 0 - font-size 0.9em - background #fff - - > article - - &:after - content "" - display block - clear both - - &:hover - > .main > footer > button - color #888 - - > .avatar-anchor - display block - float left - margin 0 16px 0 0 - - > .avatar - display block - width 52px - height 52px - margin 0 - border-radius 8px - vertical-align bottom - - > .main - float left - width calc(100% - 68px) - - > header - display flex - margin 4px 0 - white-space nowrap - - > .name - margin 0 .5em 0 0 - padding 0 - color #607073 - font-size 1em - line-height 1.1em - font-weight 700 - text-align left - text-decoration none - white-space normal - - &:hover - text-decoration underline - - > .username - text-align left - margin 0 .5em 0 0 - color #d1d8da - - > .time - margin-left auto - color #b2b8bb - - > .body - - > .text - cursor default - margin 0 - padding 0 - font-size 1.1em - color #717171 - - </style> - <script> - import dateStringify from '../../common/scripts/date-stringify'; - - this.mixin('user-preview'); - - this.post = this.opts.post; - - this.title = dateStringify(this.post.created_at); - </script> -</mk-post-preview> diff --git a/src/web/app/desktop/tags/progress-dialog.tag b/src/web/app/desktop/tags/progress-dialog.tag deleted file mode 100644 index a0ac51b2f4..0000000000 --- a/src/web/app/desktop/tags/progress-dialog.tag +++ /dev/null @@ -1,97 +0,0 @@ -<mk-progress-dialog> - <mk-window ref="window" is-modal={ false } can-close={ false } width={ '500px' }> - <yield to="header">{ parent.title }<mk-ellipsis/></yield> - <yield to="content"> - <div class="body"> - <p class="init" if={ isNaN(parent.value) }>待機中<mk-ellipsis/></p> - <p class="percentage" if={ !isNaN(parent.value) }>{ Math.floor((parent.value / parent.max) * 100) }</p> - <progress if={ !isNaN(parent.value) && parent.value < parent.max } value={ isNaN(parent.value) ? 0 : parent.value } max={ parent.max }></progress> - <div class="progress waiting" if={ parent.value >= parent.max }></div> - </div> - </yield> - </mk-window> - <style> - :scope - display block - - > mk-window - [data-yield='content'] - - > .body - padding 18px 24px 24px 24px - - > .init - display block - margin 0 - text-align center - color rgba(#000, 0.7) - - > .percentage - display block - margin 0 0 4px 0 - text-align center - line-height 16px - color rgba($theme-color, 0.7) - - &:after - content '%' - - > progress - > .progress - display block - margin 0 - width 100% - height 10px - background transparent - border none - border-radius 4px - overflow hidden - - &::-webkit-progress-value - background $theme-color - - &::-webkit-progress-bar - background rgba($theme-color, 0.1) - - > .progress - background linear-gradient( - 45deg, - lighten($theme-color, 30%) 25%, - $theme-color 25%, - $theme-color 50%, - lighten($theme-color, 30%) 50%, - lighten($theme-color, 30%) 75%, - $theme-color 75%, - $theme-color - ) - background-size 32px 32px - animation progress-dialog-tag-progress-waiting 1.5s linear infinite - - @keyframes progress-dialog-tag-progress-waiting - from {background-position: 0 0;} - to {background-position: -64px 32px;} - - </style> - <script> - this.title = this.opts.title; - this.value = parseInt(this.opts.value, 10); - this.max = parseInt(this.opts.max, 10); - - this.on('mount', () => { - this.refs.window.on('closed', () => { - this.unmount(); - }); - }); - - this.updateProgress = (value, max) => { - this.update({ - value: parseInt(value, 10), - max: parseInt(max, 10) - }); - }; - - this.close = () => { - this.refs.window.close(); - }; - </script> -</mk-progress-dialog> diff --git a/src/web/app/desktop/tags/repost-form-window.tag b/src/web/app/desktop/tags/repost-form-window.tag deleted file mode 100644 index dbc3f5a3c5..0000000000 --- a/src/web/app/desktop/tags/repost-form-window.tag +++ /dev/null @@ -1,47 +0,0 @@ -<mk-repost-form-window> - <mk-window ref="window" is-modal={ true }> - <yield to="header"> - %fa:retweet%%i18n:desktop.tags.mk-repost-form-window.title% - </yield> - <yield to="content"> - <mk-repost-form ref="form" post={ parent.opts.post }/> - </yield> - </mk-window> - <style> - :scope - > mk-window - [data-yield='header'] - > [data-fa] - margin-right 4px - - </style> - <script> - this.onDocumentKeydown = e => { - if (e.target.tagName != 'INPUT' && e.target.tagName != 'TEXTAREA') { - if (e.which == 27) { // Esc - this.refs.window.close(); - } - } - }; - - this.on('mount', () => { - this.refs.window.refs.form.on('cancel', () => { - this.refs.window.close(); - }); - - this.refs.window.refs.form.on('posted', () => { - this.refs.window.close(); - }); - - document.addEventListener('keydown', this.onDocumentKeydown); - - this.refs.window.on('closed', () => { - this.unmount(); - }); - }); - - this.on('unmount', () => { - document.removeEventListener('keydown', this.onDocumentKeydown); - }); - </script> -</mk-repost-form-window> diff --git a/src/web/app/desktop/tags/repost-form.tag b/src/web/app/desktop/tags/repost-form.tag deleted file mode 100644 index c3cf6c1fb3..0000000000 --- a/src/web/app/desktop/tags/repost-form.tag +++ /dev/null @@ -1,127 +0,0 @@ -<mk-repost-form> - <mk-post-preview post={ opts.post }/> - <virtual if={ !quote }> - <footer> - <a class="quote" if={ !quote } onclick={ onquote }>%i18n:desktop.tags.mk-repost-form.quote%</a> - <button class="cancel" onclick={ cancel }>%i18n:desktop.tags.mk-repost-form.cancel%</button> - <button class="ok" onclick={ ok } disabled={ wait }>{ wait ? '%i18n:desktop.tags.mk-repost-form.reposting%' : '%i18n:desktop.tags.mk-repost-form.repost%' }</button> - </footer> - </virtual> - <virtual if={ quote }> - <mk-post-form ref="form" repost={ opts.post }/> - </virtual> - <style> - :scope - - > mk-post-preview - margin 16px 22px - - > div - padding 16px - - > footer - height 72px - background lighten($theme-color, 95%) - - > .quote - position absolute - bottom 16px - left 28px - line-height 40px - - button - display block - position absolute - bottom 16px - cursor pointer - padding 0 - margin 0 - width 120px - height 40px - font-size 1em - outline none - border-radius 4px - - &:focus - &:after - content "" - pointer-events none - position absolute - top -5px - right -5px - bottom -5px - left -5px - border 2px solid rgba($theme-color, 0.3) - border-radius 8px - - > .cancel - right 148px - color #888 - background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%) - border solid 1px #e2e2e2 - - &:hover - background linear-gradient(to bottom, #f9f9f9 0%, #ececec 100%) - border-color #dcdcdc - - &:active - background #ececec - border-color #dcdcdc - - > .ok - right 16px - font-weight bold - color $theme-color-foreground - background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%) - border solid 1px lighten($theme-color, 15%) - - &:hover - background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%) - border-color $theme-color - - &:active - background $theme-color - border-color $theme-color - - </style> - <script> - import notify from '../scripts/notify'; - - this.mixin('api'); - - this.wait = false; - this.quote = false; - - this.cancel = () => { - this.trigger('cancel'); - }; - - this.ok = () => { - this.wait = true; - this.api('posts/create', { - repost_id: this.opts.post.id - }).then(data => { - this.trigger('posted'); - notify('%i18n:desktop.tags.mk-repost-form.success%'); - }).catch(err => { - notify('%i18n:desktop.tags.mk-repost-form.failure%'); - }).then(() => { - this.update({ - wait: false - }); - }); - }; - - this.onquote = () => { - this.update({ - quote: true - }); - - this.refs.form.on('post', () => { - this.trigger('posted'); - }); - - this.refs.form.focus(); - }; - </script> -</mk-repost-form> diff --git a/src/web/app/desktop/tags/search-posts.tag b/src/web/app/desktop/tags/search-posts.tag deleted file mode 100644 index 52f765d1a1..0000000000 --- a/src/web/app/desktop/tags/search-posts.tag +++ /dev/null @@ -1,95 +0,0 @@ -<mk-search-posts> - <div class="loading" if={ isLoading }> - <mk-ellipsis-icon/> - </div> - <p class="empty" if={ isEmpty }>%fa:search%「{ query }」に関する投稿は見つかりませんでした。</p> - <mk-timeline ref="timeline"> - <yield to="footer"> - <virtual if={ !parent.moreLoading }>%fa:moon%</virtual> - <virtual if={ parent.moreLoading }>%fa:spinner .pulse .fw%</virtual> - </yield/> - </mk-timeline> - <style> - :scope - display block - background #fff - - > .loading - padding 64px 0 - - > .empty - display block - margin 0 auto - padding 32px - max-width 400px - text-align center - color #999 - - > [data-fa] - display block - margin-bottom 16px - font-size 3em - color #ccc - - </style> - <script> - this.mixin('api'); - - this.query = this.opts.query; - this.isLoading = true; - this.isEmpty = false; - this.moreLoading = false; - this.page = 0; - - this.on('mount', () => { - document.addEventListener('keydown', this.onDocumentKeydown); - window.addEventListener('scroll', this.onScroll); - - this.api('posts/search', { - query: this.query - }).then(posts => { - this.update({ - isLoading: false, - isEmpty: posts.length == 0 - }); - this.refs.timeline.setPosts(posts); - this.trigger('loaded'); - }); - }); - - this.on('unmount', () => { - document.removeEventListener('keydown', this.onDocumentKeydown); - window.removeEventListener('scroll', this.onScroll); - }); - - this.onDocumentKeydown = e => { - if (e.target.tagName != 'INPUT' && e.target.tagName != 'TEXTAREA') { - if (e.which == 84) { // t - this.refs.timeline.focus(); - } - } - }; - - this.more = () => { - if (this.moreLoading || this.isLoading || this.timeline.posts.length == 0) return; - this.update({ - moreLoading: true - }); - this.api('posts/search', { - query: this.query, - page: this.page + 1 - }).then(posts => { - this.update({ - moreLoading: false, - page: page + 1 - }); - this.refs.timeline.prependPosts(posts); - }); - }; - - this.onScroll = () => { - const current = window.scrollY + window.innerHeight; - if (current > document.body.offsetHeight - 16) this.more(); - }; - </script> -</mk-search-posts> diff --git a/src/web/app/desktop/tags/search.tag b/src/web/app/desktop/tags/search.tag deleted file mode 100644 index d5159fe4e9..0000000000 --- a/src/web/app/desktop/tags/search.tag +++ /dev/null @@ -1,34 +0,0 @@ -<mk-search> - <header> - <h1>{ query }</h1> - </header> - <mk-search-posts ref="posts" query={ query }/> - <style> - :scope - display block - padding-bottom 32px - - > header - width 100% - max-width 600px - margin 0 auto - color #555 - - > mk-search-posts - max-width 600px - margin 0 auto - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - overflow hidden - - </style> - <script> - this.query = this.opts.query; - - this.on('mount', () => { - this.refs.posts.on('loaded', () => { - this.trigger('loaded'); - }); - }); - </script> -</mk-search> diff --git a/src/web/app/desktop/tags/select-file-from-drive-window.tag b/src/web/app/desktop/tags/select-file-from-drive-window.tag deleted file mode 100644 index c660a2fe90..0000000000 --- a/src/web/app/desktop/tags/select-file-from-drive-window.tag +++ /dev/null @@ -1,173 +0,0 @@ -<mk-select-file-from-drive-window> - <mk-window ref="window" is-modal={ true } width={ '800px' } height={ '500px' }> - <yield to="header"> - <mk-raw content={ parent.title }/> - <span class="count" if={ parent.multiple && parent.files.length > 0 }>({ parent.files.length }ファイル選択中)</span> - </yield> - <yield to="content"> - <mk-drive-browser ref="browser" multiple={ parent.multiple }/> - <div> - <button class="upload" title="PCからドライブにファイルをアップロード" onclick={ parent.upload }>%fa:upload%</button> - <button class="cancel" onclick={ parent.close }>キャンセル</button> - <button class="ok" disabled={ parent.multiple && parent.files.length == 0 } onclick={ parent.ok }>決定</button> - </div> - </yield> - </mk-window> - <style> - :scope - > mk-window - [data-yield='header'] - > mk-raw - > [data-fa] - margin-right 4px - - .count - margin-left 8px - opacity 0.7 - - [data-yield='content'] - > mk-drive-browser - height calc(100% - 72px) - - > div - height 72px - background lighten($theme-color, 95%) - - > .upload - display inline-block - position absolute - top 8px - left 16px - cursor pointer - padding 0 - margin 8px 4px 0 0 - width 40px - height 40px - font-size 1em - color rgba($theme-color, 0.5) - background transparent - outline none - border solid 1px transparent - border-radius 4px - - &:hover - background transparent - border-color rgba($theme-color, 0.3) - - &:active - color rgba($theme-color, 0.6) - background transparent - border-color rgba($theme-color, 0.5) - box-shadow 0 2px 4px rgba(darken($theme-color, 50%), 0.15) inset - - &:focus - &:after - content "" - pointer-events none - position absolute - top -5px - right -5px - bottom -5px - left -5px - border 2px solid rgba($theme-color, 0.3) - border-radius 8px - - > .ok - > .cancel - display block - position absolute - bottom 16px - cursor pointer - padding 0 - margin 0 - width 120px - height 40px - font-size 1em - outline none - border-radius 4px - - &:focus - &:after - content "" - pointer-events none - position absolute - top -5px - right -5px - bottom -5px - left -5px - border 2px solid rgba($theme-color, 0.3) - border-radius 8px - - &:disabled - opacity 0.7 - cursor default - - > .ok - right 16px - color $theme-color-foreground - background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%) - border solid 1px lighten($theme-color, 15%) - - &:not(:disabled) - font-weight bold - - &:hover:not(:disabled) - background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%) - border-color $theme-color - - &:active:not(:disabled) - background $theme-color - border-color $theme-color - - > .cancel - right 148px - color #888 - background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%) - border solid 1px #e2e2e2 - - &:hover - background linear-gradient(to bottom, #f9f9f9 0%, #ececec 100%) - border-color #dcdcdc - - &:active - background #ececec - border-color #dcdcdc - - </style> - <script> - this.files = []; - - this.multiple = this.opts.multiple != null ? this.opts.multiple : false; - this.title = this.opts.title || '%fa:R file%ファイルを選択'; - - this.on('mount', () => { - this.refs.window.refs.browser.on('selected', file => { - this.files = [file]; - this.ok(); - }); - - this.refs.window.refs.browser.on('change-selection', files => { - this.update({ - files: files - }); - }); - - this.refs.window.on('closed', () => { - this.unmount(); - }); - }); - - this.close = () => { - this.refs.window.close(); - }; - - this.upload = () => { - this.refs.window.refs.browser.selectLocalFile(); - }; - - this.ok = () => { - this.trigger('selected', this.multiple ? this.files : this.files[0]); - this.refs.window.close(); - }; - </script> -</mk-select-file-from-drive-window> diff --git a/src/web/app/desktop/tags/select-folder-from-drive-window.tag b/src/web/app/desktop/tags/select-folder-from-drive-window.tag deleted file mode 100644 index 3c66a4e6da..0000000000 --- a/src/web/app/desktop/tags/select-folder-from-drive-window.tag +++ /dev/null @@ -1,112 +0,0 @@ -<mk-select-folder-from-drive-window> - <mk-window ref="window" is-modal={ true } width={ '800px' } height={ '500px' }> - <yield to="header"> - <mk-raw content={ parent.title }/> - </yield> - <yield to="content"> - <mk-drive-browser ref="browser"/> - <div> - <button class="cancel" onclick={ parent.close }>キャンセル</button> - <button class="ok" onclick={ parent.ok }>決定</button> - </div> - </yield> - </mk-window> - <style> - :scope - > mk-window - [data-yield='header'] - > mk-raw - > [data-fa] - margin-right 4px - - [data-yield='content'] - > mk-drive-browser - height calc(100% - 72px) - - > div - height 72px - background lighten($theme-color, 95%) - - .ok - .cancel - display block - position absolute - bottom 16px - cursor pointer - padding 0 - margin 0 - width 120px - height 40px - font-size 1em - outline none - border-radius 4px - - &:focus - &:after - content "" - pointer-events none - position absolute - top -5px - right -5px - bottom -5px - left -5px - border 2px solid rgba($theme-color, 0.3) - border-radius 8px - - &:disabled - opacity 0.7 - cursor default - - .ok - right 16px - color $theme-color-foreground - background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%) - border solid 1px lighten($theme-color, 15%) - - &:not(:disabled) - font-weight bold - - &:hover:not(:disabled) - background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%) - border-color $theme-color - - &:active:not(:disabled) - background $theme-color - border-color $theme-color - - .cancel - right 148px - color #888 - background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%) - border solid 1px #e2e2e2 - - &:hover - background linear-gradient(to bottom, #f9f9f9 0%, #ececec 100%) - border-color #dcdcdc - - &:active - background #ececec - border-color #dcdcdc - - </style> - <script> - this.files = []; - - this.title = this.opts.title || '%fa:R folder%フォルダを選択'; - - this.on('mount', () => { - this.refs.window.on('closed', () => { - this.unmount(); - }); - }); - - this.close = () => { - this.refs.window.close(); - }; - - this.ok = () => { - this.trigger('selected', this.refs.window.refs.browser.folder); - this.refs.window.close(); - }; - </script> -</mk-select-folder-from-drive-window> diff --git a/src/web/app/desktop/tags/set-avatar-suggestion.tag b/src/web/app/desktop/tags/set-avatar-suggestion.tag deleted file mode 100644 index 7e871129fc..0000000000 --- a/src/web/app/desktop/tags/set-avatar-suggestion.tag +++ /dev/null @@ -1,48 +0,0 @@ -<mk-set-avatar-suggestion onclick={ set }> - <p><b>アバターを設定</b>してみませんか? - <button onclick={ close }>%fa:times%</button> - </p> - <style> - :scope - display block - cursor pointer - color #fff - background #a8cad0 - - &:hover - background #70abb5 - - > p - display block - margin 0 auto - padding 8px - max-width 1024px - - > a - font-weight bold - color #fff - - > button - position absolute - top 0 - right 0 - padding 8px - color #fff - - </style> - <script> - import updateAvatar from '../scripts/update-avatar'; - - this.mixin('i'); - - this.set = () => { - updateAvatar(this.I); - }; - - this.close = e => { - e.preventDefault(); - e.stopPropagation(); - this.unmount(); - }; - </script> -</mk-set-avatar-suggestion> diff --git a/src/web/app/desktop/tags/set-banner-suggestion.tag b/src/web/app/desktop/tags/set-banner-suggestion.tag deleted file mode 100644 index 4cd364ca3e..0000000000 --- a/src/web/app/desktop/tags/set-banner-suggestion.tag +++ /dev/null @@ -1,48 +0,0 @@ -<mk-set-banner-suggestion onclick={ set }> - <p><b>バナーを設定</b>してみませんか? - <button onclick={ close }>%fa:times%</button> - </p> - <style> - :scope - display block - cursor pointer - color #fff - background #a8cad0 - - &:hover - background #70abb5 - - > p - display block - margin 0 auto - padding 8px - max-width 1024px - - > a - font-weight bold - color #fff - - > button - position absolute - top 0 - right 0 - padding 8px - color #fff - - </style> - <script> - import updateBanner from '../scripts/update-banner'; - - this.mixin('i'); - - this.set = () => { - updateBanner(this.I); - }; - - this.close = e => { - e.preventDefault(); - e.stopPropagation(); - this.unmount(); - }; - </script> -</mk-set-banner-suggestion> diff --git a/src/web/app/desktop/tags/settings-window.tag b/src/web/app/desktop/tags/settings-window.tag deleted file mode 100644 index 5a725af51e..0000000000 --- a/src/web/app/desktop/tags/settings-window.tag +++ /dev/null @@ -1,30 +0,0 @@ -<mk-settings-window> - <mk-window ref="window" is-modal={ true } width={ '700px' } height={ '550px' }> - <yield to="header">%fa:cog%設定</yield> - <yield to="content"> - <mk-settings/> - </yield> - </mk-window> - <style> - :scope - > mk-window - [data-yield='header'] - > [data-fa] - margin-right 4px - - [data-yield='content'] - overflow hidden - - </style> - <script> - this.on('mount', () => { - this.refs.window.on('closed', () => { - this.unmount(); - }); - }); - - this.close = () => { - this.refs.window.close(); - }; - </script> -</mk-settings-window> diff --git a/src/web/app/desktop/tags/settings.tag b/src/web/app/desktop/tags/settings.tag deleted file mode 100644 index f7ecfe3e8a..0000000000 --- a/src/web/app/desktop/tags/settings.tag +++ /dev/null @@ -1,395 +0,0 @@ -<mk-settings> - <div class="nav"> - <p class={ active: page == 'profile' } onmousedown={ setPage.bind(null, 'profile') }>%fa:user .fw%%i18n:desktop.tags.mk-settings.profile%</p> - <p class={ active: page == 'web' } onmousedown={ setPage.bind(null, 'web') }>%fa:desktop .fw%Web</p> - <p class={ active: page == 'notification' } onmousedown={ setPage.bind(null, 'notification') }>%fa:R bell .fw%通知</p> - <p class={ active: page == 'drive' } onmousedown={ setPage.bind(null, 'drive') }>%fa:cloud .fw%%i18n:desktop.tags.mk-settings.drive%</p> - <p class={ active: page == 'apps' } onmousedown={ setPage.bind(null, 'apps') }>%fa:puzzle-piece .fw%アプリ</p> - <p class={ active: page == 'twitter' } onmousedown={ setPage.bind(null, 'twitter') }>%fa:B twitter .fw%Twitter</p> - <p class={ active: page == 'security' } onmousedown={ setPage.bind(null, 'security') }>%fa:unlock-alt .fw%%i18n:desktop.tags.mk-settings.security%</p> - <p class={ active: page == 'api' } onmousedown={ setPage.bind(null, 'api') }>%fa:key .fw%API</p> - </div> - <div class="pages"> - <section class="profile" show={ page == 'profile' }> - <h1>%i18n:desktop.tags.mk-settings.profile%</h1> - <mk-profile-setting/> - </section> - - <section class="web" show={ page == 'web' }> - <h1>デザイン</h1> - <a href="/i/customize-home" class="ui button">ホームをカスタマイズ</a> - </section> - - <section class="drive" show={ page == 'drive' }> - <h1>%i18n:desktop.tags.mk-settings.drive%</h1> - <mk-drive-setting/> - </section> - - <section class="apps" show={ page == 'apps' }> - <h1>アプリケーション</h1> - <mk-authorized-apps/> - </section> - - <section class="twitter" show={ page == 'twitter' }> - <h1>Twitter</h1> - <mk-twitter-setting/> - </section> - - <section class="password" show={ page == 'security' }> - <h1>%i18n:desktop.tags.mk-settings.password%</h1> - <mk-password-setting/> - </section> - - <section class="2fa" show={ page == 'security' }> - <h1>%i18n:desktop.tags.mk-settings.2fa%</h1> - <mk-2fa-setting/> - </section> - - <section class="signin" show={ page == 'security' }> - <h1>サインイン履歴</h1> - <mk-signin-history/> - </section> - - <section class="api" show={ page == 'api' }> - <h1>API</h1> - <mk-api-info/> - </section> - </div> - <style> - :scope - display flex - width 100% - height 100% - - > .nav - flex 0 0 200px - width 100% - height 100% - padding 16px 0 0 0 - overflow auto - border-right solid 1px #ddd - - > p - display block - padding 10px 16px - margin 0 - color #666 - cursor pointer - user-select none - transition margin-left 0.2s ease - - > [data-fa] - margin-right 4px - - &:hover - color #555 - - &.active - margin-left 8px - color $theme-color !important - - > .pages - width 100% - height 100% - flex auto - overflow auto - - > section - margin 32px - - h1 - display block - margin 0 0 1em 0 - padding 0 0 8px 0 - font-size 1em - color #555 - border-bottom solid 1px #eee - - label.checkbox - > input - position absolute - top 0 - left 0 - - &:checked + p - color $theme-color - - > p - width calc(100% - 32px) - margin 0 0 0 32px - font-weight bold - - &:last-child - font-weight normal - color #999 - - </style> - <script> - this.page = 'profile'; - - this.setPage = page => { - this.page = page; - }; - </script> -</mk-settings> - -<mk-profile-setting> - <label class="avatar ui from group"> - <p>%i18n:desktop.tags.mk-profile-setting.avatar%</p><img class="avatar" src={ I.avatar_url + '?thumbnail&size=64' } alt="avatar"/> - <button class="ui" onclick={ avatar }>%i18n:desktop.tags.mk-profile-setting.choice-avatar%</button> - </label> - <label class="ui from group"> - <p>%i18n:desktop.tags.mk-profile-setting.name%</p> - <input ref="accountName" type="text" value={ I.name } class="ui"/> - </label> - <label class="ui from group"> - <p>%i18n:desktop.tags.mk-profile-setting.location%</p> - <input ref="accountLocation" type="text" value={ I.profile.location } class="ui"/> - </label> - <label class="ui from group"> - <p>%i18n:desktop.tags.mk-profile-setting.description%</p> - <textarea ref="accountDescription" class="ui">{ I.description }</textarea> - </label> - <label class="ui from group"> - <p>%i18n:desktop.tags.mk-profile-setting.birthday%</p> - <input ref="accountBirthday" type="date" value={ I.profile.birthday } class="ui"/> - </label> - <button class="ui primary" onclick={ updateAccount }>%i18n:desktop.tags.mk-profile-setting.save%</button> - <style> - :scope - display block - - > .avatar - > img - display inline-block - vertical-align top - width 64px - height 64px - border-radius 4px - - > button - margin-left 8px - - </style> - <script> - import updateAvatar from '../scripts/update-avatar'; - import notify from '../scripts/notify'; - - this.mixin('i'); - this.mixin('api'); - - this.avatar = () => { - updateAvatar(this.I); - }; - - this.updateAccount = () => { - this.api('i/update', { - name: this.refs.accountName.value, - location: this.refs.accountLocation.value || null, - description: this.refs.accountDescription.value || null, - birthday: this.refs.accountBirthday.value || null - }).then(() => { - notify('プロフィールを更新しました'); - }); - }; - </script> -</mk-profile-setting> - -<mk-api-info> - <p>Token:<code>{ I.token }</code></p> - <p>APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。</p> - <p>アカウントを乗っ取られてしまう可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。</p> - <p>万が一このトークンが漏れたりその可能性がある場合は<a class="regenerate" onclick={ regenerateToken }>トークンを再生成</a>できます。(副作用として、ログインしているすべてのデバイスでログアウトが発生します)</p> - <style> - :scope - display block - color #4a535a - - code - padding 4px - background #eee - </style> - <script> - import passwordDialog from '../scripts/password-dialog'; - - this.mixin('i'); - this.mixin('api'); - - this.regenerateToken = () => { - passwordDialog('%i18n:desktop.tags.mk-api-info.regenerate-token%', password => { - this.api('i/regenerate_token', { - password: password - }); - }); - }; - </script> -</mk-api-info> - -<mk-password-setting> - <button onclick={ reset } class="ui primary">%i18n:desktop.tags.mk-password-setting.reset%</button> - <style> - :scope - display block - color #4a535a - </style> - <script> - import passwordDialog from '../scripts/password-dialog'; - import dialog from '../scripts/dialog'; - import notify from '../scripts/notify'; - - this.mixin('i'); - this.mixin('api'); - - this.reset = () => { - passwordDialog('%i18n:desktop.tags.mk-password-setting.enter-current-password%', currentPassword => { - passwordDialog('%i18n:desktop.tags.mk-password-setting.enter-new-password%', newPassword => { - passwordDialog('%i18n:desktop.tags.mk-password-setting.enter-new-password-again%', newPassword2 => { - if (newPassword !== newPassword2) { - dialog(null, '%i18n:desktop.tags.mk-password-setting.not-match%', [{ - text: 'OK' - }]); - return; - } - this.api('i/change_password', { - current_password: currentPassword, - new_password: newPassword - }).then(() => { - notify('%i18n:desktop.tags.mk-password-setting.changed%'); - }); - }); - }); - }); - }; - </script> -</mk-password-setting> - -<mk-2fa-setting> - <p>%i18n:desktop.tags.mk-2fa-setting.intro%<a href="%i18n:desktop.tags.mk-2fa-setting.url%" target="_blank">%i18n:desktop.tags.mk-2fa-setting.detail%</a></p> - <div class="ui info warn"><p>%fa:exclamation-triangle%%i18n:desktop.tags.mk-2fa-setting.caution%</p></div> - <p if={ !data && !I.two_factor_enabled }><button onclick={ register } class="ui primary">%i18n:desktop.tags.mk-2fa-setting.register%</button></p> - <virtual if={ I.two_factor_enabled }> - <p>%i18n:desktop.tags.mk-2fa-setting.already-registered%</p> - <button onclick={ unregister } class="ui">%i18n:desktop.tags.mk-2fa-setting.unregister%</button> - </virtual> - <div if={ data }> - <ol> - <li>%i18n:desktop.tags.mk-2fa-setting.authenticator% <a href="https://support.google.com/accounts/answer/1066447" target="_blank">%i18n:desktop.tags.mk-2fa-setting.howtoinstall%</a></li> - <li>%i18n:desktop.tags.mk-2fa-setting.scan%<br><img src={ data.qr }></li> - <li>%i18n:desktop.tags.mk-2fa-setting.done%<br> - <input type="number" ref="token" class="ui"> - <button onclick={ submit } class="ui primary">%i18n:desktop.tags.mk-2fa-setting.submit%</button> - </li> - </ol> - <div class="ui info"><p>%fa:info-circle%%i18n:desktop.tags.mk-2fa-setting.info%</p></div> - </div> - <style> - :scope - display block - color #4a535a - - </style> - <script> - import passwordDialog from '../scripts/password-dialog'; - import notify from '../scripts/notify'; - - this.mixin('i'); - this.mixin('api'); - - this.register = () => { - passwordDialog('%i18n:desktop.tags.mk-2fa-setting.enter-password%', password => { - this.api('i/2fa/register', { - password: password - }).then(data => { - this.update({ - data: data - }); - }); - }); - }; - - this.unregister = () => { - passwordDialog('%i18n:desktop.tags.mk-2fa-setting.enter-password%', password => { - this.api('i/2fa/unregister', { - password: password - }).then(data => { - notify('%i18n:desktop.tags.mk-2fa-setting.unregistered%'); - this.I.two_factor_enabled = false; - this.I.update(); - }); - }); - }; - - this.submit = () => { - this.api('i/2fa/done', { - token: this.refs.token.value - }).then(() => { - notify('%i18n:desktop.tags.mk-2fa-setting.success%'); - this.I.two_factor_enabled = true; - this.I.update(); - }).catch(() => { - notify('%i18n:desktop.tags.mk-2fa-setting.failed%'); - }); - }; - </script> -</mk-2fa-setting> - -<mk-drive-setting> - <svg viewBox="0 0 1 1" preserveAspectRatio="none"> - <circle - riot-r={ r } - cx="50%" cy="50%" - fill="none" - stroke-width="0.1" - stroke="rgba(0, 0, 0, 0.05)"/> - <circle - riot-r={ r } - cx="50%" cy="50%" - riot-stroke-dasharray={ Math.PI * (r * 2) } - riot-stroke-dashoffset={ strokeDashoffset } - fill="none" - stroke-width="0.1" - riot-stroke={ color }/> - <text x="50%" y="50%" dy="0.05" text-anchor="middle">{ (usageP * 100).toFixed(0) }%</text> - </svg> - - <style> - :scope - display block - color #4a535a - - > svg - display block - height 128px - - > circle - transform-origin center - transform rotate(-90deg) - transition stroke-dashoffset 0.5s ease - - > text - font-size 0.15px - fill rgba(0, 0, 0, 0.6) - - </style> - <script> - this.mixin('api'); - - this.r = 0.4; - - this.on('mount', () => { - this.api('drive').then(info => { - const usageP = info.usage / info.capacity; - const color = `hsl(${180 - (usageP * 180)}, 80%, 70%)`; - const strokeDashoffset = (1 - usageP) * (Math.PI * (this.r * 2)); - - this.update({ - color, - strokeDashoffset, - usageP, - usage: info.usage, - capacity: info.capacity - }); - }); - }); - </script> -</mk-drive-setting> diff --git a/src/web/app/desktop/tags/sub-post-content.tag b/src/web/app/desktop/tags/sub-post-content.tag deleted file mode 100644 index 8989ff1c5b..0000000000 --- a/src/web/app/desktop/tags/sub-post-content.tag +++ /dev/null @@ -1,54 +0,0 @@ -<mk-sub-post-content> - <div class="body"> - <a class="reply" if={ post.reply_id }> - %fa:reply% - </a> - <span ref="text"></span> - <a class="quote" if={ post.repost_id } href={ '/post:' + post.repost_id }>RP: ...</a> - </div> - <details if={ post.media }> - <summary>({ post.media.length }つのメディア)</summary> - <mk-images-viewer images={ post.media }/> - </details> - <details if={ post.poll }> - <summary>投票</summary> - <mk-poll post={ post }/> - </details> - <style> - :scope - display block - overflow-wrap break-word - - > .body - > .reply - margin-right 6px - color #717171 - - > .quote - margin-left 4px - font-style oblique - color #a0bf46 - - mk-poll - font-size 80% - - </style> - <script> - import compile from '../../common/scripts/text-compiler'; - - this.mixin('user-preview'); - - this.post = this.opts.post; - - this.on('mount', () => { - if (this.post.text) { - const tokens = this.post.ast; - this.refs.text.innerHTML = compile(tokens, false); - - Array.from(this.refs.text.children).forEach(e => { - if (e.tagName == 'MK-URL') riot.mount(e); - }); - } - }); - </script> -</mk-sub-post-content> diff --git a/src/web/app/desktop/tags/timeline.tag b/src/web/app/desktop/tags/timeline.tag deleted file mode 100644 index 08e658a3c6..0000000000 --- a/src/web/app/desktop/tags/timeline.tag +++ /dev/null @@ -1,709 +0,0 @@ -<mk-timeline> - <virtual each={ post, i in posts }> - <mk-timeline-post post={ post }/> - <p class="date" if={ i != posts.length - 1 && post._date != posts[i + 1]._date }><span>%fa:angle-up%{ post._datetext }</span><span>%fa:angle-down%{ posts[i + 1]._datetext }</span></p> - </virtual> - <footer data-yield="footer"> - <yield from="footer"/> - </footer> - <style> - :scope - display block - - > .date - display block - margin 0 - line-height 32px - font-size 14px - text-align center - color #aaa - background #fdfdfd - border-bottom solid 1px #eaeaea - - span - margin 0 16px - - [data-fa] - margin-right 8px - - > footer - padding 16px - text-align center - color #ccc - border-top solid 1px #eaeaea - border-bottom-left-radius 4px - border-bottom-right-radius 4px - - </style> - <script> - this.posts = []; - - this.on('update', () => { - this.posts.forEach(post => { - const date = new Date(post.created_at).getDate(); - const month = new Date(post.created_at).getMonth() + 1; - post._date = date; - post._datetext = `${month}月 ${date}日`; - }); - }); - - this.setPosts = posts => { - this.update({ - posts: posts - }); - }; - - this.prependPosts = posts => { - posts.forEach(post => { - this.posts.push(post); - this.update(); - }); - } - - this.addPost = post => { - this.posts.unshift(post); - this.update(); - }; - - this.tail = () => { - return this.posts[this.posts.length - 1]; - }; - - this.clear = () => { - this.posts = []; - this.update(); - }; - - this.focus = () => { - this.root.children[0].focus(); - }; - - </script> -</mk-timeline> - -<mk-timeline-post tabindex="-1" title={ title } onkeydown={ onKeyDown } dblclick={ onDblClick }> - <div class="reply-to" if={ p.reply }> - <mk-timeline-post-sub post={ p.reply }/> - </div> - <div class="repost" if={ isRepost }> - <p> - <a class="avatar-anchor" href={ '/' + post.user.username } data-user-preview={ post.user_id }> - <img class="avatar" src={ post.user.avatar_url + '?thumbnail&size=32' } alt="avatar"/> - </a> - %fa:retweet%{'%i18n:desktop.tags.mk-timeline-post.reposted-by%'.substr(0, '%i18n:desktop.tags.mk-timeline-post.reposted-by%'.indexOf('{'))}<a class="name" href={ '/' + post.user.username } data-user-preview={ post.user_id }>{ post.user.name }</a>{'%i18n:desktop.tags.mk-timeline-post.reposted-by%'.substr('%i18n:desktop.tags.mk-timeline-post.reposted-by%'.indexOf('}') + 1)} - </p> - <mk-time time={ post.created_at }/> - </div> - <article> - <a class="avatar-anchor" href={ '/' + p.user.username }> - <img class="avatar" src={ p.user.avatar_url + '?thumbnail&size=64' } alt="avatar" data-user-preview={ p.user.id }/> - </a> - <div class="main"> - <header> - <a class="name" href={ '/' + p.user.username } data-user-preview={ p.user.id }>{ p.user.name }</a> - <span class="is-bot" if={ p.user.is_bot }>bot</span> - <span class="username">@{ p.user.username }</span> - <div class="info"> - <span class="app" if={ p.app }>via <b>{ p.app.name }</b></span> - <a class="created-at" href={ url }> - <mk-time time={ p.created_at }/> - </a> - </div> - </header> - <div class="body"> - <div class="text" ref="text"> - <p class="channel" if={ p.channel != null }><a href={ _CH_URL_ + '/' + p.channel.id } target="_blank">{ p.channel.title }</a>:</p> - <a class="reply" if={ p.reply }> - %fa:reply% - </a> - <p class="dummy"></p> - <a class="quote" if={ p.repost != null }>RP:</a> - </div> - <div class="media" if={ p.media }> - <mk-images-viewer images={ p.media }/> - </div> - <mk-poll if={ p.poll } post={ p } ref="pollViewer"/> - <div class="repost" if={ p.repost }>%fa:quote-right -flip-h% - <mk-post-preview class="repost" post={ p.repost }/> - </div> - </div> - <footer> - <mk-reactions-viewer post={ p } ref="reactionsViewer"/> - <button onclick={ reply } title="%i18n:desktop.tags.mk-timeline-post.reply%"> - %fa:reply%<p class="count" if={ p.replies_count > 0 }>{ p.replies_count }</p> - </button> - <button onclick={ repost } title="%i18n:desktop.tags.mk-timeline-post.repost%"> - %fa:retweet%<p class="count" if={ p.repost_count > 0 }>{ p.repost_count }</p> - </button> - <button class={ reacted: p.my_reaction != null } onclick={ react } ref="reactButton" title="%i18n:desktop.tags.mk-timeline-post.add-reaction%"> - %fa:plus%<p class="count" if={ p.reactions_count > 0 }>{ p.reactions_count }</p> - </button> - <button onclick={ menu } ref="menuButton"> - %fa:ellipsis-h% - </button> - <button onclick={ toggleDetail } title="%i18n:desktop.tags.mk-timeline-post.detail"> - <virtual if={ !isDetailOpened }>%fa:caret-down%</virtual> - <virtual if={ isDetailOpened }>%fa:caret-up%</virtual> - </button> - </footer> - </div> - </article> - <div class="detail" if={ isDetailOpened }> - <mk-post-status-graph width="462" height="130" post={ p }/> - </div> - <style> - :scope - display block - margin 0 - padding 0 - background #fff - border-bottom solid 1px #eaeaea - - &:first-child - border-top-left-radius 6px - border-top-right-radius 6px - - > .repost - border-top-left-radius 6px - border-top-right-radius 6px - - &:last-of-type - border-bottom none - - &:focus - z-index 1 - - &:after - content "" - pointer-events none - position absolute - top 2px - right 2px - bottom 2px - left 2px - border 2px solid rgba($theme-color, 0.3) - border-radius 4px - - > .repost - color #9dbb00 - background linear-gradient(to bottom, #edfde2 0%, #fff 100%) - - > p - margin 0 - padding 16px 32px - line-height 28px - - .avatar-anchor - display inline-block - - .avatar - vertical-align bottom - width 28px - height 28px - margin 0 8px 0 0 - border-radius 6px - - [data-fa] - margin-right 4px - - .name - font-weight bold - - > mk-time - position absolute - top 16px - right 32px - font-size 0.9em - line-height 28px - - & + article - padding-top 8px - - > .reply-to - padding 0 16px - background rgba(0, 0, 0, 0.0125) - - > mk-post-preview - background transparent - - > article - padding 28px 32px 18px 32px - - &:after - content "" - display block - clear both - - &:hover - > .main > footer > button - color #888 - - > .avatar-anchor - display block - float left - margin 0 16px 10px 0 - position -webkit-sticky - position sticky - top 74px - - > .avatar - display block - width 58px - height 58px - margin 0 - border-radius 8px - vertical-align bottom - - > .main - float left - width calc(100% - 74px) - - > header - display flex - margin-bottom 4px - white-space nowrap - line-height 1.4 - - > .name - display block - margin 0 .5em 0 0 - padding 0 - overflow hidden - color #777 - font-size 1em - font-weight 700 - text-align left - text-decoration none - text-overflow ellipsis - - &:hover - text-decoration underline - - > .is-bot - text-align left - margin 0 .5em 0 0 - padding 1px 6px - font-size 12px - color #aaa - border solid 1px #ddd - border-radius 3px - - > .username - text-align left - margin 0 .5em 0 0 - color #ccc - - > .info - margin-left auto - text-align right - font-size 0.9em - - > .app - margin-right 8px - padding-right 8px - color #ccc - border-right solid 1px #eaeaea - - > .created-at - color #c0c0c0 - - > .body - - > .text - cursor default - display block - margin 0 - padding 0 - overflow-wrap break-word - font-size 1.1em - color #717171 - - > .dummy - display none - - mk-url-preview - margin-top 8px - - > .channel - margin 0 - - > .reply - margin-right 8px - color #717171 - - > .quote - margin-left 4px - font-style oblique - color #a0bf46 - - code - padding 4px 8px - margin 0 0.5em - font-size 80% - color #525252 - background #f8f8f8 - border-radius 2px - - pre > code - padding 16px - margin 0 - - [data-is-me]:after - content "you" - padding 0 4px - margin-left 4px - font-size 80% - color $theme-color-foreground - background $theme-color - border-radius 4px - - > .media - > img - display block - max-width 100% - - > mk-poll - font-size 80% - - > .repost - margin 8px 0 - - > [data-fa]:first-child - position absolute - top -8px - left -8px - z-index 1 - color #c0dac6 - font-size 28px - background #fff - - > mk-post-preview - padding 16px - border dashed 1px #c0dac6 - border-radius 8px - - > footer - > button - margin 0 28px 0 0 - padding 0 8px - line-height 32px - font-size 1em - color #ddd - background transparent - border none - cursor pointer - - &:hover - color #666 - - > .count - display inline - margin 0 0 0 8px - color #999 - - &.reacted - color $theme-color - - &:last-child - position absolute - right 0 - margin 0 - - > .detail - padding-top 4px - background rgba(0, 0, 0, 0.0125) - - </style> - <script> - import compile from '../../common/scripts/text-compiler'; - import dateStringify from '../../common/scripts/date-stringify'; - - this.mixin('i'); - this.mixin('api'); - this.mixin('user-preview'); - - this.mixin('stream'); - this.connection = this.stream.getConnection(); - this.connectionId = this.stream.use(); - - this.isDetailOpened = false; - - this.set = post => { - this.post = post; - this.isRepost = this.post.repost && this.post.text == null && this.post.media_ids == null && this.post.poll == null; - this.p = this.isRepost ? this.post.repost : this.post; - this.p.reactions_count = this.p.reaction_counts ? Object.keys(this.p.reaction_counts).map(key => this.p.reaction_counts[key]).reduce((a, b) => a + b) : 0; - this.title = dateStringify(this.p.created_at); - this.url = `/${this.p.user.username}/${this.p.id}`; - }; - - this.set(this.opts.post); - - this.refresh = post => { - this.set(post); - this.update(); - if (this.refs.reactionsViewer) this.refs.reactionsViewer.update({ - post - }); - if (this.refs.pollViewer) this.refs.pollViewer.init(post); - }; - - this.onStreamPostUpdated = data => { - const post = data.post; - if (post.id == this.post.id) { - this.refresh(post); - } - }; - - this.onStreamConnected = () => { - this.capture(); - }; - - this.capture = withHandler => { - if (this.SIGNIN) { - this.connection.send({ - type: 'capture', - id: this.post.id - }); - if (withHandler) this.connection.on('post-updated', this.onStreamPostUpdated); - } - }; - - this.decapture = withHandler => { - if (this.SIGNIN) { - this.connection.send({ - type: 'decapture', - id: this.post.id - }); - if (withHandler) this.connection.off('post-updated', this.onStreamPostUpdated); - } - }; - - this.on('mount', () => { - this.capture(true); - - if (this.SIGNIN) { - this.connection.on('_connected_', this.onStreamConnected); - } - - if (this.p.text) { - const tokens = this.p.ast; - - this.refs.text.innerHTML = this.refs.text.innerHTML.replace('<p class="dummy"></p>', compile(tokens)); - - Array.from(this.refs.text.children).forEach(e => { - if (e.tagName == 'MK-URL') riot.mount(e); - }); - - // URLをプレビュー - tokens - .filter(t => (t.type == 'url' || t.type == 'link') && !t.silent) - .map(t => { - riot.mount(this.refs.text.appendChild(document.createElement('mk-url-preview')), { - url: t.url - }); - }); - } - }); - - this.on('unmount', () => { - this.decapture(true); - this.connection.off('_connected_', this.onStreamConnected); - this.stream.dispose(this.connectionId); - }); - - this.reply = () => { - riot.mount(document.body.appendChild(document.createElement('mk-post-form-window')), { - reply: this.p - }); - }; - - this.repost = () => { - riot.mount(document.body.appendChild(document.createElement('mk-repost-form-window')), { - post: this.p - }); - }; - - this.react = () => { - riot.mount(document.body.appendChild(document.createElement('mk-reaction-picker')), { - source: this.refs.reactButton, - post: this.p - }); - }; - - this.menu = () => { - riot.mount(document.body.appendChild(document.createElement('mk-post-menu')), { - source: this.refs.menuButton, - post: this.p - }); - }; - - this.toggleDetail = () => { - this.update({ - isDetailOpened: !this.isDetailOpened - }); - }; - - this.onKeyDown = e => { - let shouldBeCancel = true; - - switch (true) { - case e.which == 38: // [↑] - case e.which == 74: // [j] - case e.which == 9 && e.shiftKey: // [Shift] + [Tab] - focus(this.root, e => e.previousElementSibling); - break; - - case e.which == 40: // [↓] - case e.which == 75: // [k] - case e.which == 9: // [Tab] - focus(this.root, e => e.nextElementSibling); - break; - - case e.which == 81: // [q] - case e.which == 69: // [e] - this.repost(); - break; - - case e.which == 70: // [f] - case e.which == 76: // [l] - this.like(); - break; - - case e.which == 82: // [r] - this.reply(); - break; - - default: - shouldBeCancel = false; - } - - if (shouldBeCancel) e.preventDefault(); - }; - - this.onDblClick = () => { - riot.mount(document.body.appendChild(document.createElement('mk-detailed-post-window')), { - post: this.p.id - }); - }; - - function focus(el, fn) { - const target = fn(el); - if (target) { - if (target.hasAttribute('tabindex')) { - target.focus(); - } else { - focus(target, fn); - } - } - } - </script> -</mk-timeline-post> - -<mk-timeline-post-sub title={ title }> - <article> - <a class="avatar-anchor" href={ '/' + post.user.username }> - <img class="avatar" src={ post.user.avatar_url + '?thumbnail&size=64' } alt="avatar" data-user-preview={ post.user_id }/> - </a> - <div class="main"> - <header> - <a class="name" href={ '/' + post.user.username } data-user-preview={ post.user_id }>{ post.user.name }</a> - <span class="username">@{ post.user.username }</span> - <a class="created-at" href={ '/' + post.user.username + '/' + post.id }> - <mk-time time={ post.created_at }/> - </a> - </header> - <div class="body"> - <mk-sub-post-content class="text" post={ post }/> - </div> - </div> - </article> - <style> - :scope - display block - margin 0 - padding 0 - font-size 0.9em - - > article - padding 16px - - &:after - content "" - display block - clear both - - &:hover - > .main > footer > button - color #888 - - > .avatar-anchor - display block - float left - margin 0 14px 0 0 - - > .avatar - display block - width 52px - height 52px - margin 0 - border-radius 8px - vertical-align bottom - - > .main - float left - width calc(100% - 66px) - - > header - display flex - margin-bottom 2px - white-space nowrap - line-height 21px - - > .name - display block - margin 0 .5em 0 0 - padding 0 - overflow hidden - color #607073 - font-size 1em - font-weight 700 - text-align left - text-decoration none - text-overflow ellipsis - - &:hover - text-decoration underline - - > .username - text-align left - margin 0 .5em 0 0 - color #d1d8da - - > .created-at - margin-left auto - color #b2b8bb - - > .body - - > .text - cursor default - margin 0 - padding 0 - font-size 1.1em - color #717171 - - pre - max-height 120px - font-size 80% - - </style> - <script> - import dateStringify from '../../common/scripts/date-stringify'; - - this.mixin('user-preview'); - - this.post = this.opts.post; - this.title = dateStringify(this.post.created_at); - </script> -</mk-timeline-post-sub> diff --git a/src/web/app/desktop/tags/ui.tag b/src/web/app/desktop/tags/ui.tag deleted file mode 100644 index bc0a937a53..0000000000 --- a/src/web/app/desktop/tags/ui.tag +++ /dev/null @@ -1,896 +0,0 @@ -<mk-ui> - <mk-ui-header page={ opts.page }/> - <mk-set-avatar-suggestion if={ SIGNIN && I.avatar_id == null }/> - <mk-set-banner-suggestion if={ SIGNIN && I.banner_id == null }/> - <div class="content"> - <yield /> - </div> - <mk-stream-indicator if={ SIGNIN }/> - <style> - :scope - display block - </style> - <script> - this.mixin('i'); - - this.openPostForm = () => { - riot.mount(document.body.appendChild(document.createElement('mk-post-form-window'))); - }; - - this.on('mount', () => { - document.addEventListener('keydown', this.onkeydown); - }); - - this.on('unmount', () => { - document.removeEventListener('keydown', this.onkeydown); - }); - - this.onkeydown = e => { - if (e.target.tagName == 'INPUT' || e.target.tagName == 'TEXTAREA') return; - - if (e.which == 80 || e.which == 78) { // p or n - e.preventDefault(); - this.openPostForm(); - } - }; - </script> -</mk-ui> - -<mk-ui-header> - <mk-donation if={ SIGNIN && I.client_settings.show_donation }/> - <mk-special-message/> - <div class="main"> - <div class="backdrop"></div> - <div class="main"> - <div class="container"> - <div class="left"> - <mk-ui-header-nav page={ opts.page }/> - </div> - <div class="right"> - <mk-ui-header-search/> - <mk-ui-header-account if={ SIGNIN }/> - <mk-ui-header-notifications if={ SIGNIN }/> - <mk-ui-header-post-button if={ SIGNIN }/> - <mk-ui-header-clock/> - </div> - </div> - </div> - </div> - <style> - :scope - display block - position -webkit-sticky - position sticky - top 0 - z-index 1024 - width 100% - box-shadow 0 1px 1px rgba(0, 0, 0, 0.075) - - > .main - - > .backdrop - position absolute - top 0 - z-index 1023 - width 100% - height 48px - backdrop-filter blur(12px) - background #f7f7f7 - - &:after - content "" - display block - width 100% - height 48px - background-image url(/assets/desktop/header-logo.svg) - background-size 46px - background-position center - background-repeat no-repeat - opacity 0.3 - - > .main - z-index 1024 - margin 0 - padding 0 - background-clip content-box - font-size 0.9rem - user-select none - - > .container - width 100% - max-width 1300px - margin 0 auto - - &:after - content "" - display block - clear both - - > .left - float left - height 3rem - - > .right - float right - height 48px - - @media (max-width 1100px) - > mk-ui-header-search - display none - - </style> - <script>this.mixin('i');</script> -</mk-ui-header> - -<mk-ui-header-search> - <form class="search" onsubmit={ onsubmit }> - %fa:search% - <input ref="q" type="search" placeholder="%i18n:desktop.tags.mk-ui-header-search.placeholder%"/> - <div class="result"></div> - </form> - <style> - :scope - - > form - display block - float left - - > [data-fa] - display block - position absolute - top 0 - left 0 - width 48px - text-align center - line-height 48px - color #9eaba8 - pointer-events none - - > * - vertical-align middle - - > input - user-select text - cursor auto - margin 8px 0 0 0 - padding 6px 18px 6px 36px - width 14em - height 32px - font-size 1em - background rgba(0, 0, 0, 0.05) - outline none - //border solid 1px #ddd - border none - border-radius 16px - transition color 0.5s ease, border 0.5s ease - font-family FontAwesome, sans-serif - - &:placeholder-shown - color #9eaba8 - - &:hover - background rgba(0, 0, 0, 0.08) - - &:focus - box-shadow 0 0 0 2px rgba($theme-color, 0.5) !important - - </style> - <script> - this.mixin('page'); - - this.onsubmit = e => { - e.preventDefault(); - this.page('/search:' + this.refs.q.value); - }; - </script> -</mk-ui-header-search> - -<mk-ui-header-post-button> - <button onclick={ post } title="%i18n:desktop.tags.mk-ui-header-post-button.post%">%fa:pencil-alt%</button> - <style> - :scope - display inline-block - padding 8px - height 100% - vertical-align top - - > button - display inline-block - margin 0 - padding 0 10px - height 100% - font-size 1.2em - font-weight normal - text-decoration none - color $theme-color-foreground - background $theme-color !important - outline none - border none - border-radius 4px - transition background 0.1s ease - cursor pointer - - * - pointer-events none - - &:hover - background lighten($theme-color, 10%) !important - - &:active - background darken($theme-color, 10%) !important - transition background 0s ease - - </style> - <script> - this.post = e => { - this.parent.parent.openPostForm(); - }; - </script> -</mk-ui-header-post-button> - -<mk-ui-header-notifications> - <button data-active={ isOpen } onclick={ toggle } title="%i18n:desktop.tags.mk-ui-header-notifications.title%"> - %fa:R bell%<virtual if={ hasUnreadNotifications }>%fa:circle%</virtual> - </button> - <div class="notifications" if={ isOpen }> - <mk-notifications/> - </div> - <style> - :scope - display block - float left - - > button - display block - margin 0 - padding 0 - width 32px - color #9eaba8 - border none - background transparent - cursor pointer - - * - pointer-events none - - &:hover - &[data-active='true'] - color darken(#9eaba8, 20%) - - &:active - color darken(#9eaba8, 30%) - - > [data-fa].bell - font-size 1.2em - line-height 48px - - > [data-fa].circle - margin-left -5px - vertical-align super - font-size 10px - color $theme-color - - > .notifications - display block - position absolute - top 56px - right -72px - width 300px - background #fff - border-radius 4px - box-shadow 0 1px 4px rgba(0, 0, 0, 0.25) - - &:before - content "" - pointer-events none - display block - position absolute - top -28px - right 74px - border-top solid 14px transparent - border-right solid 14px transparent - border-bottom solid 14px rgba(0, 0, 0, 0.1) - border-left solid 14px transparent - - &:after - content "" - pointer-events none - display block - position absolute - top -27px - right 74px - border-top solid 14px transparent - border-right solid 14px transparent - border-bottom solid 14px #fff - border-left solid 14px transparent - - > mk-notifications - max-height 350px - font-size 1rem - overflow auto - - </style> - <script> - import contains from '../../common/scripts/contains'; - - this.mixin('i'); - this.mixin('api'); - - if (this.SIGNIN) { - this.mixin('stream'); - this.connection = this.stream.getConnection(); - this.connectionId = this.stream.use(); - } - - this.isOpen = false; - - this.on('mount', () => { - if (this.SIGNIN) { - this.connection.on('read_all_notifications', this.onReadAllNotifications); - this.connection.on('unread_notification', this.onUnreadNotification); - - // Fetch count of unread notifications - this.api('notifications/get_unread_count').then(res => { - if (res.count > 0) { - this.update({ - hasUnreadNotifications: true - }); - } - }); - } - }); - - this.on('unmount', () => { - if (this.SIGNIN) { - this.connection.off('read_all_notifications', this.onReadAllNotifications); - this.connection.off('unread_notification', this.onUnreadNotification); - this.stream.dispose(this.connectionId); - } - }); - - this.onReadAllNotifications = () => { - this.update({ - hasUnreadNotifications: false - }); - }; - - this.onUnreadNotification = () => { - this.update({ - hasUnreadNotifications: true - }); - }; - - this.toggle = () => { - this.isOpen ? this.close() : this.open(); - }; - - this.open = () => { - this.update({ - isOpen: true - }); - document.querySelectorAll('body *').forEach(el => { - el.addEventListener('mousedown', this.mousedown); - }); - }; - - this.close = () => { - this.update({ - isOpen: false - }); - document.querySelectorAll('body *').forEach(el => { - el.removeEventListener('mousedown', this.mousedown); - }); - }; - - this.mousedown = e => { - e.preventDefault(); - if (!contains(this.root, e.target) && this.root != e.target) this.close(); - return false; - }; - </script> -</mk-ui-header-notifications> - -<mk-ui-header-nav> - <ul> - <virtual if={ SIGNIN }> - <li class="home { active: page == 'home' }"> - <a href={ _URL_ }> - %fa:home% - <p>%i18n:desktop.tags.mk-ui-header-nav.home%</p> - </a> - </li> - <li class="messaging"> - <a onclick={ messaging }> - %fa:comments% - <p>%i18n:desktop.tags.mk-ui-header-nav.messaging%</p> - <virtual if={ hasUnreadMessagingMessages }>%fa:circle%</virtual> - </a> - </li> - </virtual> - <li class="ch"> - <a href={ _CH_URL_ } target="_blank"> - %fa:tv% - <p>%i18n:desktop.tags.mk-ui-header-nav.ch%</p> - </a> - </li> - <li class="info"> - <a href="https://twitter.com/misskey_xyz" target="_blank"> - %fa:info% - <p>%i18n:desktop.tags.mk-ui-header-nav.info%</p> - </a> - </li> - </ul> - <style> - :scope - display inline-block - margin 0 - padding 0 - line-height 3rem - vertical-align top - - > ul - display inline-block - margin 0 - padding 0 - vertical-align top - line-height 3rem - list-style none - - > li - display inline-block - vertical-align top - height 48px - line-height 48px - - &.active - > a - border-bottom solid 3px $theme-color - - > a - display inline-block - z-index 1 - height 100% - padding 0 24px - font-size 13px - font-variant small-caps - color #9eaba8 - text-decoration none - transition none - cursor pointer - - * - pointer-events none - - &:hover - color darken(#9eaba8, 20%) - text-decoration none - - > [data-fa]:first-child - margin-right 8px - - > [data-fa]:last-child - margin-left 5px - font-size 10px - color $theme-color - - @media (max-width 1100px) - margin-left -5px - - > p - display inline - margin 0 - - @media (max-width 1100px) - display none - - @media (max-width 700px) - padding 0 12px - - </style> - <script> - this.mixin('i'); - this.mixin('api'); - - if (this.SIGNIN) { - this.mixin('stream'); - this.connection = this.stream.getConnection(); - this.connectionId = this.stream.use(); - } - - this.page = this.opts.page; - - this.on('mount', () => { - if (this.SIGNIN) { - this.connection.on('read_all_messaging_messages', this.onReadAllMessagingMessages); - this.connection.on('unread_messaging_message', this.onUnreadMessagingMessage); - - // Fetch count of unread messaging messages - this.api('messaging/unread').then(res => { - if (res.count > 0) { - this.update({ - hasUnreadMessagingMessages: true - }); - } - }); - } - }); - - this.on('unmount', () => { - if (this.SIGNIN) { - this.connection.off('read_all_messaging_messages', this.onReadAllMessagingMessages); - this.connection.off('unread_messaging_message', this.onUnreadMessagingMessage); - this.stream.dispose(this.connectionId); - } - }); - - this.onReadAllMessagingMessages = () => { - this.update({ - hasUnreadMessagingMessages: false - }); - }; - - this.onUnreadMessagingMessage = () => { - this.update({ - hasUnreadMessagingMessages: true - }); - }; - - this.messaging = () => { - riot.mount(document.body.appendChild(document.createElement('mk-messaging-window'))); - }; - </script> -</mk-ui-header-nav> - -<mk-ui-header-clock> - <div class="header"> - <time ref="time"> - <span class="yyyymmdd">{ yyyy }/{ mm }/{ dd }</span> - <br> - <span class="hhnn">{ hh }<span style="visibility:{ now.getSeconds() % 2 == 0 ? 'visible' : 'hidden' }">:</span>{ nn }</span> - </time> - </div> - <div class="content"> - <mk-analog-clock/> - </div> - <style> - :scope - display inline-block - overflow visible - - > .header - padding 0 12px - text-align center - font-size 10px - - &, * - cursor: default - - &:hover - background #899492 - - & + .content - visibility visible - - > time - color #fff !important - - * - color #fff !important - - &:after - content "" - display block - clear both - - > time - display table-cell - vertical-align middle - height 48px - color #9eaba8 - - > .yyyymmdd - opacity 0.7 - - > .content - visibility hidden - display block - position absolute - top auto - right 0 - z-index 3 - margin 0 - padding 0 - width 256px - background #899492 - - </style> - <script> - this.now = new Date(); - - this.draw = () => { - const now = this.now = new Date(); - this.yyyy = now.getFullYear(); - this.mm = ('0' + (now.getMonth() + 1)).slice(-2); - this.dd = ('0' + now.getDate()).slice(-2); - this.hh = ('0' + now.getHours()).slice(-2); - this.nn = ('0' + now.getMinutes()).slice(-2); - this.update(); - }; - - this.on('mount', () => { - this.draw(); - this.clock = setInterval(this.draw, 1000); - }); - - this.on('unmount', () => { - clearInterval(this.clock); - }); - </script> -</mk-ui-header-clock> - -<mk-ui-header-account> - <button class="header" data-active={ isOpen.toString() } onclick={ toggle }> - <span class="username">{ I.username }<virtual if={ !isOpen }>%fa:angle-down%</virtual><virtual if={ isOpen }>%fa:angle-up%</virtual></span> - <img class="avatar" src={ I.avatar_url + '?thumbnail&size=64' } alt="avatar"/> - </button> - <div class="menu" if={ isOpen }> - <ul> - <li> - <a href={ '/' + I.username }>%fa:user%%i18n:desktop.tags.mk-ui-header-account.profile%%fa:angle-right%</a> - </li> - <li onclick={ drive }> - <p>%fa:cloud%%i18n:desktop.tags.mk-ui-header-account.drive%%fa:angle-right%</p> - </li> - <li> - <a href="/i/mentions">%fa:at%%i18n:desktop.tags.mk-ui-header-account.mentions%%fa:angle-right%</a> - </li> - </ul> - <ul> - <li onclick={ settings }> - <p>%fa:cog%%i18n:desktop.tags.mk-ui-header-account.settings%%fa:angle-right%</p> - </li> - </ul> - <ul> - <li onclick={ signout }> - <p>%fa:power-off%%i18n:desktop.tags.mk-ui-header-account.signout%%fa:angle-right%</p> - </li> - </ul> - </div> - <style> - :scope - display block - float left - - > .header - display block - margin 0 - padding 0 - color #9eaba8 - border none - background transparent - cursor pointer - - * - pointer-events none - - &:hover - &[data-active='true'] - color darken(#9eaba8, 20%) - - > .avatar - filter saturate(150%) - - &:active - color darken(#9eaba8, 30%) - - > .username - display block - float left - margin 0 12px 0 16px - max-width 16em - line-height 48px - font-weight bold - font-family Meiryo, sans-serif - text-decoration none - - [data-fa] - margin-left 8px - - > .avatar - display block - float left - min-width 32px - max-width 32px - min-height 32px - max-height 32px - margin 8px 8px 8px 0 - border-radius 4px - transition filter 100ms ease - - > .menu - display block - position absolute - top 56px - right -2px - width 230px - font-size 0.8em - background #fff - border-radius 4px - box-shadow 0 1px 4px rgba(0, 0, 0, 0.25) - - &:before - content "" - pointer-events none - display block - position absolute - top -28px - right 12px - border-top solid 14px transparent - border-right solid 14px transparent - border-bottom solid 14px rgba(0, 0, 0, 0.1) - border-left solid 14px transparent - - &:after - content "" - pointer-events none - display block - position absolute - top -27px - right 12px - border-top solid 14px transparent - border-right solid 14px transparent - border-bottom solid 14px #fff - border-left solid 14px transparent - - ul - display block - margin 10px 0 - padding 0 - list-style none - - & + ul - padding-top 10px - border-top solid 1px #eee - - > li - display block - margin 0 - padding 0 - - > a - > p - display block - z-index 1 - padding 0 28px - margin 0 - line-height 40px - color #868C8C - cursor pointer - - * - pointer-events none - - > [data-fa]:first-of-type - margin-right 6px - - > [data-fa]:last-of-type - display block - position absolute - top 0 - right 8px - z-index 1 - padding 0 20px - font-size 1.2em - line-height 40px - - &:hover, &:active - text-decoration none - background $theme-color - color $theme-color-foreground - - </style> - <script> - import contains from '../../common/scripts/contains'; - import signout from '../../common/scripts/signout'; - this.signout = signout; - - this.mixin('i'); - - this.isOpen = false; - - this.on('before-unmount', () => { - this.close(); - }); - - this.toggle = () => { - this.isOpen ? this.close() : this.open(); - }; - - this.open = () => { - this.update({ - isOpen: true - }); - document.querySelectorAll('body *').forEach(el => { - el.addEventListener('mousedown', this.mousedown); - }); - }; - - this.close = () => { - this.update({ - isOpen: false - }); - document.querySelectorAll('body *').forEach(el => { - el.removeEventListener('mousedown', this.mousedown); - }); - }; - - this.mousedown = e => { - e.preventDefault(); - if (!contains(this.root, e.target) && this.root != e.target) this.close(); - return false; - }; - - this.drive = () => { - this.close(); - riot.mount(document.body.appendChild(document.createElement('mk-drive-browser-window'))); - }; - - this.settings = () => { - this.close(); - riot.mount(document.body.appendChild(document.createElement('mk-settings-window'))); - }; - - </script> -</mk-ui-header-account> - -<mk-ui-notification> - <p>{ opts.message }</p> - <style> - :scope - display block - position fixed - z-index 10000 - top -128px - left 0 - right 0 - margin 0 auto - padding 128px 0 0 0 - width 500px - color rgba(#000, 0.6) - background rgba(#fff, 0.9) - border-radius 0 0 8px 8px - box-shadow 0 2px 4px rgba(#000, 0.2) - transform translateY(-64px) - opacity 0 - - > p - margin 0 - line-height 64px - text-align center - - </style> - <script> - import anime from 'animejs'; - - this.on('mount', () => { - anime({ - targets: this.root, - opacity: 1, - translateY: [-64, 0], - easing: 'easeOutElastic', - duration: 500 - }); - - setTimeout(() => { - anime({ - targets: this.root, - opacity: 0, - translateY: -64, - duration: 500, - easing: 'easeInElastic', - complete: () => this.unmount() - }); - }, 6000); - }); - </script> -</mk-ui-notification> diff --git a/src/web/app/desktop/tags/user-followers-window.tag b/src/web/app/desktop/tags/user-followers-window.tag deleted file mode 100644 index 43127a68a8..0000000000 --- a/src/web/app/desktop/tags/user-followers-window.tag +++ /dev/null @@ -1,19 +0,0 @@ -<mk-user-followers-window> - <mk-window is-modal={ false } width={ '400px' } height={ '550px' }><yield to="header"><img src={ parent.user.avatar_url + '?thumbnail&size=64' } alt=""/>{ parent.user.name }のフォロワー</yield> -<yield to="content"> - <mk-user-followers user={ parent.user }/></yield> - </mk-window> - <style> - :scope - > mk-window - [data-yield='header'] - > img - display inline-block - vertical-align bottom - height calc(100% - 10px) - margin 5px - border-radius 4px - - </style> - <script>this.user = this.opts.user</script> -</mk-user-followers-window> diff --git a/src/web/app/desktop/tags/user-followers.tag b/src/web/app/desktop/tags/user-followers.tag deleted file mode 100644 index ea670e2729..0000000000 --- a/src/web/app/desktop/tags/user-followers.tag +++ /dev/null @@ -1,23 +0,0 @@ -<mk-user-followers> - <mk-users-list fetch={ fetch } count={ user.followers_count } you-know-count={ user.followers_you_know_count } no-users={ 'フォロワーはいないようです。' }/> - <style> - :scope - display block - height 100% - - </style> - <script> - this.mixin('api'); - - this.user = this.opts.user; - - this.fetch = (iknow, limit, cursor, cb) => { - this.api('users/followers', { - user_id: this.user.id, - iknow: iknow, - limit: limit, - cursor: cursor ? cursor : undefined - }).then(cb); - }; - </script> -</mk-user-followers> diff --git a/src/web/app/desktop/tags/user-following-window.tag b/src/web/app/desktop/tags/user-following-window.tag deleted file mode 100644 index 10a84db315..0000000000 --- a/src/web/app/desktop/tags/user-following-window.tag +++ /dev/null @@ -1,19 +0,0 @@ -<mk-user-following-window> - <mk-window is-modal={ false } width={ '400px' } height={ '550px' }><yield to="header"><img src={ parent.user.avatar_url + '?thumbnail&size=64' } alt=""/>{ parent.user.name }のフォロー</yield> -<yield to="content"> - <mk-user-following user={ parent.user }/></yield> - </mk-window> - <style> - :scope - > mk-window - [data-yield='header'] - > img - display inline-block - vertical-align bottom - height calc(100% - 10px) - margin 5px - border-radius 4px - - </style> - <script>this.user = this.opts.user</script> -</mk-user-following-window> diff --git a/src/web/app/desktop/tags/user-following.tag b/src/web/app/desktop/tags/user-following.tag deleted file mode 100644 index 4523beac2c..0000000000 --- a/src/web/app/desktop/tags/user-following.tag +++ /dev/null @@ -1,23 +0,0 @@ -<mk-user-following> - <mk-users-list fetch={ fetch } count={ user.following_count } you-know-count={ user.following_you_know_count } no-users={ 'フォロー中のユーザーはいないようです。' }/> - <style> - :scope - display block - height 100% - - </style> - <script> - this.mixin('api'); - - this.user = this.opts.user; - - this.fetch = (iknow, limit, cursor, cb) => { - this.api('users/following', { - user_id: this.user.id, - iknow: iknow, - limit: limit, - cursor: cursor ? cursor : undefined - }).then(cb); - }; - </script> -</mk-user-following> diff --git a/src/web/app/desktop/tags/user-preview.tag b/src/web/app/desktop/tags/user-preview.tag deleted file mode 100644 index b836ff1e78..0000000000 --- a/src/web/app/desktop/tags/user-preview.tag +++ /dev/null @@ -1,149 +0,0 @@ -<mk-user-preview> - <virtual if={ user != null }> - <div class="banner" style={ user.banner_url ? 'background-image: url(' + user.banner_url + '?thumbnail&size=512)' : '' }></div><a class="avatar" href={ '/' + user.username } target="_blank"><img src={ user.avatar_url + '?thumbnail&size=64' } alt="avatar"/></a> - <div class="title"> - <p class="name">{ user.name }</p> - <p class="username">@{ user.username }</p> - </div> - <div class="description">{ user.description }</div> - <div class="status"> - <div> - <p>投稿</p><a>{ user.posts_count }</a> - </div> - <div> - <p>フォロー</p><a>{ user.following_count }</a> - </div> - <div> - <p>フォロワー</p><a>{ user.followers_count }</a> - </div> - </div> - <mk-follow-button if={ SIGNIN && user.id != I.id } user={ userPromise }/> - </virtual> - <style> - :scope - display block - position absolute - z-index 2048 - margin-top -8px - width 250px - background #fff - background-clip content-box - border solid 1px rgba(0, 0, 0, 0.1) - border-radius 4px - overflow hidden - opacity 0 - - > .banner - height 84px - background-color #f5f5f5 - background-size cover - background-position center - - > .avatar - display block - position absolute - top 62px - left 13px - - > img - display block - width 58px - height 58px - margin 0 - border solid 3px #fff - border-radius 8px - - > .title - display block - padding 8px 0 8px 82px - - > .name - display block - margin 0 - font-weight bold - line-height 16px - color #656565 - - > .username - display block - margin 0 - line-height 16px - font-size 0.8em - color #999 - - > .description - padding 0 16px - font-size 0.7em - color #555 - - > .status - padding 8px 16px - - > div - display inline-block - width 33% - - > p - margin 0 - font-size 0.7em - color #aaa - - > a - font-size 1em - color $theme-color - - > mk-follow-button - position absolute - top 92px - right 8px - - </style> - <script> - import anime from 'animejs'; - - this.mixin('i'); - this.mixin('api'); - - this.u = this.opts.user; - this.user = null; - this.userPromise = - typeof this.u == 'string' ? - new Promise((resolve, reject) => { - this.api('users/show', { - user_id: this.u[0] == '@' ? undefined : this.u, - username: this.u[0] == '@' ? this.u.substr(1) : undefined - }).then(resolve); - }) - : Promise.resolve(this.u); - - this.on('mount', () => { - this.userPromise.then(user => { - this.update({ - user: user - }); - this.open(); - }); - }); - - this.open = () => { - anime({ - targets: this.root, - opacity: 1, - 'margin-top': 0, - duration: 200, - easing: 'easeOutQuad' - }); - }; - - this.close = () => { - anime({ - targets: this.root, - opacity: 0, - 'margin-top': '-8px', - duration: 200, - easing: 'easeOutQuad', - complete: () => this.unmount() - }); - }; - </script> -</mk-user-preview> diff --git a/src/web/app/desktop/tags/user-timeline.tag b/src/web/app/desktop/tags/user-timeline.tag deleted file mode 100644 index 2b05f6b5cf..0000000000 --- a/src/web/app/desktop/tags/user-timeline.tag +++ /dev/null @@ -1,150 +0,0 @@ -<mk-user-timeline> - <header> - <span data-is-active={ mode == 'default' } onclick={ setMode.bind(this, 'default') }>投稿</span><span data-is-active={ mode == 'with-replies' } onclick={ setMode.bind(this, 'with-replies') }>投稿と返信</span> - </header> - <div class="loading" if={ isLoading }> - <mk-ellipsis-icon/> - </div> - <p class="empty" if={ isEmpty }>%fa:R comments%このユーザーはまだ何も投稿していないようです。</p> - <mk-timeline ref="timeline"> - <yield to="footer"> - <virtual if={ !parent.moreLoading }>%fa:moon%</virtual> - <virtual if={ parent.moreLoading }>%fa:spinner .pulse .fw%</virtual> - </yield/> - </mk-timeline> - <style> - :scope - display block - background #fff - - > header - padding 8px 16px - border-bottom solid 1px #eee - - > span - margin-right 16px - line-height 27px - font-size 18px - color #555 - - &:not([data-is-active]) - color $theme-color - cursor pointer - - &:hover - text-decoration underline - - > .loading - padding 64px 0 - - > .empty - display block - margin 0 auto - padding 32px - max-width 400px - text-align center - color #999 - - > [data-fa] - display block - margin-bottom 16px - font-size 3em - color #ccc - - </style> - <script> - import isPromise from '../../common/scripts/is-promise'; - - this.mixin('api'); - - this.user = null; - this.userPromise = isPromise(this.opts.user) - ? this.opts.user - : Promise.resolve(this.opts.user); - this.isLoading = true; - this.isEmpty = false; - this.moreLoading = false; - this.unreadCount = 0; - this.mode = 'default'; - - this.on('mount', () => { - document.addEventListener('keydown', this.onDocumentKeydown); - window.addEventListener('scroll', this.onScroll); - - this.userPromise.then(user => { - this.update({ - user: user - }); - - this.fetch(() => this.trigger('loaded')); - }); - }); - - this.on('unmount', () => { - document.removeEventListener('keydown', this.onDocumentKeydown); - window.removeEventListener('scroll', this.onScroll); - }); - - this.onDocumentKeydown = e => { - if (e.target.tagName !== 'INPUT' && e.target.tagName !== 'TEXTAREA') { - if (e.which == 84) { // [t] - this.refs.timeline.focus(); - } - } - }; - - this.fetch = cb => { - this.api('users/posts', { - user_id: this.user.id, - max_date: this.date ? this.date.getTime() : undefined, - with_replies: this.mode == 'with-replies' - }).then(posts => { - this.update({ - isLoading: false, - isEmpty: posts.length == 0 - }); - this.refs.timeline.setPosts(posts); - if (cb) cb(); - }); - }; - - this.more = () => { - if (this.moreLoading || this.isLoading || this.refs.timeline.posts.length == 0) return; - this.update({ - moreLoading: true - }); - this.api('users/posts', { - user_id: this.user.id, - with_replies: this.mode == 'with-replies', - max_id: this.refs.timeline.tail().id - }).then(posts => { - this.update({ - moreLoading: false - }); - this.refs.timeline.prependPosts(posts); - }); - }; - - this.onScroll = () => { - const current = window.scrollY + window.innerHeight; - if (current > document.body.offsetHeight - 16/*遊び*/) { - this.more(); - } - }; - - this.setMode = mode => { - this.update({ - mode: mode - }); - this.fetch(); - }; - - this.warp = date => { - this.update({ - date: date - }); - - this.fetch(); - }; - </script> -</mk-user-timeline> diff --git a/src/web/app/desktop/tags/user.tag b/src/web/app/desktop/tags/user.tag deleted file mode 100644 index b4db47f9dd..0000000000 --- a/src/web/app/desktop/tags/user.tag +++ /dev/null @@ -1,827 +0,0 @@ -<mk-user> - <div class="user" if={ !fetching }> - <header> - <mk-user-header user={ user }/> - </header> - <mk-user-home if={ page == 'home' } user={ user }/> - <mk-user-graphs if={ page == 'graphs' } user={ user }/> - </div> - <style> - :scope - display block - - > .user - > header - > mk-user-header - overflow hidden - - </style> - <script> - this.mixin('api'); - - this.username = this.opts.user; - this.page = this.opts.page ? this.opts.page : 'home'; - this.fetching = true; - this.user = null; - - this.on('mount', () => { - this.api('users/show', { - username: this.username - }).then(user => { - this.update({ - fetching: false, - user: user - }); - this.trigger('loaded'); - }); - }); - </script> -</mk-user> - -<mk-user-header data-is-dark-background={ user.banner_url != null }> - <div class="banner-container" style={ user.banner_url ? 'background-image: url(' + user.banner_url + '?thumbnail&size=2048)' : '' }> - <div class="banner" ref="banner" style={ user.banner_url ? 'background-image: url(' + user.banner_url + '?thumbnail&size=2048)' : '' } onclick={ onUpdateBanner }></div> - </div> - <div class="fade"></div> - <div class="container"> - <img class="avatar" src={ user.avatar_url + '?thumbnail&size=150' } alt="avatar"/> - <div class="title"> - <p class="name" href={ '/' + user.username }>{ user.name }</p> - <p class="username">@{ user.username }</p> - <p class="location" if={ user.profile.location }>%fa:map-marker%{ user.profile.location }</p> - </div> - <footer> - <a href={ '/' + user.username } data-active={ parent.page == 'home' }>%fa:home%概要</a> - <a href={ '/' + user.username + '/media' } data-active={ parent.page == 'media' }>%fa:image%メディア</a> - <a href={ '/' + user.username + '/graphs' } data-active={ parent.page == 'graphs' }>%fa:chart-bar%グラフ</a> - </footer> - </div> - <style> - :scope - $banner-height = 320px - $footer-height = 58px - - display block - background #f7f7f7 - box-shadow 0 1px 1px rgba(0, 0, 0, 0.075) - - &[data-is-dark-background] - > .banner-container - > .banner - background-color #383838 - - > .fade - background linear-gradient(transparent, rgba(0, 0, 0, 0.7)) - - > .container - > .title - color #fff - - > .name - text-shadow 0 0 8px #000 - - > .banner-container - height $banner-height - overflow hidden - background-size cover - background-position center - - > .banner - height 100% - background-color #f5f5f5 - background-size cover - background-position center - - > .fade - $fade-hight = 78px - - position absolute - top ($banner-height - $fade-hight) - left 0 - width 100% - height $fade-hight - - > .container - max-width 1200px - margin 0 auto - - > .avatar - display block - position absolute - bottom 16px - left 16px - z-index 2 - width 160px - height 160px - margin 0 - border solid 3px #fff - border-radius 8px - box-shadow 1px 1px 3px rgba(0, 0, 0, 0.2) - - > .title - position absolute - bottom $footer-height - left 0 - width 100% - padding 0 0 8px 195px - color #656565 - font-family '游ゴシック', 'YuGothic', 'ヒラギノ角ゴ ProN W3', 'Hiragino Kaku Gothic ProN', 'Meiryo', 'メイリオ', sans-serif - - > .name - display block - margin 0 - line-height 40px - font-weight bold - font-size 2em - - > .username - > .location - display inline-block - margin 0 16px 0 0 - line-height 20px - opacity 0.8 - - > i - margin-right 4px - - > footer - z-index 1 - height $footer-height - padding-left 195px - - > a - display inline-block - margin 0 - padding 0 16px - height $footer-height - line-height $footer-height - color #555 - - &[data-active] - border-bottom solid 4px $theme-color - - > i - margin-right 6px - - > button - display block - position absolute - top 0 - right 0 - margin 8px - padding 0 - width $footer-height - 16px - line-height $footer-height - 16px - 2px - font-size 1.2em - color #777 - border solid 1px #eee - border-radius 4px - - &:hover - color #555 - border solid 1px #ddd - - </style> - <script> - import updateBanner from '../scripts/update-banner'; - - this.mixin('i'); - - this.user = this.opts.user; - - this.on('mount', () => { - window.addEventListener('load', this.scroll); - window.addEventListener('scroll', this.scroll); - window.addEventListener('resize', this.scroll); - }); - - this.on('unmount', () => { - window.removeEventListener('load', this.scroll); - window.removeEventListener('scroll', this.scroll); - window.removeEventListener('resize', this.scroll); - }); - - this.scroll = () => { - const top = window.scrollY; - - const z = 1.25; // 奥行き(小さいほど奥) - const pos = -(top / z); - this.refs.banner.style.backgroundPosition = `center calc(50% - ${pos}px)`; - - const blur = top / 32 - if (blur <= 10) this.refs.banner.style.filter = `blur(${blur}px)`; - }; - - this.onUpdateBanner = () => { - if (!this.SIGNIN || this.I.id != this.user.id) return; - - updateBanner(this.I, i => { - this.user.banner_url = i.banner_url; - this.update(); - }); - }; - </script> -</mk-user-header> - -<mk-user-profile> - <div class="friend-form" if={ SIGNIN && I.id != user.id }> - <mk-big-follow-button user={ user }/> - <p class="followed" if={ user.is_followed }>フォローされています</p> - </div> - <div class="description" if={ user.description }>{ user.description }</div> - <div class="birthday" if={ user.profile.birthday }> - <p>%fa:birthday-cake%{ user.profile.birthday.replace('-', '年').replace('-', '月') + '日' } ({ age(user.profile.birthday) }歳)</p> - </div> - <div class="twitter" if={ user.twitter }> - <p>%fa:B twitter%<a href={ 'https://twitter.com/' + user.twitter.screen_name } target="_blank">@{ user.twitter.screen_name }</a></p> - </div> - <div class="status"> - <p class="posts-count">%fa:angle-right%<a>{ user.posts_count }</a><b>ポスト</b></p> - <p class="following">%fa:angle-right%<a onclick={ showFollowing }>{ user.following_count }</a>人を<b>フォロー</b></p> - <p class="followers">%fa:angle-right%<a onclick={ showFollowers }>{ user.followers_count }</a>人の<b>フォロワー</b></p> - </div> - <style> - :scope - display block - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - > *:first-child - border-top none !important - - > .friend-form - padding 16px - border-top solid 1px #eee - - > mk-big-follow-button - width 100% - - > .followed - margin 12px 0 0 0 - padding 0 - text-align center - line-height 24px - font-size 0.8em - color #71afc7 - background #eefaff - border-radius 4px - - > .description - padding 16px - color #555 - border-top solid 1px #eee - - > .birthday - padding 16px - color #555 - border-top solid 1px #eee - - > p - margin 0 - - > i - margin-right 8px - - > .twitter - padding 16px - color #555 - border-top solid 1px #eee - - > p - margin 0 - - > i - margin-right 8px - - > .status - padding 16px - color #555 - border-top solid 1px #eee - - > p - margin 8px 0 - - > i - margin-left 8px - margin-right 8px - - </style> - <script> - this.age = require('s-age'); - - this.mixin('i'); - - this.user = this.opts.user; - - this.showFollowing = () => { - riot.mount(document.body.appendChild(document.createElement('mk-user-following-window')), { - user: this.user - }); - }; - - this.showFollowers = () => { - riot.mount(document.body.appendChild(document.createElement('mk-user-followers-window')), { - user: this.user - }); - }; - </script> -</mk-user-profile> - -<mk-user-photos> - <p class="title">%fa:camera%%i18n:desktop.tags.mk-user.photos.title%</p> - <p class="initializing" if={ initializing }>%fa:spinner .pulse .fw%%i18n:desktop.tags.mk-user.photos.loading%<mk-ellipsis/></p> - <div class="stream" if={ !initializing && images.length > 0 }> - <virtual each={ image in images }> - <div class="img" style={ 'background-image: url(' + image.url + '?thumbnail&size=256)' }></div> - </virtual> - </div> - <p class="empty" if={ !initializing && images.length == 0 }>%i18n:desktop.tags.mk-user.photos.no-photos%</p> - <style> - :scope - display block - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - > .title - z-index 1 - margin 0 - padding 0 16px - line-height 42px - font-size 0.9em - font-weight bold - color #888 - box-shadow 0 1px rgba(0, 0, 0, 0.07) - - > i - margin-right 4px - - > .stream - display -webkit-flex - display -moz-flex - display -ms-flex - display flex - justify-content center - flex-wrap wrap - padding 8px - - > .img - flex 1 1 33% - width 33% - height 80px - background-position center center - background-size cover - background-clip content-box - border solid 2px transparent - - > .initializing - > .empty - margin 0 - padding 16px - text-align center - color #aaa - - > i - margin-right 4px - - </style> - <script> - import isPromise from '../../common/scripts/is-promise'; - - this.mixin('api'); - - this.images = []; - this.initializing = true; - this.user = null; - this.userPromise = isPromise(this.opts.user) - ? this.opts.user - : Promise.resolve(this.opts.user); - - this.on('mount', () => { - this.userPromise.then(user => { - this.update({ - user: user - }); - - this.api('users/posts', { - user_id: this.user.id, - with_media: true, - limit: 9 - }).then(posts => { - this.initializing = false; - posts.forEach(post => { - post.media.forEach(media => { - if (this.images.length < 9) this.images.push(media); - }); - }); - this.update(); - }); - }); - }); - </script> -</mk-user-photos> - -<mk-user-frequently-replied-users> - <p class="title">%fa:users%%i18n:desktop.tags.mk-user.frequently-replied-users.title%</p> - <p class="initializing" if={ initializing }>%fa:spinner .pulse .fw%%i18n:desktop.tags.mk-user.frequently-replied-users.loading%<mk-ellipsis/></p> - <div class="user" if={ !initializing && users.length != 0 } each={ _user in users }> - <a class="avatar-anchor" href={ '/' + _user.username }> - <img class="avatar" src={ _user.avatar_url + '?thumbnail&size=42' } alt="" data-user-preview={ _user.id }/> - </a> - <div class="body"> - <a class="name" href={ '/' + _user.username } data-user-preview={ _user.id }>{ _user.name }</a> - <p class="username">@{ _user.username }</p> - </div> - <mk-follow-button user={ _user }/> - </div> - <p class="empty" if={ !initializing && users.length == 0 }>%i18n:desktop.tags.mk-user.frequently-replied-users.no-users%</p> - <style> - :scope - display block - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - > .title - z-index 1 - margin 0 - padding 0 16px - line-height 42px - font-size 0.9em - font-weight bold - color #888 - box-shadow 0 1px rgba(0, 0, 0, 0.07) - - > i - margin-right 4px - - > .initializing - > .empty - margin 0 - padding 16px - text-align center - color #aaa - - > i - margin-right 4px - - > .user - padding 16px - border-bottom solid 1px #eee - - &:last-child - border-bottom none - - &:after - content "" - display block - clear both - - > .avatar-anchor - display block - float left - margin 0 12px 0 0 - - > .avatar - display block - width 42px - height 42px - margin 0 - border-radius 8px - vertical-align bottom - - > .body - float left - width calc(100% - 54px) - - > .name - margin 0 - font-size 16px - line-height 24px - color #555 - - > .username - display block - margin 0 - font-size 15px - line-height 16px - color #ccc - - > mk-follow-button - position absolute - top 16px - right 16px - - </style> - <script> - this.mixin('api'); - - this.user = this.opts.user; - this.initializing = true; - - this.on('mount', () => { - this.api('users/get_frequently_replied_users', { - user_id: this.user.id, - limit: 4 - }).then(docs => { - this.update({ - users: docs.map(doc => doc.user), - initializing: false - }); - }); - }); - </script> -</mk-user-frequently-replied-users> - -<mk-user-followers-you-know> - <p class="title">%fa:users%%i18n:desktop.tags.mk-user.followers-you-know.title%</p> - <p class="initializing" if={ initializing }>%fa:spinner .pulse .fw%%i18n:desktop.tags.mk-user.followers-you-know.loading%<mk-ellipsis/></p> - <div if={ !initializing && users.length > 0 }> - <virtual each={ user in users }> - <a href={ '/' + user.username }><img src={ user.avatar_url + '?thumbnail&size=64' } alt={ user.name }/></a> - </virtual> - </div> - <p class="empty" if={ !initializing && users.length == 0 }>%i18n:desktop.tags.mk-user.followers-you-know.no-users%</p> - <style> - :scope - display block - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - > .title - z-index 1 - margin 0 - padding 0 16px - line-height 42px - font-size 0.9em - font-weight bold - color #888 - box-shadow 0 1px rgba(0, 0, 0, 0.07) - - > i - margin-right 4px - - > div - padding 8px - - > a - display inline-block - margin 4px - - > img - width 48px - height 48px - vertical-align bottom - border-radius 100% - - > .initializing - > .empty - margin 0 - padding 16px - text-align center - color #aaa - - > i - margin-right 4px - - </style> - <script> - this.mixin('api'); - - this.user = this.opts.user; - this.initializing = true; - - this.on('mount', () => { - this.api('users/followers', { - user_id: this.user.id, - iknow: true, - limit: 16 - }).then(x => { - this.update({ - users: x.users, - initializing: false - }); - }); - }); - </script> -</mk-user-followers-you-know> - -<mk-user-home> - <div> - <div ref="left"> - <mk-user-profile user={ user }/> - <mk-user-photos user={ user }/> - <mk-user-followers-you-know if={ SIGNIN && I.id !== user.id } user={ user }/> - <p>%i18n:desktop.tags.mk-user.last-used-at%: <b><mk-time time={ user.last_used_at }/></b></p> - </div> - </div> - <main> - <mk-post-detail if={ user.pinned_post } post={ user.pinned_post } compact={ true }/> - <mk-user-timeline ref="tl" user={ user }/> - </main> - <div> - <div ref="right"> - <mk-calendar-widget warp={ warp } start={ new Date(user.created_at) }/> - <mk-activity-widget user={ user }/> - <mk-user-frequently-replied-users user={ user }/> - <div class="nav"><mk-nav-links/></div> - </div> - </div> - <style> - :scope - display flex - justify-content center - margin 0 auto - max-width 1200px - - > main - > div > div - > *:not(:last-child) - margin-bottom 16px - - > main - padding 16px - width calc(100% - 275px * 2) - - > mk-user-timeline - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - > div - width 275px - margin 0 - - &:first-child > div - padding 16px 0 16px 16px - - > p - display block - margin 0 - padding 0 12px - text-align center - font-size 0.8em - color #aaa - - &:last-child > div - padding 16px 16px 16px 0 - - > .nav - padding 16px - font-size 12px - color #aaa - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - a - color #999 - - i - color #ccc - - </style> - <script> - import ScrollFollower from '../scripts/scroll-follower'; - - this.mixin('i'); - - this.user = this.opts.user; - - this.on('mount', () => { - this.refs.tl.on('loaded', () => { - this.trigger('loaded'); - }); - - this.scrollFollowerLeft = new ScrollFollower(this.refs.left, this.parent.root.getBoundingClientRect().top); - this.scrollFollowerRight = new ScrollFollower(this.refs.right, this.parent.root.getBoundingClientRect().top); - }); - - this.on('unmount', () => { - this.scrollFollowerLeft.dispose(); - this.scrollFollowerRight.dispose(); - }); - - this.warp = date => { - this.refs.tl.warp(date); - }; - </script> -</mk-user-home> - -<mk-user-graphs> - <section> - <div> - <h1>%fa:pencil-alt%投稿</h1> - <mk-user-graphs-activity-chart user={ opts.user }/> - </div> - </section> - <section> - <div> - <h1>フォロー/フォロワー</h1> - <mk-user-friends-graph user={ opts.user }/> - </div> - </section> - <section> - <div> - <h1>いいね</h1> - <mk-user-likes-graph user={ opts.user }/> - </div> - </section> - <style> - :scope - display block - - > section - margin 16px 0 - color #666 - border-bottom solid 1px rgba(0, 0, 0, 0.1) - - > div - max-width 1200px - margin 0 auto - padding 0 16px - - > h1 - margin 0 0 16px 0 - padding 0 - font-size 1.3em - - > i - margin-right 8px - - </style> - <script> - this.on('mount', () => { - this.trigger('loaded'); - }); - </script> -</mk-user-graphs> - -<mk-user-graphs-activity-chart> - <svg if={ data } ref="canvas" viewBox="0 0 365 1" preserveAspectRatio="none"> - <g each={ d, i in data.reverse() }> - <rect width="0.8" riot-height={ d.postsH } - riot-x={ i + 0.1 } riot-y={ 1 - d.postsH - d.repliesH - d.repostsH } - fill="#41ddde"/> - <rect width="0.8" riot-height={ d.repliesH } - riot-x={ i + 0.1 } riot-y={ 1 - d.repliesH - d.repostsH } - fill="#f7796c"/> - <rect width="0.8" riot-height={ d.repostsH } - riot-x={ i + 0.1 } riot-y={ 1 - d.repostsH } - fill="#a1de41"/> - </g> - </svg> - <p>直近1年間分の統計です。一番右が現在で、一番左が1年前です。青は通常の投稿、赤は返信、緑はRepostをそれぞれ表しています。</p> - <p> - <span>だいたい*1日に<b>{ averageOfAllTypePostsEachDays }回</b>投稿(返信、Repost含む)しています。</span><br> - <span>だいたい*1日に<b>{ averageOfPostsEachDays }回</b>投稿(通常の)しています。</span><br> - <span>だいたい*1日に<b>{ averageOfRepliesEachDays }回</b>返信しています。</span><br> - <span>だいたい*1日に<b>{ averageOfRepostsEachDays }回</b>Repostしています。</span><br> - </p> - <p>* 中央値</p> - - <style> - :scope - display block - - > svg - display block - width 100% - height 180px - - > rect - transform-origin center - - </style> - <script> - import getMedian from '../../common/scripts/get-median'; - - this.mixin('api'); - - this.user = this.opts.user; - - this.on('mount', () => { - this.api('aggregation/users/activity', { - user_id: this.user.id, - limit: 365 - }).then(data => { - data.forEach(d => d.total = d.posts + d.replies + d.reposts); - this.peak = Math.max.apply(null, data.map(d => d.total)); - data.forEach(d => { - d.postsH = d.posts / this.peak; - d.repliesH = d.replies / this.peak; - d.repostsH = d.reposts / this.peak; - }); - - this.update({ - data, - averageOfAllTypePostsEachDays: getMedian(data.map(d => d.total)), - averageOfPostsEachDays: getMedian(data.map(d => d.posts)), - averageOfRepliesEachDays: getMedian(data.map(d => d.replies)), - averageOfRepostsEachDays: getMedian(data.map(d => d.reposts)) - }); - }); - }); - </script> -</mk-user-graphs-activity-chart> diff --git a/src/web/app/desktop/tags/users-list.tag b/src/web/app/desktop/tags/users-list.tag deleted file mode 100644 index ec9c7d8c7b..0000000000 --- a/src/web/app/desktop/tags/users-list.tag +++ /dev/null @@ -1,138 +0,0 @@ -<mk-users-list> - <nav> - <div> - <span data-is-active={ mode == 'all' } onclick={ setMode.bind(this, 'all') }>すべて<span>{ opts.count }</span></span> - <span if={ SIGNIN && opts.youKnowCount } data-is-active={ mode == 'iknow' } onclick={ setMode.bind(this, 'iknow') }>知り合い<span>{ opts.youKnowCount }</span></span> - </div> - </nav> - <div class="users" if={ !fetching && users.length != 0 }> - <div each={ users }> - <mk-list-user user={ this }/> - </div> - </div> - <button class="more" if={ !fetching && next != null } onclick={ more } disabled={ moreFetching }> - <span if={ !moreFetching }>もっと</span> - <span if={ moreFetching }>読み込み中<mk-ellipsis/></span> - </button> - <p class="no" if={ !fetching && users.length == 0 }>{ opts.noUsers }</p> - <p class="fetching" if={ fetching }>%fa:spinner .pulse .fw%読み込んでいます<mk-ellipsis/></p> - <style> - :scope - display block - height 100% - background #fff - - > nav - z-index 1 - box-shadow 0 1px 0 rgba(#000, 0.1) - - > div - display flex - justify-content center - margin 0 auto - max-width 600px - - > span - display block - flex 1 1 - text-align center - line-height 52px - font-size 14px - color #657786 - border-bottom solid 2px transparent - cursor pointer - - * - pointer-events none - - &[data-is-active] - font-weight bold - color $theme-color - border-color $theme-color - cursor default - - > span - display inline-block - margin-left 4px - padding 2px 5px - font-size 12px - line-height 1 - color #888 - background #eee - border-radius 20px - - > .users - height calc(100% - 54px) - overflow auto - - > * - border-bottom solid 1px rgba(0, 0, 0, 0.05) - - > * - max-width 600px - margin 0 auto - - > .no - margin 0 - padding 16px - text-align center - color #aaa - - > .fetching - margin 0 - padding 16px - text-align center - color #aaa - - > [data-fa] - margin-right 4px - - </style> - <script> - this.mixin('i'); - - this.limit = 30; - this.mode = 'all'; - - this.fetching = true; - this.moreFetching = false; - - this.on('mount', () => { - this.fetch(() => this.trigger('loaded')); - }); - - this.fetch = cb => { - this.update({ - fetching: true - }); - this.opts.fetch(this.mode == 'iknow', this.limit, null, obj => { - this.update({ - fetching: false, - users: obj.users, - next: obj.next - }); - if (cb) cb(); - }); - }; - - this.more = () => { - this.update({ - moreFetching: true - }); - this.opts.fetch(this.mode == 'iknow', this.limit, this.cursor, obj => { - this.update({ - moreFetching: false, - users: this.users.concat(obj.users), - next: obj.next - }); - }); - }; - - this.setMode = mode => { - this.update({ - mode: mode - }); - this.fetch(); - }; - </script> -</mk-users-list> diff --git a/src/web/app/desktop/tags/widgets/activity.tag b/src/web/app/desktop/tags/widgets/activity.tag deleted file mode 100644 index e8c8a47632..0000000000 --- a/src/web/app/desktop/tags/widgets/activity.tag +++ /dev/null @@ -1,246 +0,0 @@ -<mk-activity-widget data-melt={ design == 2 }> - <virtual if={ design == 0 }> - <p class="title">%fa:chart-bar%%i18n:desktop.tags.mk-activity-widget.title%</p> - <button onclick={ toggle } title="%i18n:desktop.tags.mk-activity-widget.toggle%">%fa:sort%</button> - </virtual> - <p class="initializing" if={ initializing }>%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p> - <mk-activity-widget-calender if={ !initializing && view == 0 } data={ [].concat(activity) }/> - <mk-activity-widget-chart if={ !initializing && view == 1 } data={ [].concat(activity) }/> - <style> - :scope - display block - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - &[data-melt] - background transparent !important - border none !important - - > .title - z-index 1 - margin 0 - padding 0 16px - line-height 42px - font-size 0.9em - font-weight bold - color #888 - box-shadow 0 1px rgba(0, 0, 0, 0.07) - - > [data-fa] - margin-right 4px - - > button - position absolute - z-index 2 - top 0 - right 0 - padding 0 - width 42px - font-size 0.9em - line-height 42px - color #ccc - - &:hover - color #aaa - - &:active - color #999 - - > .initializing - margin 0 - padding 16px - text-align center - color #aaa - - > [data-fa] - margin-right 4px - - </style> - <script> - this.mixin('api'); - - this.design = this.opts.design || 0; - this.view = this.opts.view || 0; - - this.user = this.opts.user; - this.initializing = true; - - this.on('mount', () => { - this.api('aggregation/users/activity', { - user_id: this.user.id, - limit: 20 * 7 - }).then(activity => { - this.update({ - initializing: false, - activity - }); - }); - }); - - this.toggle = () => { - this.view++; - if (this.view == 2) this.view = 0; - this.update(); - this.trigger('view-changed', this.view); - }; - </script> -</mk-activity-widget> - -<mk-activity-widget-calender> - <svg viewBox="0 0 21 7" preserveAspectRatio="none"> - <rect each={ data } class="day" - width="1" height="1" - riot-x={ x } riot-y={ date.weekday } - rx="1" ry="1" - fill="transparent"> - <title>{ date.year }/{ date.month }/{ date.day }<br/>Post: { posts }, Reply: { replies }, Repost: { reposts }</title> - </rect> - <rect each={ data } - riot-width={ v } riot-height={ v } - riot-x={ x + ((1 - v) / 2) } riot-y={ date.weekday + ((1 - v) / 2) } - rx="1" ry="1" - fill={ color } - style="pointer-events: none;"/> - <rect class="today" - width="1" height="1" - riot-x={ data[data.length - 1].x } riot-y={ data[data.length - 1].date.weekday } - rx="1" ry="1" - fill="none" - stroke-width="0.1" - stroke="#f73520"/> - </svg> - <style> - :scope - display block - - > svg - display block - padding 10px - width 100% - - > rect - transform-origin center - - &.day - &:hover - fill rgba(0, 0, 0, 0.05) - - </style> - <script> - this.data = this.opts.data; - this.data.forEach(d => d.total = d.posts + d.replies + d.reposts); - const peak = Math.max.apply(null, this.data.map(d => d.total)); - - let x = 0; - this.data.reverse().forEach(d => { - d.x = x; - d.date.weekday = (new Date(d.date.year, d.date.month - 1, d.date.day)).getDay(); - - d.v = d.total / (peak / 2); - if (d.v > 1) d.v = 1; - const ch = d.date.weekday == 0 || d.date.weekday == 6 ? 275 : 170; - const cs = d.v * 100; - const cl = 15 + ((1 - d.v) * 80); - d.color = `hsl(${ch}, ${cs}%, ${cl}%)`; - - if (d.date.weekday == 6) x++; - }); - </script> -</mk-activity-widget-calender> - -<mk-activity-widget-chart> - <svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none" onmousedown={ onMousedown }> - <title>Black ... Total<br/>Blue ... Posts<br/>Red ... Replies<br/>Green ... Reposts</title> - <polyline - riot-points={ pointsPost } - fill="none" - stroke-width="1" - stroke="#41ddde"/> - <polyline - riot-points={ pointsReply } - fill="none" - stroke-width="1" - stroke="#f7796c"/> - <polyline - riot-points={ pointsRepost } - fill="none" - stroke-width="1" - stroke="#a1de41"/> - <polyline - riot-points={ pointsTotal } - fill="none" - stroke-width="1" - stroke="#555" - stroke-dasharray="2 2"/> - </svg> - <style> - :scope - display block - - > svg - display block - padding 10px - width 100% - cursor all-scroll - </style> - <script> - this.viewBoxX = 140; - this.viewBoxY = 60; - this.zoom = 1; - this.pos = 0; - - this.data = this.opts.data.reverse(); - this.data.forEach(d => d.total = d.posts + d.replies + d.reposts); - const peak = Math.max.apply(null, this.data.map(d => d.total)); - - this.on('mount', () => { - this.render(); - }); - - this.render = () => { - this.update({ - pointsPost: this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.posts / peak)) * this.viewBoxY}`).join(' '), - pointsReply: this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.replies / peak)) * this.viewBoxY}`).join(' '), - pointsRepost: this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.reposts / peak)) * this.viewBoxY}`).join(' '), - pointsTotal: this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.total / peak)) * this.viewBoxY}`).join(' ') - }); - }; - - this.onMousedown = e => { - e.preventDefault(); - - const clickX = e.clientX; - const clickY = e.clientY; - const baseZoom = this.zoom; - const basePos = this.pos; - - // 動かした時 - dragListen(me => { - let moveLeft = me.clientX - clickX; - let moveTop = me.clientY - clickY; - - this.zoom = baseZoom + (-moveTop / 20); - this.pos = basePos + moveLeft; - if (this.zoom < 1) this.zoom = 1; - if (this.pos > 0) this.pos = 0; - if (this.pos < -(((this.data.length - 1) * this.zoom) - this.viewBoxX)) this.pos = -(((this.data.length - 1) * this.zoom) - this.viewBoxX); - - this.render(); - }); - }; - - function dragListen(fn) { - window.addEventListener('mousemove', fn); - window.addEventListener('mouseleave', dragClear.bind(null, fn)); - window.addEventListener('mouseup', dragClear.bind(null, fn)); - } - - function dragClear(fn) { - window.removeEventListener('mousemove', fn); - window.removeEventListener('mouseleave', dragClear); - window.removeEventListener('mouseup', dragClear); - } - </script> -</mk-activity-widget-chart> - diff --git a/src/web/app/desktop/tags/widgets/calendar.tag b/src/web/app/desktop/tags/widgets/calendar.tag deleted file mode 100644 index abe9981873..0000000000 --- a/src/web/app/desktop/tags/widgets/calendar.tag +++ /dev/null @@ -1,241 +0,0 @@ -<mk-calendar-widget data-melt={ opts.design == 4 || opts.design == 5 }> - <virtual if={ opts.design == 0 || opts.design == 1 }> - <button onclick={ prev } title="%i18n:desktop.tags.mk-calendar-widget.prev%">%fa:chevron-circle-left%</button> - <p class="title">{ '%i18n:desktop.tags.mk-calendar-widget.title%'.replace('{1}', year).replace('{2}', month) }</p> - <button onclick={ next } title="%i18n:desktop.tags.mk-calendar-widget.next%">%fa:chevron-circle-right%</button> - </virtual> - - <div class="calendar"> - <div class="weekday" if={ opts.design == 0 || opts.design == 2 || opts.design == 4} each={ day, i in Array(7).fill(0) } - data-today={ year == today.getFullYear() && month == today.getMonth() + 1 && today.getDay() == i } - data-is-donichi={ i == 0 || i == 6 }>{ weekdayText[i] }</div> - <div each={ day, i in Array(paddingDays).fill(0) }></div> - <div class="day" each={ day, i in Array(days).fill(0) } - data-today={ isToday(i + 1) } - data-selected={ isSelected(i + 1) } - data-is-out-of-range={ isOutOfRange(i + 1) } - data-is-donichi={ isDonichi(i + 1) } - onclick={ go.bind(null, i + 1) } - title={ isOutOfRange(i + 1) ? null : '%i18n:desktop.tags.mk-calendar-widget.go%' }><div>{ i + 1 }</div></div> - </div> - <style> - :scope - display block - color #777 - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - &[data-melt] - background transparent !important - border none !important - - > .title - z-index 1 - margin 0 - padding 0 16px - text-align center - line-height 42px - font-size 0.9em - font-weight bold - color #888 - box-shadow 0 1px rgba(0, 0, 0, 0.07) - - > [data-fa] - margin-right 4px - - > button - position absolute - z-index 2 - top 0 - padding 0 - width 42px - font-size 0.9em - line-height 42px - color #ccc - - &:hover - color #aaa - - &:active - color #999 - - &:first-of-type - left 0 - - &:last-of-type - right 0 - - > .calendar - display flex - flex-wrap wrap - padding 16px - - * - user-select none - - > div - width calc(100% * (1/7)) - text-align center - line-height 32px - font-size 14px - - &.weekday - color #19a2a9 - - &[data-is-donichi] - color #ef95a0 - - &[data-today] - box-shadow 0 0 0 1px #19a2a9 inset - border-radius 6px - - &[data-is-donichi] - box-shadow 0 0 0 1px #ef95a0 inset - - &.day - cursor pointer - color #777 - - > div - border-radius 6px - - &:hover > div - background rgba(0, 0, 0, 0.025) - - &:active > div - background rgba(0, 0, 0, 0.05) - - &[data-is-donichi] - color #ef95a0 - - &[data-is-out-of-range] - cursor default - color rgba(#777, 0.5) - - &[data-is-donichi] - color rgba(#ef95a0, 0.5) - - &[data-selected] - font-weight bold - - > div - background rgba(0, 0, 0, 0.025) - - &:active > div - background rgba(0, 0, 0, 0.05) - - &[data-today] - > div - color $theme-color-foreground - background $theme-color - - &:hover > div - background lighten($theme-color, 10%) - - &:active > div - background darken($theme-color, 10%) - - </style> - <script> - if (this.opts.design == null) this.opts.design = 0; - - const eachMonthDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; - - function isLeapYear(year) { - return (year % 400 == 0) ? true : - (year % 100 == 0) ? false : - (year % 4 == 0) ? true : - false; - } - - this.today = new Date(); - this.year = this.today.getFullYear(); - this.month = this.today.getMonth() + 1; - this.selected = this.today; - this.weekdayText = [ - '%i18n:common.weekday-short.sunday%', - '%i18n:common.weekday-short.monday%', - '%i18n:common.weekday-short.tuesday%', - '%i18n:common.weekday-short.wednesday%', - '%i18n:common.weekday-short.thursday%', - '%i18n:common.weekday-short.friday%', - '%i18n:common.weekday-short.satruday%' - ]; - - this.on('mount', () => { - this.calc(); - }); - - this.isToday = day => { - return this.year == this.today.getFullYear() && this.month == this.today.getMonth() + 1 && day == this.today.getDate(); - }; - - this.isSelected = day => { - return this.year == this.selected.getFullYear() && this.month == this.selected.getMonth() + 1 && day == this.selected.getDate(); - }; - - this.isOutOfRange = day => { - const test = (new Date(this.year, this.month - 1, day)).getTime(); - return test > this.today.getTime() || - (this.opts.start ? test < this.opts.start.getTime() : false); - }; - - this.isDonichi = day => { - const weekday = (new Date(this.year, this.month - 1, day)).getDay(); - return weekday == 0 || weekday == 6; - }; - - this.calc = () => { - let days = eachMonthDays[this.month - 1]; - - // うるう年なら+1日 - if (this.month == 2 && isLeapYear(this.year)) days++; - - const date = new Date(this.year, this.month - 1, 1); - const weekday = date.getDay(); - - this.update({ - paddingDays: weekday, - days: days - }); - }; - - this.prev = () => { - if (this.month == 1) { - this.update({ - year: this.year - 1, - month: 12 - }); - } else { - this.update({ - month: this.month - 1 - }); - } - this.calc(); - }; - - this.next = () => { - if (this.month == 12) { - this.update({ - year: this.year + 1, - month: 1 - }); - } else { - this.update({ - month: this.month + 1 - }); - } - this.calc(); - }; - - this.go = day => { - if (this.isOutOfRange(day)) return; - const date = new Date(this.year, this.month - 1, day, 23, 59, 59, 999); - this.update({ - selected: date - }); - this.opts.warp(date); - }; -</script> -</mk-calendar-widget> diff --git a/src/web/app/desktop/tags/window.tag b/src/web/app/desktop/tags/window.tag deleted file mode 100644 index 5b4b3c83e4..0000000000 --- a/src/web/app/desktop/tags/window.tag +++ /dev/null @@ -1,549 +0,0 @@ -<mk-window data-flexible={ isFlexible } ondragover={ ondragover }> - <div class="bg" ref="bg" show={ isModal } onclick={ bgClick }></div> - <div class="main" ref="main" tabindex="-1" data-is-modal={ isModal } onmousedown={ onBodyMousedown } onkeydown={ onKeydown }> - <div class="body"> - <header ref="header" onmousedown={ onHeaderMousedown }> - <h1 data-yield="header"><yield from="header"/></h1> - <div> - <button class="popout" if={ popoutUrl } onmousedown={ repelMove } onclick={ popout } title="ポップアウト">%fa:R window-restore%</button> - <button class="close" if={ canClose } onmousedown={ repelMove } onclick={ close } title="閉じる">%fa:times%</button> - </div> - </header> - <div class="content" data-yield="content"><yield from="content"/></div> - </div> - <div class="handle top" if={ canResize } onmousedown={ onTopHandleMousedown }></div> - <div class="handle right" if={ canResize } onmousedown={ onRightHandleMousedown }></div> - <div class="handle bottom" if={ canResize } onmousedown={ onBottomHandleMousedown }></div> - <div class="handle left" if={ canResize } onmousedown={ onLeftHandleMousedown }></div> - <div class="handle top-left" if={ canResize } onmousedown={ onTopLeftHandleMousedown }></div> - <div class="handle top-right" if={ canResize } onmousedown={ onTopRightHandleMousedown }></div> - <div class="handle bottom-right" if={ canResize } onmousedown={ onBottomRightHandleMousedown }></div> - <div class="handle bottom-left" if={ canResize } onmousedown={ onBottomLeftHandleMousedown }></div> - </div> - <style> - :scope - display block - - > .bg - display block - position fixed - z-index 2048 - top 0 - left 0 - width 100% - height 100% - background rgba(0, 0, 0, 0.7) - opacity 0 - pointer-events none - - > .main - display block - position fixed - z-index 2048 - top 15% - left 0 - margin 0 - opacity 0 - pointer-events none - - &:focus - &:not([data-is-modal]) - > .body - box-shadow 0 0 0px 1px rgba($theme-color, 0.5), 0 2px 6px 0 rgba(0, 0, 0, 0.2) - - > .handle - $size = 8px - - position absolute - - &.top - top -($size) - left 0 - width 100% - height $size - cursor ns-resize - - &.right - top 0 - right -($size) - width $size - height 100% - cursor ew-resize - - &.bottom - bottom -($size) - left 0 - width 100% - height $size - cursor ns-resize - - &.left - top 0 - left -($size) - width $size - height 100% - cursor ew-resize - - &.top-left - top -($size) - left -($size) - width $size * 2 - height $size * 2 - cursor nwse-resize - - &.top-right - top -($size) - right -($size) - width $size * 2 - height $size * 2 - cursor nesw-resize - - &.bottom-right - bottom -($size) - right -($size) - width $size * 2 - height $size * 2 - cursor nwse-resize - - &.bottom-left - bottom -($size) - left -($size) - width $size * 2 - height $size * 2 - cursor nesw-resize - - > .body - height 100% - overflow hidden - background #fff - border-radius 6px - box-shadow 0 2px 6px 0 rgba(0, 0, 0, 0.2) - - > header - $header-height = 40px - - z-index 128 - height $header-height - overflow hidden - white-space nowrap - cursor move - background #fff - border-radius 6px 6px 0 0 - box-shadow 0 1px 0 rgba(#000, 0.1) - - &, * - user-select none - - > h1 - pointer-events none - display block - margin 0 auto - overflow hidden - height $header-height - text-overflow ellipsis - text-align center - font-size 1em - line-height $header-height - font-weight normal - color #666 - - > div:last-child - position absolute - top 0 - right 0 - display block - z-index 1 - - > * - display inline-block - margin 0 - padding 0 - cursor pointer - font-size 1.2em - color rgba(#000, 0.4) - border none - outline none - background transparent - - &:hover - color rgba(#000, 0.6) - - &:active - color darken(#000, 30%) - - > [data-fa] - padding 0 - width $header-height - line-height $header-height - text-align center - - > .content - height 100% - - &:not([flexible]) - > .main > .body > .content - height calc(100% - 40px) - - </style> - <script> - import anime from 'animejs'; - import contains from '../../common/scripts/contains'; - - this.minHeight = 40; - this.minWidth = 200; - - this.isModal = this.opts.isModal != null ? this.opts.isModal : false; - this.canClose = this.opts.canClose != null ? this.opts.canClose : true; - this.popoutUrl = this.opts.popout; - this.isFlexible = this.opts.height == null; - this.canResize = !this.isFlexible; - - this.on('mount', () => { - this.refs.main.style.width = this.opts.width || '530px'; - this.refs.main.style.height = this.opts.height || 'auto'; - - this.refs.main.style.top = '15%'; - this.refs.main.style.left = (window.innerWidth / 2) - (this.refs.main.offsetWidth / 2) + 'px'; - - this.refs.header.addEventListener('contextmenu', e => { - e.preventDefault(); - }); - - window.addEventListener('resize', this.onBrowserResize); - - this.open(); - }); - - this.on('unmount', () => { - window.removeEventListener('resize', this.onBrowserResize); - }); - - this.onBrowserResize = () => { - const position = this.refs.main.getBoundingClientRect(); - const browserWidth = window.innerWidth; - const browserHeight = window.innerHeight; - const windowWidth = this.refs.main.offsetWidth; - const windowHeight = this.refs.main.offsetHeight; - if (position.left < 0) this.refs.main.style.left = 0; - if (position.top < 0) this.refs.main.style.top = 0; - if (position.left + windowWidth > browserWidth) this.refs.main.style.left = browserWidth - windowWidth + 'px'; - if (position.top + windowHeight > browserHeight) this.refs.main.style.top = browserHeight - windowHeight + 'px'; - }; - - this.open = () => { - this.trigger('opening'); - - this.top(); - - if (this.isModal) { - this.refs.bg.style.pointerEvents = 'auto'; - anime({ - targets: this.refs.bg, - opacity: 1, - duration: 100, - easing: 'linear' - }); - } - - this.refs.main.style.pointerEvents = 'auto'; - anime({ - targets: this.refs.main, - opacity: 1, - scale: [1.1, 1], - duration: 200, - easing: 'easeOutQuad' - }); - - //this.refs.main.focus(); - - setTimeout(() => { - this.trigger('opened'); - }, 300); - }; - - this.popout = () => { - const position = this.refs.main.getBoundingClientRect(); - - const width = parseInt(getComputedStyle(this.refs.main, '').width, 10); - const height = parseInt(getComputedStyle(this.refs.main, '').height, 10); - const x = window.screenX + position.left; - const y = window.screenY + position.top; - - const url = typeof this.popoutUrl == 'function' ? this.popoutUrl() : this.popoutUrl; - - window.open(url, url, - `height=${height},width=${width},left=${x},top=${y}`); - - this.close(); - }; - - this.close = () => { - this.trigger('closing'); - - if (this.isModal) { - this.refs.bg.style.pointerEvents = 'none'; - anime({ - targets: this.refs.bg, - opacity: 0, - duration: 300, - easing: 'linear' - }); - } - - this.refs.main.style.pointerEvents = 'none'; - - anime({ - targets: this.refs.main, - opacity: 0, - scale: 0.8, - duration: 300, - easing: [0.5, -0.5, 1, 0.5] - }); - - setTimeout(() => { - this.trigger('closed'); - }, 300); - }; - - // 最前面へ移動します - this.top = () => { - let z = 0; - - const ws = document.querySelectorAll('mk-window'); - ws.forEach(w => { - if (w == this.root) return; - const m = w.querySelector(':scope > .main'); - const mz = Number(document.defaultView.getComputedStyle(m, null).zIndex); - if (mz > z) z = mz; - }); - - if (z > 0) { - this.refs.main.style.zIndex = z + 1; - if (this.isModal) this.refs.bg.style.zIndex = z + 1; - } - }; - - this.repelMove = e => { - e.stopPropagation(); - return true; - }; - - this.bgClick = () => { - if (this.canClose) this.close(); - }; - - this.onBodyMousedown = () => { - this.top(); - }; - - // ヘッダー掴み時 - this.onHeaderMousedown = e => { - e.preventDefault(); - - if (!contains(this.refs.main, document.activeElement)) this.refs.main.focus(); - - const position = this.refs.main.getBoundingClientRect(); - - const clickX = e.clientX; - const clickY = e.clientY; - const moveBaseX = clickX - position.left; - const moveBaseY = clickY - position.top; - const browserWidth = window.innerWidth; - const browserHeight = window.innerHeight; - const windowWidth = this.refs.main.offsetWidth; - const windowHeight = this.refs.main.offsetHeight; - - // 動かした時 - dragListen(me => { - let moveLeft = me.clientX - moveBaseX; - let moveTop = me.clientY - moveBaseY; - - // 上はみ出し - if (moveTop < 0) moveTop = 0; - - // 左はみ出し - if (moveLeft < 0) moveLeft = 0; - - // 下はみ出し - if (moveTop + windowHeight > browserHeight) moveTop = browserHeight - windowHeight; - - // 右はみ出し - if (moveLeft + windowWidth > browserWidth) moveLeft = browserWidth - windowWidth; - - this.refs.main.style.left = moveLeft + 'px'; - this.refs.main.style.top = moveTop + 'px'; - }); - }; - - // 上ハンドル掴み時 - this.onTopHandleMousedown = e => { - e.preventDefault(); - - const base = e.clientY; - const height = parseInt(getComputedStyle(this.refs.main, '').height, 10); - const top = parseInt(getComputedStyle(this.refs.main, '').top, 10); - - // 動かした時 - dragListen(me => { - const move = me.clientY - base; - if (top + move > 0) { - if (height + -move > this.minHeight) { - this.applyTransformHeight(height + -move); - this.applyTransformTop(top + move); - } else { // 最小の高さより小さくなろうとした時 - this.applyTransformHeight(this.minHeight); - this.applyTransformTop(top + (height - this.minHeight)); - } - } else { // 上のはみ出し時 - this.applyTransformHeight(top + height); - this.applyTransformTop(0); - } - }); - }; - - // 右ハンドル掴み時 - this.onRightHandleMousedown = e => { - e.preventDefault(); - - const base = e.clientX; - const width = parseInt(getComputedStyle(this.refs.main, '').width, 10); - const left = parseInt(getComputedStyle(this.refs.main, '').left, 10); - const browserWidth = window.innerWidth; - - // 動かした時 - dragListen(me => { - const move = me.clientX - base; - if (left + width + move < browserWidth) { - if (width + move > this.minWidth) { - this.applyTransformWidth(width + move); - } else { // 最小の幅より小さくなろうとした時 - this.applyTransformWidth(this.minWidth); - } - } else { // 右のはみ出し時 - this.applyTransformWidth(browserWidth - left); - } - }); - }; - - // 下ハンドル掴み時 - this.onBottomHandleMousedown = e => { - e.preventDefault(); - - const base = e.clientY; - const height = parseInt(getComputedStyle(this.refs.main, '').height, 10); - const top = parseInt(getComputedStyle(this.refs.main, '').top, 10); - const browserHeight = window.innerHeight; - - // 動かした時 - dragListen(me => { - const move = me.clientY - base; - if (top + height + move < browserHeight) { - if (height + move > this.minHeight) { - this.applyTransformHeight(height + move); - } else { // 最小の高さより小さくなろうとした時 - this.applyTransformHeight(this.minHeight); - } - } else { // 下のはみ出し時 - this.applyTransformHeight(browserHeight - top); - } - }); - }; - - // 左ハンドル掴み時 - this.onLeftHandleMousedown = e => { - e.preventDefault(); - - const base = e.clientX; - const width = parseInt(getComputedStyle(this.refs.main, '').width, 10); - const left = parseInt(getComputedStyle(this.refs.main, '').left, 10); - - // 動かした時 - dragListen(me => { - const move = me.clientX - base; - if (left + move > 0) { - if (width + -move > this.minWidth) { - this.applyTransformWidth(width + -move); - this.applyTransformLeft(left + move); - } else { // 最小の幅より小さくなろうとした時 - this.applyTransformWidth(this.minWidth); - this.applyTransformLeft(left + (width - this.minWidth)); - } - } else { // 左のはみ出し時 - this.applyTransformWidth(left + width); - this.applyTransformLeft(0); - } - }); - }; - - // 左上ハンドル掴み時 - this.onTopLeftHandleMousedown = e => { - this.onTopHandleMousedown(e); - this.onLeftHandleMousedown(e); - }; - - // 右上ハンドル掴み時 - this.onTopRightHandleMousedown = e => { - this.onTopHandleMousedown(e); - this.onRightHandleMousedown(e); - }; - - // 右下ハンドル掴み時 - this.onBottomRightHandleMousedown = e => { - this.onBottomHandleMousedown(e); - this.onRightHandleMousedown(e); - }; - - // 左下ハンドル掴み時 - this.onBottomLeftHandleMousedown = e => { - this.onBottomHandleMousedown(e); - this.onLeftHandleMousedown(e); - }; - - // 高さを適用 - this.applyTransformHeight = height => { - this.refs.main.style.height = height + 'px'; - }; - - // 幅を適用 - this.applyTransformWidth = width => { - this.refs.main.style.width = width + 'px'; - }; - - // Y座標を適用 - this.applyTransformTop = top => { - this.refs.main.style.top = top + 'px'; - }; - - // X座標を適用 - this.applyTransformLeft = left => { - this.refs.main.style.left = left + 'px'; - }; - - function dragListen(fn) { - window.addEventListener('mousemove', fn); - window.addEventListener('mouseleave', dragClear.bind(null, fn)); - window.addEventListener('mouseup', dragClear.bind(null, fn)); - } - - function dragClear(fn) { - window.removeEventListener('mousemove', fn); - window.removeEventListener('mouseleave', dragClear); - window.removeEventListener('mouseup', dragClear); - } - - this.ondragover = e => { - e.dataTransfer.dropEffect = 'none'; - }; - - this.onKeydown = e => { - if (e.which == 27) { // Esc - if (this.canClose) { - e.preventDefault(); - e.stopPropagation(); - this.close(); - } - } - }; - - </script> -</mk-window> diff --git a/src/web/app/desktop/ui.styl b/src/web/app/desktop/ui.styl deleted file mode 100644 index cb98bf06a0..0000000000 --- a/src/web/app/desktop/ui.styl +++ /dev/null @@ -1,122 +0,0 @@ -@import "../app" - -button - font-family sans-serif - - * - pointer-events none - -button.ui -.button.ui - display inline-block - cursor pointer - padding 0 14px - margin 0 - min-width 100px - line-height 38px - font-size 14px - color #888 - text-decoration none - background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%) - border solid 1px #e2e2e2 - border-radius 4px - outline none - - &:focus - &:after - content "" - pointer-events none - position absolute - top -5px - right -5px - bottom -5px - left -5px - border 2px solid rgba($theme-color, 0.3) - border-radius 8px - - &:disabled - opacity 0.7 - cursor default - - &:hover - background linear-gradient(to bottom, #f9f9f9 0%, #ececec 100%) - border-color #dcdcdc - - &:active - background #ececec - border-color #dcdcdc - - &.primary - color $theme-color-foreground - background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%) - border solid 1px lighten($theme-color, 15%) - - &:not(:disabled) - font-weight bold - - &:hover:not(:disabled) - background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%) - border-color $theme-color - - &:active:not(:disabled) - background $theme-color - border-color $theme-color - -input:not([type]).ui -input[type='text'].ui -input[type='password'].ui -input[type='email'].ui -input[type='date'].ui -input[type='number'].ui -textarea.ui - display block - padding 10px - width 100% - height 40px - font-family sans-serif - font-size 16px - color #55595c - border solid 1px #dadada - border-radius 4px - - &:hover - border-color #b0b0b0 - - &:focus - border-color $theme-color - -textarea.ui - min-width 100% - max-width 100% - min-height 64px - -.ui.info - display block - margin 1em 0 - padding 0 1em - font-size 90% - color rgba(#000, 0.87) - background #f8f8f9 - border solid 1px rgba(34, 36, 38, 0.22) - border-radius 4px - - > p - opacity 0.8 - - > [data-fa]:first-child - margin-right 0.25em - - &.warn - color #573a08 - background #FFFAF3 - border-color #C9BA9B - -.ui.from.group - display block - margin 16px 0 - - > p:first-child - margin 0 0 6px 0 - font-size 90% - font-weight bold - color rgba(#373a3c, 0.9) diff --git a/src/web/app/dev/router.ts b/src/web/app/dev/router.ts deleted file mode 100644 index fcd2b1f76b..0000000000 --- a/src/web/app/dev/router.ts +++ /dev/null @@ -1,42 +0,0 @@ -import * as riot from 'riot'; -import * as route from 'page'; -let page = null; - -export default () => { - 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.ts b/src/web/app/dev/script.ts deleted file mode 100644 index b115c5be48..0000000000 --- a/src/web/app/dev/script.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Developer Center - */ - -// Style -import './style.styl'; - -require('./tags'); -import init from '../init'; -import route from './router'; - -/** - * init - */ -init(() => { - // Start routing - route(); -}); diff --git a/src/web/app/dev/style.styl b/src/web/app/dev/style.styl deleted file mode 100644 index cdbcb0e261..0000000000 --- a/src/web/app/dev/style.styl +++ /dev/null @@ -1,5 +0,0 @@ -@import "../app" -@import "../reset" - -html - background-color #fff 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 fdd442a836..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" if={ nidState == 'wait' } style="color:#999">%fa:spinner .pulse .fw%確認しています...</p> - <p class="info" if={ nidState == 'ok' } style="color:#3CB7B5">%fa:fw check%利用できます</p> - <p class="info" if={ nidState == 'unavailable' } style="color:#FF1161">%fa:fw exclamation-triangle%既に利用されています</p> - <p class="info" if={ nidState == 'error' } style="color:#FF1161">%fa:fw exclamation-triangle%通信エラー</p> - <p class="info" if={ nidState == 'invalid-format' } style="color:#FF1161">%fa:fw exclamation-triangle%a~z、A~Z、0~9、-(ハイフン)が使えます</p> - <p class="info" if={ nidState == 'min-range' } style="color:#FF1161">%fa:fw exclamation-triangle%3文字以上でお願いします!</p> - <p class="info" 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 onclick={ onsubmit }>アプリ作成</button> - </form> - <style> - :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> - 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.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.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 b25e0d8595..0000000000 --- a/src/web/app/dev/tags/pages/app.tag +++ /dev/null @@ -1,32 +0,0 @@ -<mk-app-page> - <p if={ fetching }>読み込み中</p> - <main if={ !fetching }> - <header> - <h1>{ app.name }</h1> - </header> - <div class="body"> - <p>App Secret</p> - <input value={ app.secret } readonly="readonly"/> - </div> - </main> - <style> - :scope - display block - </style> - <script> - this.mixin('api'); - - this.fetching = true; - - this.on('mount', () => { - this.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 43db70fcf2..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 if={ fetching }>読み込み中</p> - <virtual if={ !fetching }> - <p if={ apps.length == 0 }>アプリなし</p> - <ul if={ apps.length > 0 }> - <li each={ app in apps }><a href={ '/app/' + app.id }> - <p class="name">{ app.name }</p></a></li> - </ul> - </virtual> - </div> - <style> - :scope - display block - </style> - <script> - this.mixin('api'); - - this.fetching = true; - - this.on('mount', () => { - this.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 f863876fa7..0000000000 --- a/src/web/app/dev/tags/pages/index.tag +++ /dev/null @@ -1,6 +0,0 @@ -<mk-index><a href="/apps">アプリ</a> - <style> - :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 238b6865e1..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> - :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/init.css b/src/web/app/init.css deleted file mode 100644 index 2587f63943..0000000000 --- a/src/web/app/init.css +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Boot screen style - */ - -@charset 'utf-8'; - -html { - font-family: sans-serif; -} - -body > noscript { - position: fixed; - z-index: 2; - top: 0; - left: 0; - width: 100%; - height: 100%; - text-align: center; - background: #fff; -} - body > noscript > p { - display: block; - margin: 32px; - font-size: 2em; - color: #555; - } - -#ini { - position: fixed; - z-index: 1; - top: 0; - left: 0; - width: 100%; - height: 100%; - text-align: center; - background: #fff; - cursor: wait; -} - #ini > p { - display: block; - user-select: none; - margin: 32px; - font-size: 4em; - color: #555; - } - #ini > p > span { - animation: ini 1.4s infinite ease-in-out both; - } - #ini > p > span:nth-child(1) { - animation-delay: 0s; - } - #ini > p > span:nth-child(2) { - animation-delay: 0.16s; - } - #ini > p > span:nth-child(3) { - animation-delay: 0.32s; - } - -@keyframes ini { - 0%, 80%, 100% { - opacity: 1; - } - 40% { - opacity: 0; - } -} diff --git a/src/web/app/init.ts b/src/web/app/init.ts deleted file mode 100644 index 154b1ba0f0..0000000000 --- a/src/web/app/init.ts +++ /dev/null @@ -1,109 +0,0 @@ -/** - * App initializer - */ - -declare const _VERSION_: string; -declare const _LANG_: string; -declare const _HOST_: string; -declare const __CONSTS__: any; - -import * as riot from 'riot'; -import checkForUpdate from './common/scripts/check-for-update'; -import mixin from './common/mixins'; -import MiOS from './common/mios'; -require('./common/tags'); - -/** - * APP ENTRY POINT! - */ - -console.info(`Misskey v${_VERSION_} (葵 aoi)`); - -// BootTimer解除 -window.clearTimeout((window as any).mkBootTimer); -delete (window as any).mkBootTimer; - -if (_HOST_ != 'localhost') { - document.domain = _HOST_; -} - -{ // 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); -} - -// Set global configuration -(riot as any).mixin(__CONSTS__); - -// 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); -} - -// MiOSを初期化してコールバックする -export default (callback, sw = false) => { - const mios = new MiOS(sw); - - mios.init(() => { - // ミックスイン初期化 - mixin(mios); - - // ローディング画面クリア - const ini = document.getElementById('ini'); - ini.parentNode.removeChild(ini); - - // アプリ基底要素マウント - const app = document.createElement('div'); - app.setAttribute('id', 'app'); - document.body.appendChild(app); - - try { - callback(mios); - } catch (e) { - panic(e); - } - - // 更新チェック - setTimeout(() => { - checkForUpdate(mios); - }, 3000); - }); -}; - -// BSoD -function panic(e) { - console.error(e); - - // Display blue screen - document.documentElement.style.background = '#1269e2'; - document.body.innerHTML = - '<div id="error">' - + '<h1>:( 致命的な問題が発生しました。</h1>' - + '<p>お使いのブラウザ(またはOS)のバージョンを更新すると解決する可能性があります。</p>' - + '<hr>' - + `<p>エラーコード: ${e.toString()}</p>` - + `<p>ブラウザ バージョン: ${navigator.userAgent}</p>` - + `<p>クライアント バージョン: ${_VERSION_}</p>` - + '<hr>' - + '<p>問題が解決しない場合は、上記の情報をお書き添えの上 syuilotan@yahoo.co.jp までご連絡ください。</p>' - + '<p>Thank you for using Misskey.</p>' - + '</div>'; - - // TODO: Report the bug -} diff --git a/src/web/app/mobile/router.ts b/src/web/app/mobile/router.ts deleted file mode 100644 index 0358d10e9e..0000000000 --- a/src/web/app/mobile/router.ts +++ /dev/null @@ -1,148 +0,0 @@ -/** - * Mobile App Router - */ - -import * as riot from 'riot'; -import * as route from 'page'; -import MiOS from '../common/mios'; -let page = null; - -export default (mios: MiOS) => { - 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() { - mios.isSignedin ? 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.ts b/src/web/app/mobile/script.ts deleted file mode 100644 index 4dfff8f72f..0000000000 --- a/src/web/app/mobile/script.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Mobile Client - */ - -// Style -import './style.styl'; - -require('./tags'); -import init from '../init'; -import route from './router'; -import MiOS from '../common/mios'; - -/** - * init - */ -init((mios: MiOS) => { - // http://qiita.com/junya/items/3ff380878f26ca447f85 - document.body.setAttribute('ontouchstart', ''); - - // Start routing - route(mios); -}, true); diff --git a/src/web/app/mobile/scripts/open-post-form.ts b/src/web/app/mobile/scripts/open-post-form.ts deleted file mode 100644 index e0fae4d8ca..0000000000 --- a/src/web/app/mobile/scripts/open-post-form.ts +++ /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/ui-event.ts b/src/web/app/mobile/scripts/ui-event.ts deleted file mode 100644 index 2e406549a4..0000000000 --- a/src/web/app/mobile/scripts/ui-event.ts +++ /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/style.styl b/src/web/app/mobile/style.styl deleted file mode 100644 index 63e4f2349f..0000000000 --- a/src/web/app/mobile/style.styl +++ /dev/null @@ -1,7 +0,0 @@ -@import "../app" -@import "../reset" - -#wait - top auto - bottom 15px - left 15px diff --git a/src/web/app/mobile/tags/drive-folder-selector.tag b/src/web/app/mobile/tags/drive-folder-selector.tag deleted file mode 100644 index 35d0208a07..0000000000 --- a/src/web/app/mobile/tags/drive-folder-selector.tag +++ /dev/null @@ -1,69 +0,0 @@ -<mk-drive-folder-selector> - <div class="body"> - <header> - <h1>%i18n:mobile.tags.mk-drive-folder-selector.select-folder%</h1> - <button class="close" onclick={ cancel }>%fa:times%</button> - <button class="ok" onclick={ ok }>%fa:check%</button> - </header> - <mk-drive ref="browser" select-folder={ true }/> - </div> - <style> - :scope - display block - position fixed - z-index 2048 - top 0 - left 0 - width 100% - height 100% - padding 8px - background rgba(0, 0, 0, 0.2) - - > .body - width 100% - height 100% - background #fff - - > header - border-bottom solid 1px #eee - - > h1 - margin 0 - padding 0 - text-align center - line-height 42px - font-size 1em - font-weight normal - - > .close - position absolute - top 0 - left 0 - line-height 42px - width 42px - - > .ok - position absolute - top 0 - right 0 - line-height 42px - width 42px - - > mk-drive - height calc(100% - 42px) - overflow scroll - -webkit-overflow-scrolling touch - - </style> - <script> - this.cancel = () => { - this.trigger('canceled'); - this.unmount(); - }; - - this.ok = () => { - this.trigger('selected', this.refs.browser.folder); - this.unmount(); - }; - </script> -</mk-drive-folder-selector> diff --git a/src/web/app/mobile/tags/drive-selector.tag b/src/web/app/mobile/tags/drive-selector.tag deleted file mode 100644 index f8bc49dab0..0000000000 --- a/src/web/app/mobile/tags/drive-selector.tag +++ /dev/null @@ -1,88 +0,0 @@ -<mk-drive-selector> - <div class="body"> - <header> - <h1>%i18n:mobile.tags.mk-drive-selector.select-file%<span class="count" if={ files.length > 0 }>({ files.length })</span></h1> - <button class="close" onclick={ cancel }>%fa:times%</button> - <button if={ opts.multiple } class="ok" onclick={ ok }>%fa:check%</button> - </header> - <mk-drive ref="browser" select-file={ true } multiple={ opts.multiple }/> - </div> - <style> - :scope - display block - position fixed - z-index 2048 - top 0 - left 0 - width 100% - height 100% - padding 8px - background rgba(0, 0, 0, 0.2) - - > .body - width 100% - height 100% - background #fff - - > header - border-bottom solid 1px #eee - - > h1 - margin 0 - padding 0 - text-align center - line-height 42px - font-size 1em - font-weight normal - - > .count - margin-left 4px - opacity 0.5 - - > .close - position absolute - top 0 - left 0 - line-height 42px - width 42px - - > .ok - position absolute - top 0 - right 0 - line-height 42px - width 42px - - > mk-drive - height calc(100% - 42px) - overflow scroll - -webkit-overflow-scrolling touch - - </style> - <script> - this.files = []; - - this.on('mount', () => { - this.refs.browser.on('change-selection', files => { - this.update({ - files: files - }); - }); - - this.refs.browser.on('selected', file => { - this.trigger('selected', file); - this.unmount(); - }); - }); - - this.cancel = () => { - this.trigger('canceled'); - this.unmount(); - }; - - this.ok = () => { - this.trigger('selected', this.files); - this.unmount(); - }; - </script> -</mk-drive-selector> diff --git a/src/web/app/mobile/tags/drive.tag b/src/web/app/mobile/tags/drive.tag deleted file mode 100644 index 41dbfddae9..0000000000 --- a/src/web/app/mobile/tags/drive.tag +++ /dev/null @@ -1,580 +0,0 @@ -<mk-drive> - <nav ref="nav"> - <a onclick={ goRoot } href="/i/drive">%fa:cloud%%i18n:mobile.tags.mk-drive.drive%</a> - <virtual each={ folder in hierarchyFolders }> - <span>%fa:angle-right%</span> - <a onclick={ move } href="/i/drive/folder/{ folder.id }">{ folder.name }</a> - </virtual> - <virtual if={ folder != null }> - <span>%fa:angle-right%</span> - <p>{ folder.name }</p> - </virtual> - <virtual if={ file != null }> - <span>%fa:angle-right%</span> - <p>{ file.name }</p> - </virtual> - </nav> - <mk-uploader ref="uploader"/> - <div class="browser { fetching: fetching }" if={ file == null }> - <div class="info" if={ info }> - <p if={ folder == null }>{ (info.usage / info.capacity * 100).toFixed(1) }% %i18n:mobile.tags.mk-drive.used%</p> - <p if={ folder != null && (folder.folders_count > 0 || folder.files_count > 0) }> - <virtual if={ folder.folders_count > 0 }>{ folder.folders_count } %i18n:mobile.tags.mk-drive.folder-count%</virtual> - <virtual if={ folder.folders_count > 0 && folder.files_count > 0 }>%i18n:mobile.tags.mk-drive.count-separator%</virtual> - <virtual if={ folder.files_count > 0 }>{ folder.files_count } %i18n:mobile.tags.mk-drive.file-count%</virtual> - </p> - </div> - <div class="folders" if={ folders.length > 0 }> - <virtual each={ folder in folders }> - <mk-drive-folder folder={ folder }/> - </virtual> - <p if={ moreFolders }>%i18n:mobile.tags.mk-drive.load-more%</p> - </div> - <div class="files" if={ files.length > 0 }> - <virtual each={ file in files }> - <mk-drive-file file={ file }/> - </virtual> - <button class="more" if={ moreFiles } onclick={ fetchMoreFiles }> - { fetchingMoreFiles ? '%i18n:common.loading%' : '%i18n:mobile.tags.mk-drive.load-more%' } - </button> - </div> - <div class="empty" if={ files.length == 0 && folders.length == 0 && !fetching }> - <p if={ folder == null }>%i18n:mobile.tags.mk-drive.nothing-in-drive%</p> - <p if={ folder != null }>%i18n:mobile.tags.mk-drive.folder-is-empty%</p> - </div> - </div> - <div class="fetching" if={ fetching && file == null && files.length == 0 && folders.length == 0 }> - <div class="spinner"> - <div class="dot1"></div> - <div class="dot2"></div> - </div> - </div> - <input ref="file" type="file" multiple="multiple" onchange={ changeLocalFile }/> - <mk-drive-file-viewer if={ file != null } file={ file }/> - <style> - :scope - display block - background #fff - - > nav - display block - position sticky - position -webkit-sticky - top 0 - z-index 1 - width 100% - padding 10px 12px - overflow auto - white-space nowrap - font-size 0.9em - color rgba(0, 0, 0, 0.67) - -webkit-backdrop-filter blur(12px) - backdrop-filter blur(12px) - background-color rgba(#fff, 0.75) - border-bottom solid 1px rgba(0, 0, 0, 0.13) - - > p - > a - display inline - margin 0 - padding 0 - text-decoration none !important - color inherit - - &:last-child - font-weight bold - - > [data-fa] - margin-right 4px - - > span - margin 0 8px - opacity 0.5 - - > .browser - &.fetching - opacity 0.5 - - > .info - border-bottom solid 1px #eee - - &:empty - display none - - > p - display block - max-width 500px - margin 0 auto - padding 4px 16px - font-size 10px - color #777 - - > .folders - > mk-drive-folder - border-bottom solid 1px #eee - - > .files - > mk-drive-file - border-bottom solid 1px #eee - - > .more - display block - width 100% - padding 16px - font-size 16px - color #555 - - > .empty - padding 16px - text-align center - color #999 - pointer-events none - - > p - margin 0 - - > .fetching - .spinner - margin 100px auto - width 40px - height 40px - text-align center - - animation sk-rotate 2.0s infinite linear - - .dot1, .dot2 - width 60% - height 60% - display inline-block - position absolute - top 0 - background rgba(0, 0, 0, 0.2) - border-radius 100% - - animation sk-bounce 2.0s infinite ease-in-out - - .dot2 - top auto - bottom 0 - animation-delay -1.0s - - @keyframes sk-rotate { 100% { transform: rotate(360deg); }} - - @keyframes sk-bounce { - 0%, 100% { - transform: scale(0.0); - } 50% { - transform: scale(1.0); - } - } - - > [ref='file'] - display none - - </style> - <script> - this.mixin('i'); - this.mixin('api'); - - this.mixin('drive-stream'); - this.connection = this.driveStream.getConnection(); - this.connectionId = this.driveStream.use(); - - this.files = []; - this.folders = []; - this.hierarchyFolders = []; - this.selectedFiles = []; - - // 現在の階層(フォルダ) - // * null でルートを表す - this.folder = null; - - this.file = null; - - this.isFileSelectMode = this.opts.selectFile; - this.multiple = this.opts.multiple; - - this.on('mount', () => { - this.connection.on('file_created', this.onStreamDriveFileCreated); - this.connection.on('file_updated', this.onStreamDriveFileUpdated); - this.connection.on('folder_created', this.onStreamDriveFolderCreated); - this.connection.on('folder_updated', this.onStreamDriveFolderUpdated); - - if (this.opts.folder) { - this.cd(this.opts.folder, true); - } else if (this.opts.file) { - this.cf(this.opts.file, true); - } else { - this.fetch(); - } - - if (this.opts.isNaked) { - this.refs.nav.style.top = `${this.opts.top}px`; - } - }); - - this.on('unmount', () => { - this.connection.off('file_created', this.onStreamDriveFileCreated); - this.connection.off('file_updated', this.onStreamDriveFileUpdated); - this.connection.off('folder_created', this.onStreamDriveFolderCreated); - this.connection.off('folder_updated', this.onStreamDriveFolderUpdated); - this.driveStream.dispose(this.connectionId); - }); - - this.onStreamDriveFileCreated = file => { - this.addFile(file, true); - }; - - this.onStreamDriveFileUpdated = file => { - const current = this.folder ? this.folder.id : null; - if (current != file.folder_id) { - this.removeFile(file); - } else { - this.addFile(file, true); - } - }; - - this.onStreamDriveFolderCreated = folder => { - this.addFolder(folder, true); - }; - - this.onStreamDriveFolderUpdated = folder => { - const current = this.folder ? this.folder.id : null; - if (current != folder.parent_id) { - this.removeFolder(folder); - } else { - this.addFolder(folder, true); - } - }; - - this.move = ev => { - ev.preventDefault(); - this.cd(ev.item.folder); - return false; - }; - - this.cd = (target, silent = false) => { - this.file = null; - - if (target == null) { - this.goRoot(); - return; - } else if (typeof target == 'object') target = target.id; - - this.update({ - fetching: true - }); - - this.api('drive/folders/show', { - folder_id: target - }).then(folder => { - this.folder = folder; - this.hierarchyFolders = []; - - if (folder.parent) dive(folder.parent); - - this.update(); - this.trigger('open-folder', this.folder, silent); - this.fetch(); - }); - }; - - this.addFolder = (folder, unshift = false) => { - const current = this.folder ? this.folder.id : null; - // 追加しようとしているフォルダが、今居る階層とは違う階層のものだったら中断 - if (current != folder.parent_id) return; - - // 追加しようとしているフォルダを既に所有してたら中断 - if (this.folders.some(f => f.id == folder.id)) return; - - if (unshift) { - this.folders.unshift(folder); - } else { - this.folders.push(folder); - } - - this.update(); - }; - - this.addFile = (file, unshift = false) => { - const current = this.folder ? this.folder.id : null; - // 追加しようとしているファイルが、今居る階層とは違う階層のものだったら中断 - if (current != file.folder_id) return; - - if (this.files.some(f => f.id == file.id)) { - const exist = this.files.map(f => f.id).indexOf(file.id); - this.files[exist] = file; - this.update(); - return; - } - - if (unshift) { - this.files.unshift(file); - } else { - this.files.push(file); - } - - this.update(); - }; - - this.removeFolder = folder => { - if (typeof folder == 'object') folder = folder.id; - this.folders = this.folders.filter(f => f.id != folder); - this.update(); - }; - - this.removeFile = file => { - if (typeof file == 'object') file = file.id; - this.files = this.files.filter(f => f.id != file); - this.update(); - }; - - this.appendFile = file => this.addFile(file); - this.appendFolder = file => this.addFolder(file); - this.prependFile = file => this.addFile(file, true); - this.prependFolder = file => this.addFolder(file, true); - - this.goRoot = ev => { - ev.preventDefault(); - - if (this.folder || this.file) { - this.update({ - file: null, - folder: null, - hierarchyFolders: [] - }); - this.trigger('move-root'); - this.fetch(); - } - - return false; - }; - - this.fetch = () => { - this.update({ - folders: [], - files: [], - moreFolders: false, - moreFiles: false, - fetching: true - }); - - this.trigger('begin-fetch'); - - let fetchedFolders = null; - let fetchedFiles = null; - - const foldersMax = 20; - const filesMax = 20; - - // フォルダ一覧取得 - this.api('drive/folders', { - folder_id: this.folder ? this.folder.id : null, - limit: foldersMax + 1 - }).then(folders => { - if (folders.length == foldersMax + 1) { - this.moreFolders = true; - folders.pop(); - } - fetchedFolders = folders; - complete(); - }); - - // ファイル一覧取得 - this.api('drive/files', { - folder_id: this.folder ? this.folder.id : null, - limit: filesMax + 1 - }).then(files => { - if (files.length == filesMax + 1) { - this.moreFiles = true; - files.pop(); - } - fetchedFiles = files; - complete(); - }); - - let flag = false; - const complete = () => { - if (flag) { - fetchedFolders.forEach(this.appendFolder); - fetchedFiles.forEach(this.appendFile); - this.update({ - fetching: false - }); - // 一連の読み込みが完了したイベントを発行 - this.trigger('fetched'); - } else { - flag = true; - // 一連の読み込みが半分完了したイベントを発行 - this.trigger('fetch-mid'); - } - }; - - if (this.folder == null) { - // Fetch addtional drive info - this.api('drive').then(info => { - this.update({ info }); - }); - } - }; - - this.fetchMoreFiles = () => { - this.update({ - fetching: true, - fetchingMoreFiles: true - }); - - const max = 30; - - // ファイル一覧取得 - this.api('drive/files', { - folder_id: this.folder ? this.folder.id : null, - limit: max + 1, - max_id: this.files[this.files.length - 1].id - }).then(files => { - if (files.length == max + 1) { - this.moreFiles = true; - files.pop(); - } else { - this.moreFiles = false; - } - files.forEach(this.appendFile); - this.update({ - fetching: false, - fetchingMoreFiles: false - }); - }); - }; - - this.chooseFile = file => { - if (this.isFileSelectMode) { - if (this.multiple) { - if (this.selectedFiles.some(f => f.id == file.id)) { - this.selectedFiles = this.selectedFiles.filter(f => f.id != file.id); - } else { - this.selectedFiles.push(file); - } - this.update(); - this.trigger('change-selection', this.selectedFiles); - } else { - this.trigger('selected', file); - } - } else { - this.cf(file); - } - }; - - this.cf = (file, silent = false) => { - if (typeof file == 'object') file = file.id; - - this.update({ - fetching: true - }); - - this.api('drive/files/show', { - file_id: file - }).then(file => { - this.fetching = false; - this.file = file; - this.folder = null; - this.hierarchyFolders = []; - - if (file.folder) dive(file.folder); - - this.update(); - this.trigger('open-file', this.file, silent); - }); - }; - - const dive = folder => { - this.hierarchyFolders.unshift(folder); - if (folder.parent) dive(folder.parent); - }; - - this.openContextMenu = () => { - const fn = window.prompt('何をしますか?(数字を入力してください): <1 → ファイルをアップロード | 2 → ファイルをURLでアップロード | 3 → フォルダ作成 | 4 → このフォルダ名を変更 | 5 → このフォルダを移動 | 6 → このフォルダを削除>'); - if (fn == null || fn == '') return; - switch (fn) { - case '1': - this.selectLocalFile(); - break; - case '2': - this.urlUpload(); - break; - case '3': - this.createFolder(); - break; - case '4': - this.renameFolder(); - break; - case '5': - this.moveFolder(); - break; - case '6': - alert('ごめんなさい!フォルダの削除は未実装です...。'); - break; - } - }; - - this.selectLocalFile = () => { - this.refs.file.click(); - }; - - this.createFolder = () => { - const name = window.prompt('フォルダー名'); - if (name == null || name == '') return; - this.api('drive/folders/create', { - name: name, - parent_id: this.folder ? this.folder.id : undefined - }).then(folder => { - this.addFolder(folder, true); - this.update(); - }); - }; - - this.renameFolder = () => { - if (this.folder == null) { - alert('現在いる場所はルートで、フォルダではないため名前の変更はできません。名前を変更したいフォルダに移動してからやってください。'); - return; - } - const name = window.prompt('フォルダー名', this.folder.name); - if (name == null || name == '') return; - this.api('drive/folders/update', { - name: name, - folder_id: this.folder.id - }).then(folder => { - this.cd(folder); - }); - }; - - this.moveFolder = () => { - if (this.folder == null) { - alert('現在いる場所はルートで、フォルダではないため移動はできません。移動したいフォルダに移動してからやってください。'); - return; - } - const dialog = riot.mount(document.body.appendChild(document.createElement('mk-drive-folder-selector')))[0]; - dialog.one('selected', folder => { - this.api('drive/folders/update', { - parent_id: folder ? folder.id : null, - folder_id: this.folder.id - }).then(folder => { - this.cd(folder); - }); - }); - }; - - this.urlUpload = () => { - const url = window.prompt('アップロードしたいファイルのURL'); - if (url == null || url == '') return; - this.api('drive/files/upload_from_url', { - url: url, - folder_id: this.folder ? this.folder.id : undefined - }); - alert('アップロードをリクエストしました。アップロードが完了するまで時間がかかる場合があります。'); - }; - - this.changeLocalFile = () => { - Array.from(this.refs.file.files).forEach(f => this.refs.uploader.upload(f, this.folder)); - }; - </script> -</mk-drive> diff --git a/src/web/app/mobile/tags/drive/file-viewer.tag b/src/web/app/mobile/tags/drive/file-viewer.tag deleted file mode 100644 index 48fc83fa67..0000000000 --- a/src/web/app/mobile/tags/drive/file-viewer.tag +++ /dev/null @@ -1,277 +0,0 @@ -<mk-drive-file-viewer> - <div class="preview"> - <img if={ kind == 'image' } src={ file.url } alt={ file.name } title={ file.name } onload={ onImageLoaded } ref="img"> - <virtual if={ kind != 'image' }>%fa:file%</virtual> - <footer if={ kind == 'image' && file.properties && file.properties.width && file.properties.height }> - <span class="size"> - <span class="width">{ file.properties.width }</span> - <span class="time">×</span> - <span class="height">{ file.properties.height }</span> - <span class="px">px</span> - </span> - <span class="separator"></span> - <span class="aspect-ratio"> - <span class="width">{ file.properties.width / gcd(file.properties.width, file.properties.height) }</span> - <span class="colon">:</span> - <span class="height">{ file.properties.height / gcd(file.properties.width, file.properties.height) }</span> - </span> - </footer> - </div> - <div class="info"> - <div> - <span class="type"><mk-file-type-icon type={ file.type }/>{ file.type }</span> - <span class="separator"></span> - <span class="data-size">{ bytesToSize(file.datasize) }</span> - <span class="separator"></span> - <span class="created-at" onclick={ showCreatedAt }>%fa:R clock%<mk-time time={ file.created_at }/></span> - </div> - </div> - <div class="menu"> - <div> - <a href={ file.url + '?download' } download={ file.name }> - %fa:download%%i18n:mobile.tags.mk-drive-file-viewer.download% - </a> - <button onclick={ rename }> - %fa:pencil-alt%%i18n:mobile.tags.mk-drive-file-viewer.rename% - </button> - <button onclick={ move }> - %fa:R folder-open%%i18n:mobile.tags.mk-drive-file-viewer.move% - </button> - </div> - </div> - <div class="exif" show={ exif }> - <div> - <p> - %fa:camera%%i18n:mobile.tags.mk-drive-file-viewer.exif% - </p> - <pre ref="exif" class="json">{ exif ? JSON.stringify(exif, null, 2) : '' }</pre> - </div> - </div> - <div class="hash"> - <div> - <p> - %fa:hashtag%%i18n:mobile.tags.mk-drive-file-viewer.hash% - </p> - <code>{ file.md5 }</code> - </div> - </div> - <style> - :scope - display block - - > .preview - padding 8px - background #f0f0f0 - - > img - display block - max-width 100% - max-height 300px - margin 0 auto - box-shadow 1px 1px 4px rgba(0, 0, 0, 0.2) - - > footer - padding 8px 8px 0 8px - font-size 0.8em - color #888 - text-align center - - > .separator - display inline - padding 0 4px - - > .size - display inline - - .time - margin 0 2px - - .px - margin-left 4px - - > .aspect-ratio - display inline - opacity 0.7 - - &:before - content "(" - - &:after - content ")" - - > .info - padding 14px - font-size 0.8em - border-top solid 1px #dfdfdf - - > div - max-width 500px - margin 0 auto - - > .separator - padding 0 4px - color #cdcdcd - - > .type - > .data-size - color #9d9d9d - - > mk-file-type-icon - margin-right 4px - - > .created-at - color #bdbdbd - - > [data-fa] - margin-right 2px - - > .menu - padding 14px - border-top solid 1px #dfdfdf - - > div - max-width 500px - margin 0 auto - - > * - display block - width 100% - padding 10px 16px - margin 0 0 12px 0 - color #333 - font-size 0.9em - text-align center - text-decoration none - text-shadow 0 1px 0 rgba(255, 255, 255, 0.9) - background-image linear-gradient(#fafafa, #eaeaea) - border 1px solid #ddd - border-bottom-color #cecece - border-radius 3px - - &:last-child - margin-bottom 0 - - &:active - background-color #767676 - background-image none - border-color #444 - box-shadow 0 1px 3px rgba(0, 0, 0, 0.075), inset 0 0 5px rgba(0, 0, 0, 0.2) - - > [data-fa] - margin-right 4px - - > .hash - padding 14px - border-top solid 1px #dfdfdf - - > div - max-width 500px - margin 0 auto - - > p - display block - margin 0 - padding 0 - color #555 - font-size 0.9em - - > [data-fa] - margin-right 4px - - > code - display block - width 100% - margin 6px 0 0 0 - padding 8px - white-space nowrap - overflow auto - font-size 0.8em - color #222 - border solid 1px #dfdfdf - border-radius 2px - background #f5f5f5 - - > .exif - padding 14px - border-top solid 1px #dfdfdf - - > div - max-width 500px - margin 0 auto - - > p - display block - margin 0 - padding 0 - color #555 - font-size 0.9em - - > [data-fa] - margin-right 4px - - > pre - display block - width 100% - margin 6px 0 0 0 - padding 8px - height 128px - overflow auto - font-size 0.9em - border solid 1px #dfdfdf - border-radius 2px - background #f5f5f5 - - </style> - <script> - import EXIF from 'exif-js'; - import hljs from 'highlight.js'; - import bytesToSize from '../../../common/scripts/bytes-to-size'; - import gcd from '../../../common/scripts/gcd'; - - this.bytesToSize = bytesToSize; - this.gcd = gcd; - - this.mixin('api'); - - this.file = this.opts.file; - this.kind = this.file.type.split('/')[0]; - - this.onImageLoaded = () => { - const self = this; - EXIF.getData(this.refs.img, function() { - const allMetaData = EXIF.getAllTags(this); - self.update({ - exif: allMetaData - }); - hljs.highlightBlock(self.refs.exif); - }); - }; - - this.rename = () => { - const name = window.prompt('名前を変更', this.file.name); - if (name == null || name == '' || name == this.file.name) return; - this.api('drive/files/update', { - file_id: this.file.id, - name: name - }).then(() => { - this.parent.cf(this.file, true); - }); - }; - - this.move = () => { - const dialog = riot.mount(document.body.appendChild(document.createElement('mk-drive-folder-selector')))[0]; - dialog.one('selected', folder => { - this.api('drive/files/update', { - file_id: this.file.id, - folder_id: folder == null ? null : folder.id - }).then(() => { - this.parent.cf(this.file, true); - }); - }); - }; - - this.showCreatedAt = () => { - alert(new Date(this.file.created_at).toLocaleString()); - }; - </script> -</mk-drive-file-viewer> diff --git a/src/web/app/mobile/tags/drive/file.tag b/src/web/app/mobile/tags/drive/file.tag deleted file mode 100644 index 196dd1141e..0000000000 --- a/src/web/app/mobile/tags/drive/file.tag +++ /dev/null @@ -1,147 +0,0 @@ -<mk-drive-file data-is-selected={ isSelected }> - <a onclick={ onclick } href="/i/drive/file/{ file.id }"> - <div class="container"> - <div class="thumbnail" style={ 'background-image: url(' + file.url + '?thumbnail&size=128)' }></div> - <div class="body"> - <p class="name"><span>{ file.name.lastIndexOf('.') != -1 ? file.name.substr(0, file.name.lastIndexOf('.')) : file.name }</span><span class="ext" if={ file.name.lastIndexOf('.') != -1 }>{ file.name.substr(file.name.lastIndexOf('.')) }</span></p> - <!-- - if file.tags.length > 0 - ul.tags - each tag in file.tags - li.tag(style={background: tag.color, color: contrast(tag.color)})= tag.name - --> - <footer> - <p class="type"><mk-file-type-icon type={ file.type }/>{ file.type }</p> - <p class="separator"></p> - <p class="data-size">{ bytesToSize(file.datasize) }</p> - <p class="separator"></p> - <p class="created-at"> - %fa:R clock%<mk-time time={ file.created_at }/> - </p> - </footer> - </div> - </div> - </a> - <style> - :scope - display block - - > a - display block - text-decoration none !important - - * - user-select none - pointer-events none - - > .container - max-width 500px - margin 0 auto - padding 16px - - &:after - content "" - display block - clear both - - > .thumbnail - display block - float left - width 64px - height 64px - background-size cover - background-position center center - - > .body - display block - float left - width calc(100% - 74px) - margin-left 10px - - > .name - display block - margin 0 - padding 0 - font-size 0.9em - font-weight bold - color #555 - text-overflow ellipsis - overflow-wrap break-word - - > .ext - opacity 0.5 - - > .tags - display block - margin 4px 0 0 0 - padding 0 - list-style none - font-size 0.5em - - > .tag - display inline-block - margin 0 5px 0 0 - padding 1px 5px - border-radius 2px - - > footer - display block - margin 4px 0 0 0 - font-size 0.7em - - > .separator - display inline - margin 0 - padding 0 4px - color #CDCDCD - - > .type - display inline - margin 0 - padding 0 - color #9D9D9D - - > mk-file-type-icon - margin-right 4px - - > .data-size - display inline - margin 0 - padding 0 - color #9D9D9D - - > .created-at - display inline - margin 0 - padding 0 - color #BDBDBD - - > [data-fa] - margin-right 2px - - &[data-is-selected] - background $theme-color - - &, * - color #fff !important - - </style> - <script> - import bytesToSize from '../../../common/scripts/bytes-to-size'; - this.bytesToSize = bytesToSize; - - this.browser = this.parent; - this.file = this.opts.file; - this.isSelected = this.browser.selectedFiles.some(f => f.id == this.file.id); - - this.browser.on('change-selection', selections => { - this.isSelected = selections.some(f => f.id == this.file.id); - }); - - this.onclick = ev => { - ev.preventDefault(); - this.browser.chooseFile(this.file); - return false; - }; - </script> -</mk-drive-file> diff --git a/src/web/app/mobile/tags/drive/folder.tag b/src/web/app/mobile/tags/drive/folder.tag deleted file mode 100644 index 6125e0b254..0000000000 --- a/src/web/app/mobile/tags/drive/folder.tag +++ /dev/null @@ -1,53 +0,0 @@ -<mk-drive-folder> - <a onclick={ onclick } href="/i/drive/folder/{ folder.id }"> - <div class="container"> - <p class="name">%fa:folder%{ folder.name }</p>%fa:angle-right% - </div> - </a> - <style> - :scope - display block - - > a - display block - color #777 - text-decoration none !important - - * - user-select none - pointer-events none - - > .container - max-width 500px - margin 0 auto - padding 16px - - > .name - display block - margin 0 - padding 0 - - > [data-fa] - margin-right 6px - - > [data-fa] - position absolute - top 0 - bottom 0 - right 20px - - > * - height 100% - - </style> - <script> - this.browser = this.parent; - this.folder = this.opts.folder; - - this.onclick = ev => { - ev.preventDefault(); - this.browser.cd(this.folder); - return false; - }; - </script> -</mk-drive-folder> diff --git a/src/web/app/mobile/tags/follow-button.tag b/src/web/app/mobile/tags/follow-button.tag deleted file mode 100644 index 5b710bfa9d..0000000000 --- a/src/web/app/mobile/tags/follow-button.tag +++ /dev/null @@ -1,131 +0,0 @@ -<mk-follow-button> - <button class={ wait: wait, follow: !user.is_following, unfollow: user.is_following } if={ !init } onclick={ onclick } disabled={ wait }> - <virtual if={ !wait && user.is_following }>%fa:minus%</virtual> - <virtual if={ !wait && !user.is_following }>%fa:plus%</virtual> - <virtual if={ wait }>%fa:spinner .pulse .fw%</virtual>{ user.is_following ? '%i18n:mobile.tags.mk-follow-button.unfollow%' : '%i18n:mobile.tags.mk-follow-button.follow%' } - </button> - <div class="init" if={ init }>%fa:spinner .pulse .fw%</div> - <style> - :scope - display block - - > button - > .init - display block - user-select none - cursor pointer - padding 0 16px - margin 0 - height inherit - font-size 16px - outline none - border solid 1px $theme-color - border-radius 4px - - * - pointer-events none - - &.follow - color $theme-color - background transparent - - &:hover - background rgba($theme-color, 0.1) - - &:active - background rgba($theme-color, 0.2) - - &.unfollow - color $theme-color-foreground - background $theme-color - - &.wait - cursor wait !important - opacity 0.7 - - &.init - cursor wait !important - opacity 0.7 - - > [data-fa] - margin-right 4px - - </style> - <script> - import isPromise from '../../common/scripts/is-promise'; - - this.mixin('i'); - this.mixin('api'); - - this.mixin('stream'); - this.connection = this.stream.getConnection(); - this.connectionId = this.stream.use(); - - this.user = null; - this.userPromise = isPromise(this.opts.user) - ? this.opts.user - : Promise.resolve(this.opts.user); - this.init = true; - this.wait = false; - - this.on('mount', () => { - this.userPromise.then(user => { - this.update({ - init: false, - user: user - }); - this.connection.on('follow', this.onStreamFollow); - this.connection.on('unfollow', this.onStreamUnfollow); - }); - }); - - this.on('unmount', () => { - this.connection.off('follow', this.onStreamFollow); - this.connection.off('unfollow', this.onStreamUnfollow); - this.stream.dispose(this.connectionId); - }); - - this.onStreamFollow = user => { - if (user.id == this.user.id) { - this.update({ - user: user - }); - } - }; - - this.onStreamUnfollow = user => { - if (user.id == this.user.id) { - this.update({ - user: user - }); - } - }; - - this.onclick = () => { - this.wait = true; - if (this.user.is_following) { - this.api('following/delete', { - user_id: this.user.id - }).then(() => { - this.user.is_following = false; - }).catch(err => { - console.error(err); - }).then(() => { - this.wait = false; - this.update(); - }); - } else { - this.api('following/create', { - user_id: this.user.id - }).then(() => { - this.user.is_following = true; - }).catch(err => { - console.error(err); - }).then(() => { - this.wait = false; - this.update(); - }); - } - }; - </script> -</mk-follow-button> diff --git a/src/web/app/mobile/tags/home-timeline.tag b/src/web/app/mobile/tags/home-timeline.tag deleted file mode 100644 index e96823fa10..0000000000 --- a/src/web/app/mobile/tags/home-timeline.tag +++ /dev/null @@ -1,69 +0,0 @@ -<mk-home-timeline> - <mk-init-following if={ noFollowing } /> - <mk-timeline ref="timeline" init={ init } more={ more } empty={ '%i18n:mobile.tags.mk-home-timeline.empty-timeline%' }/> - <style> - :scope - display block - - > mk-init-following - margin-bottom 8px - - </style> - <script> - this.mixin('i'); - this.mixin('api'); - - this.mixin('stream'); - this.connection = this.stream.getConnection(); - this.connectionId = this.stream.use(); - - this.noFollowing = this.I.following_count == 0; - - this.init = new Promise((res, rej) => { - this.api('posts/timeline').then(posts => { - res(posts); - this.trigger('loaded'); - }); - }); - - this.fetch = () => { - this.api('posts/timeline').then(posts => { - this.refs.timeline.setPosts(posts); - }); - }; - - this.on('mount', () => { - this.connection.on('post', this.onStreamPost); - this.connection.on('follow', this.onStreamFollow); - this.connection.on('unfollow', this.onStreamUnfollow); - }); - - this.on('unmount', () => { - this.connection.off('post', this.onStreamPost); - this.connection.off('follow', this.onStreamFollow); - this.connection.off('unfollow', this.onStreamUnfollow); - this.stream.dispose(this.connectionId); - }); - - this.more = () => { - return this.api('posts/timeline', { - max_id: this.refs.timeline.tail().id - }); - }; - - this.onStreamPost = post => { - this.update({ - isEmpty: false - }); - this.refs.timeline.addPost(post); - }; - - this.onStreamFollow = () => { - this.fetch(); - }; - - this.onStreamUnfollow = () => { - this.fetch(); - }; - </script> -</mk-home-timeline> diff --git a/src/web/app/mobile/tags/home.tag b/src/web/app/mobile/tags/home.tag deleted file mode 100644 index d92e3ae4e5..0000000000 --- a/src/web/app/mobile/tags/home.tag +++ /dev/null @@ -1,23 +0,0 @@ -<mk-home> - <mk-home-timeline ref="tl"/> - <style> - :scope - display block - - > mk-home-timeline - max-width 600px - margin 0 auto - padding 8px - - @media (min-width 500px) - padding 16px - - </style> - <script> - this.on('mount', () => { - this.refs.tl.on('loaded', () => { - this.trigger('loaded'); - }); - }); - </script> -</mk-home> diff --git a/src/web/app/mobile/tags/images-viewer.tag b/src/web/app/mobile/tags/images-viewer.tag deleted file mode 100644 index 8ef4a50be0..0000000000 --- a/src/web/app/mobile/tags/images-viewer.tag +++ /dev/null @@ -1,26 +0,0 @@ -<mk-images-viewer> - <div class="image" ref="view" onclick={ click }><img ref="img" src={ image.url + '?thumbnail&size=512' } alt={ image.name } title={ image.name }/></div> - <style> - :scope - display block - overflow hidden - border-radius 4px - - > .image - - > img - display block - max-height 256px - max-width 100% - margin 0 auto - - </style> - <script> - this.images = this.opts.images; - this.image = this.images[0]; - - this.click = () => { - window.open(this.image.url); - }; - </script> -</mk-images-viewer> diff --git a/src/web/app/mobile/tags/index.ts b/src/web/app/mobile/tags/index.ts deleted file mode 100644 index 19952c20cd..0000000000 --- a/src/web/app/mobile/tags/index.ts +++ /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/init-following.tag b/src/web/app/mobile/tags/init-following.tag deleted file mode 100644 index 105a1f70d3..0000000000 --- a/src/web/app/mobile/tags/init-following.tag +++ /dev/null @@ -1,130 +0,0 @@ -<mk-init-following> - <p class="title">気になるユーザーをフォロー:</p> - <div class="users" if={ !fetching && users.length > 0 }> - <virtual each={ users }> - <mk-user-card user={ this } /> - </virtual> - </div> - <p class="empty" if={ !fetching && users.length == 0 }>おすすめのユーザーは見つかりませんでした。</p> - <p class="fetching" if={ fetching }>%fa:spinner .pulse .fw%読み込んでいます<mk-ellipsis/></p> - <a class="refresh" onclick={ refresh }>もっと見る</a> - <button class="close" onclick={ close } title="閉じる">%fa:times%</button> - <style> - :scope - display block - background #fff - border-radius 8px - box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2) - - > .title - margin 0 - padding 8px 16px - font-size 1em - font-weight bold - color #888 - - > .users - overflow-x scroll - -webkit-overflow-scrolling touch - white-space nowrap - padding 16px - background #eee - - > mk-user-card - &:not(:last-child) - margin-right 16px - - > .empty - margin 0 - padding 16px - text-align center - color #aaa - - > .fetching - margin 0 - padding 16px - text-align center - color #aaa - - > [data-fa] - margin-right 4px - - > .refresh - display block - margin 0 - padding 8px 16px - text-align right - font-size 0.9em - color #999 - - > .close - cursor pointer - display block - position absolute - top 0 - right 0 - z-index 1 - margin 0 - padding 0 - font-size 1.2em - color #999 - border none - outline none - background transparent - - &:hover - color #555 - - &:active - color #222 - - > [data-fa] - padding 10px - - </style> - <script> - this.mixin('api'); - - this.users = null; - this.fetching = true; - - this.limit = 6; - this.page = 0; - - this.on('mount', () => { - this.fetch(); - }); - - this.fetch = () => { - this.update({ - fetching: true, - users: null - }); - - this.api('users/recommendation', { - limit: this.limit, - offset: this.limit * this.page - }).then(users => { - this.fetching = false - this.users = users - this.update({ - fetching: false, - users: users - }); - }); - }; - - this.refresh = () => { - if (this.users.length < this.limit) { - this.page = 0; - } else { - this.page++; - } - this.fetch(); - }; - - this.close = () => { - this.unmount(); - }; - </script> -</mk-init-following> diff --git a/src/web/app/mobile/tags/notification-preview.tag b/src/web/app/mobile/tags/notification-preview.tag deleted file mode 100644 index ab923ea9d7..0000000000 --- a/src/web/app/mobile/tags/notification-preview.tag +++ /dev/null @@ -1,110 +0,0 @@ -<mk-notification-preview class={ notification.type }> - <virtual if={ notification.type == 'reaction' }> - <img class="avatar" src={ notification.user.avatar_url + '?thumbnail&size=64' } alt="avatar"/> - <div class="text"> - <p><mk-reaction-icon reaction={ notification.reaction }/>{ notification.user.name }</p> - <p class="post-ref">%fa:quote-left%{ getPostSummary(notification.post) }%fa:quote-right%</p> - </div> - </virtual> - <virtual if={ notification.type == 'repost' }> - <img class="avatar" src={ notification.post.user.avatar_url + '?thumbnail&size=64' } alt="avatar"/> - <div class="text"> - <p>%fa:retweet%{ notification.post.user.name }</p> - <p class="post-ref">%fa:quote-left%{ getPostSummary(notification.post.repost) }%fa:quote-right%</p> - </div> - </virtual> - <virtual if={ notification.type == 'quote' }> - <img class="avatar" src={ notification.post.user.avatar_url + '?thumbnail&size=64' } alt="avatar"/> - <div class="text"> - <p>%fa:quote-left%{ notification.post.user.name }</p> - <p class="post-preview">{ getPostSummary(notification.post) }</p> - </div> - </virtual> - <virtual if={ notification.type == 'follow' }> - <img class="avatar" src={ notification.user.avatar_url + '?thumbnail&size=64' } alt="avatar"/> - <div class="text"> - <p>%fa:user-plus%{ notification.user.name }</p> - </div> - </virtual> - <virtual if={ notification.type == 'reply' }> - <img class="avatar" src={ notification.post.user.avatar_url + '?thumbnail&size=64' } alt="avatar"/> - <div class="text"> - <p>%fa:reply%{ notification.post.user.name }</p> - <p class="post-preview">{ getPostSummary(notification.post) }</p> - </div> - </virtual> - <virtual if={ notification.type == 'mention' }> - <img class="avatar" src={ notification.post.user.avatar_url + '?thumbnail&size=64' } alt="avatar"/> - <div class="text"> - <p>%fa:at%{ notification.post.user.name }</p> - <p class="post-preview">{ getPostSummary(notification.post) }</p> - </div> - </virtual> - <virtual if={ notification.type == 'poll_vote' }> - <img class="avatar" src={ notification.user.avatar_url + '?thumbnail&size=64' } alt="avatar"/> - <div class="text"> - <p>%fa:chart-pie%{ notification.user.name }</p> - <p class="post-ref">%fa:quote-left%{ getPostSummary(notification.post) }%fa:quote-right%</p> - </div> - </virtual> - <style> - :scope - display block - margin 0 - padding 8px - color #fff - overflow-wrap break-word - - &:after - content "" - display block - clear both - - img - display block - float left - min-width 36px - min-height 36px - max-width 36px - max-height 36px - border-radius 6px - - .text - float right - width calc(100% - 36px) - padding-left 8px - - p - margin 0 - - i, mk-reaction-icon - margin-right 4px - - .post-ref - - [data-fa] - font-size 1em - font-weight normal - font-style normal - display inline-block - margin-right 3px - - &.repost, &.quote - .text p i - color #77B255 - - &.follow - .text p i - color #53c7ce - - &.reply, &.mention - .text p i - color #fff - - </style> - <script> - import getPostSummary from '../../../../common/get-post-summary.ts'; - this.getPostSummary = getPostSummary; - this.notification = this.opts.notification; - </script> -</mk-notification-preview> diff --git a/src/web/app/mobile/tags/notification.tag b/src/web/app/mobile/tags/notification.tag deleted file mode 100644 index de44caea2a..0000000000 --- a/src/web/app/mobile/tags/notification.tag +++ /dev/null @@ -1,169 +0,0 @@ -<mk-notification class={ notification.type }> - <mk-time time={ notification.created_at }/> - <virtual if={ notification.type == 'reaction' }> - <a class="avatar-anchor" href={ '/' + notification.user.username }> - <img class="avatar" src={ notification.user.avatar_url + '?thumbnail&size=64' } alt="avatar"/> - </a> - <div class="text"> - <p> - <mk-reaction-icon reaction={ notification.reaction }/> - <a href={ '/' + notification.user.username }>{ notification.user.name }</a> - </p> - <a class="post-ref" href={ '/' + notification.post.user.username + '/' + notification.post.id }> - %fa:quote-left%{ getPostSummary(notification.post) }%fa:quote-right% - </a> - </div> - </virtual> - <virtual if={ notification.type == 'repost' }> - <a class="avatar-anchor" href={ '/' + notification.post.user.username }> - <img class="avatar" src={ notification.post.user.avatar_url + '?thumbnail&size=64' } alt="avatar"/> - </a> - <div class="text"> - <p> - %fa:retweet% - <a href={ '/' + notification.post.user.username }>{ notification.post.user.name }</a> - </p> - <a class="post-ref" href={ '/' + notification.post.user.username + '/' + notification.post.id }> - %fa:quote-left%{ getPostSummary(notification.post.repost) }%fa:quote-right% - </a> - </div> - </virtual> - <virtual if={ notification.type == 'quote' }> - <a class="avatar-anchor" href={ '/' + notification.post.user.username }> - <img class="avatar" src={ notification.post.user.avatar_url + '?thumbnail&size=64' } alt="avatar"/> - </a> - <div class="text"> - <p> - %fa:quote-left% - <a href={ '/' + notification.post.user.username }>{ notification.post.user.name }</a> - </p> - <a class="post-preview" href={ '/' + notification.post.user.username + '/' + notification.post.id }>{ getPostSummary(notification.post) }</a> - </div> - </virtual> - <virtual if={ notification.type == 'follow' }> - <a class="avatar-anchor" href={ '/' + notification.user.username }> - <img class="avatar" src={ notification.user.avatar_url + '?thumbnail&size=64' } alt="avatar"/> - </a> - <div class="text"> - <p> - %fa:user-plus% - <a href={ '/' + notification.user.username }>{ notification.user.name }</a> - </p> - </div> - </virtual> - <virtual if={ notification.type == 'reply' }> - <a class="avatar-anchor" href={ '/' + notification.post.user.username }> - <img class="avatar" src={ notification.post.user.avatar_url + '?thumbnail&size=64' } alt="avatar"/> - </a> - <div class="text"> - <p> - %fa:reply% - <a href={ '/' + notification.post.user.username }>{ notification.post.user.name }</a> - </p> - <a class="post-preview" href={ '/' + notification.post.user.username + '/' + notification.post.id }>{ getPostSummary(notification.post) }</a> - </div> - </virtual> - <virtual if={ notification.type == 'mention' }> - <a class="avatar-anchor" href={ '/' + notification.post.user.username }> - <img class="avatar" src={ notification.post.user.avatar_url + '?thumbnail&size=64' } alt="avatar"/> - </a> - <div class="text"> - <p> - %fa:at% - <a href={ '/' + notification.post.user.username }>{ notification.post.user.name }</a> - </p> - <a class="post-preview" href={ '/' + notification.post.user.username + '/' + notification.post.id }>{ getPostSummary(notification.post) }</a> - </div> - </virtual> - <virtual if={ notification.type == 'poll_vote' }> - <a class="avatar-anchor" href={ '/' + notification.user.username }> - <img class="avatar" src={ notification.user.avatar_url + '?thumbnail&size=64' } alt="avatar"/> - </a> - <div class="text"> - <p> - %fa:chart-pie% - <a href={ '/' + notification.user.username }>{ notification.user.name }</a> - </p> - <a class="post-ref" href={ '/' + notification.post.user.username + '/' + notification.post.id }> - %fa:quote-left%{ getPostSummary(notification.post) }%fa:quote-right% - </a> - </div> - </virtual> - <style> - :scope - display block - margin 0 - padding 16px - overflow-wrap break-word - - > mk-time - display inline - position absolute - top 16px - right 12px - vertical-align top - color rgba(0, 0, 0, 0.6) - font-size 12px - - &:after - content "" - display block - clear both - - .avatar-anchor - display block - float left - - img - min-width 36px - min-height 36px - max-width 36px - max-height 36px - border-radius 6px - - .text - float right - width calc(100% - 36px) - padding-left 8px - - p - margin 0 - - i, mk-reaction-icon - margin-right 4px - - .post-preview - color rgba(0, 0, 0, 0.7) - - .post-ref - color rgba(0, 0, 0, 0.7) - - [data-fa] - font-size 1em - font-weight normal - font-style normal - display inline-block - margin-right 3px - - &.repost, &.quote - .text p i - color #77B255 - - &.follow - .text p i - color #53c7ce - - &.reply, &.mention - .text p i - color #555 - - .post-preview - color rgba(0, 0, 0, 0.7) - - </style> - <script> - import getPostSummary from '../../../../common/get-post-summary.ts'; - this.getPostSummary = getPostSummary; - this.notification = this.opts.notification; - </script> -</mk-notification> diff --git a/src/web/app/mobile/tags/notifications.tag b/src/web/app/mobile/tags/notifications.tag deleted file mode 100644 index c3500d1b84..0000000000 --- a/src/web/app/mobile/tags/notifications.tag +++ /dev/null @@ -1,164 +0,0 @@ -<mk-notifications> - <div class="notifications" if={ notifications.length != 0 }> - <virtual each={ notification, i in notifications }> - <mk-notification notification={ notification }/> - <p class="date" if={ i != notifications.length - 1 && notification._date != notifications[i + 1]._date }><span>%fa:angle-up%{ notification._datetext }</span><span>%fa:angle-down%{ notifications[i + 1]._datetext }</span></p> - </virtual> - </div> - <button class="more" if={ moreNotifications } onclick={ fetchMoreNotifications } disabled={ fetchingMoreNotifications }> - <virtual if={ fetchingMoreNotifications }>%fa:spinner .pulse .fw%</virtual>{ fetchingMoreNotifications ? '%i18n:common.loading%' : '%i18n:mobile.tags.mk-notifications.more%' } - </button> - <p class="empty" if={ notifications.length == 0 && !loading }>%i18n:mobile.tags.mk-notifications.empty%</p> - <p class="loading" if={ loading }>%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p> - <style> - :scope - display block - margin 8px auto - padding 0 - max-width 500px - width calc(100% - 16px) - background #fff - border-radius 8px - box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2) - - @media (min-width 500px) - margin 16px auto - width calc(100% - 32px) - - > .notifications - - > mk-notification - margin 0 auto - max-width 500px - border-bottom solid 1px rgba(0, 0, 0, 0.05) - - &:last-child - border-bottom none - - > .date - display block - margin 0 - line-height 32px - text-align center - font-size 0.8em - color #aaa - background #fdfdfd - border-bottom solid 1px rgba(0, 0, 0, 0.05) - - span - margin 0 16px - - i - margin-right 8px - - > .more - display block - width 100% - padding 16px - color #555 - border-top solid 1px rgba(0, 0, 0, 0.05) - - > [data-fa] - margin-right 4px - - > .empty - margin 0 - padding 16px - text-align center - color #aaa - - > .loading - margin 0 - padding 16px - text-align center - color #aaa - - > [data-fa] - margin-right 4px - - </style> - <script> - import getPostSummary from '../../../../common/get-post-summary.ts'; - this.getPostSummary = getPostSummary; - - this.mixin('api'); - - this.mixin('stream'); - this.connection = this.stream.getConnection(); - this.connectionId = this.stream.use(); - - this.notifications = []; - this.loading = true; - - this.on('mount', () => { - const max = 10; - - this.api('i/notifications', { - limit: max + 1 - }).then(notifications => { - if (notifications.length == max + 1) { - this.moreNotifications = true; - notifications.pop(); - } - - this.update({ - loading: false, - notifications: notifications - }); - - this.trigger('fetched'); - }); - - this.connection.on('notification', this.onNotification); - }); - - this.on('unmount', () => { - this.connection.off('notification', this.onNotification); - this.stream.dispose(this.connectionId); - }); - - this.on('update', () => { - this.notifications.forEach(notification => { - const date = new Date(notification.created_at).getDate(); - const month = new Date(notification.created_at).getMonth() + 1; - notification._date = date; - notification._datetext = `${month}月 ${date}日`; - }); - }); - - this.onNotification = notification => { - // TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない - this.connection.send({ - type: 'read_notification', - id: notification.id - }); - - this.notifications.unshift(notification); - this.update(); - }; - - this.fetchMoreNotifications = () => { - this.update({ - fetchingMoreNotifications: true - }); - - const max = 30; - - this.api('i/notifications', { - limit: max + 1, - max_id: this.notifications[this.notifications.length - 1].id - }).then(notifications => { - if (notifications.length == max + 1) { - this.moreNotifications = true; - notifications.pop(); - } else { - this.moreNotifications = false; - } - this.update({ - notifications: this.notifications.concat(notifications), - fetchingMoreNotifications: false - }); - }); - }; - </script> -</mk-notifications> diff --git a/src/web/app/mobile/tags/notify.tag b/src/web/app/mobile/tags/notify.tag deleted file mode 100644 index 2dfc2dddb8..0000000000 --- a/src/web/app/mobile/tags/notify.tag +++ /dev/null @@ -1,40 +0,0 @@ -<mk-notify> - <mk-notification-preview notification={ opts.notification }/> - <style> - :scope - display block - position fixed - z-index 1024 - bottom -64px - left 0 - width 100% - height 64px - pointer-events none - -webkit-backdrop-filter blur(2px) - backdrop-filter blur(2px) - background-color rgba(#000, 0.5) - - </style> - <script> - import anime from 'animejs'; - - this.on('mount', () => { - anime({ - targets: this.root, - bottom: '0px', - duration: 500, - easing: 'easeOutQuad' - }); - - setTimeout(() => { - anime({ - targets: this.root, - bottom: '-64px', - duration: 500, - easing: 'easeOutQuad', - complete: () => this.unmount() - }); - }, 6000); - }); - </script> -</mk-notify> diff --git a/src/web/app/mobile/tags/page/drive.tag b/src/web/app/mobile/tags/page/drive.tag deleted file mode 100644 index 0033ffe653..0000000000 --- a/src/web/app/mobile/tags/page/drive.tag +++ /dev/null @@ -1,73 +0,0 @@ -<mk-drive-page> - <mk-ui ref="ui"> - <mk-drive ref="browser" folder={ parent.opts.folder } file={ parent.opts.file } is-naked={ true } top={ 48 }/> - </mk-ui> - <style> - :scope - display block - </style> - <script> - import ui from '../../scripts/ui-event'; - import Progress from '../../../common/scripts/loading'; - - this.on('mount', () => { - document.title = 'Misskey Drive'; - ui.trigger('title', '%fa:cloud%%i18n:mobile.tags.mk-drive-page.drive%'); - - ui.trigger('func', () => { - this.refs.ui.refs.browser.openContextMenu(); - }, '%fa:ellipsis-h%'); - - this.refs.ui.refs.browser.on('begin-fetch', () => { - Progress.start(); - }); - - this.refs.ui.refs.browser.on('fetched-mid', () => { - Progress.set(0.5); - }); - - this.refs.ui.refs.browser.on('fetched', () => { - Progress.done(); - }); - - this.refs.ui.refs.browser.on('move-root', () => { - const title = 'Misskey Drive'; - - // Rewrite URL - history.pushState(null, title, '/i/drive'); - - document.title = title; - ui.trigger('title', '%fa:cloud%%i18n:mobile.tags.mk-drive-page.drive%'); - }); - - this.refs.ui.refs.browser.on('open-folder', (folder, silent) => { - const title = folder.name + ' | Misskey Drive'; - - if (!silent) { - // Rewrite URL - history.pushState(null, title, '/i/drive/folder/' + folder.id); - } - - document.title = title; - // TODO: escape html characters in folder.name - ui.trigger('title', '%fa:R folder-open%' + folder.name); - }); - - this.refs.ui.refs.browser.on('open-file', (file, silent) => { - const title = file.name + ' | Misskey Drive'; - - if (!silent) { - // Rewrite URL - history.pushState(null, title, '/i/drive/file/' + file.id); - } - - document.title = title; - // TODO: escape html characters in file.name - ui.trigger('title', '<mk-file-type-icon class="icon"></mk-file-type-icon>' + file.name); - riot.mount('mk-file-type-icon', { - type: file.type - }); - }); - }); - </script> -</mk-drive-page> diff --git a/src/web/app/mobile/tags/page/entrance.tag b/src/web/app/mobile/tags/page/entrance.tag deleted file mode 100644 index 380fb780bc..0000000000 --- a/src/web/app/mobile/tags/page/entrance.tag +++ /dev/null @@ -1,66 +0,0 @@ -<mk-entrance> - <main><img src="/assets/title.svg" alt="Misskey"/> - <mk-entrance-signin if={ mode == 'signin' }/> - <mk-entrance-signup if={ mode == 'signup' }/> - <div class="introduction" if={ mode == 'introduction' }> - <mk-introduction/> - <button onclick={ signin }>%i18n:common.ok%</button> - </div> - </main> - <footer> - <mk-copyright/> - </footer> - <style> - :scope - display block - height 100% - - > main - display block - - > img - display block - width 130px - height 120px - margin 0 auto - - > .introduction - max-width 300px - margin 0 auto - color #666 - - > button - display block - margin 16px auto 0 auto - - > footer - > mk-copyright - margin 0 - text-align center - line-height 64px - font-size 10px - color rgba(#000, 0.5) - - </style> - <script> - this.mode = 'signin'; - - this.signup = () => { - this.update({ - mode: 'signup' - }); - }; - - this.signin = () => { - this.update({ - mode: 'signin' - }); - }; - - this.introduction = () => { - this.update({ - mode: 'introduction' - }); - }; - </script> -</mk-entrance> diff --git a/src/web/app/mobile/tags/page/entrance/signin.tag b/src/web/app/mobile/tags/page/entrance/signin.tag deleted file mode 100644 index 6f473feb9d..0000000000 --- a/src/web/app/mobile/tags/page/entrance/signin.tag +++ /dev/null @@ -1,52 +0,0 @@ -<mk-entrance-signin> - <mk-signin/> - <a href={ _API_URL_ + '/signin/twitter' }>Twitterでサインイン</a> - <div class="divider"><span>or</span></div> - <button class="signup" onclick={ parent.signup }>%i18n:mobile.tags.mk-entrance-signin.signup%</button><a class="introduction" onclick={ parent.introduction }>%i18n:mobile.tags.mk-entrance-signin.about%</a> - <style> - :scope - display block - margin 0 auto - padding 0 8px - max-width 350px - text-align center - - > .signup - padding 16px - width 100% - font-size 1em - color #fff - background $theme-color - border-radius 3px - - > .divider - padding 16px 0 - text-align center - - &:after - content "" - display block - position absolute - top 50% - width 100% - height 1px - border-top solid 1px rgba(0, 0, 0, 0.1) - - > * - z-index 1 - padding 0 8px - color rgba(0, 0, 0, 0.5) - background #fdfdfd - - > .introduction - display inline-block - margin-top 16px - font-size 12px - color #666 - - - - - - </style> -</mk-entrance-signin> diff --git a/src/web/app/mobile/tags/page/entrance/signup.tag b/src/web/app/mobile/tags/page/entrance/signup.tag deleted file mode 100644 index 7b11bcad4d..0000000000 --- a/src/web/app/mobile/tags/page/entrance/signup.tag +++ /dev/null @@ -1,38 +0,0 @@ -<mk-entrance-signup> - <mk-signup/> - <button class="cancel" type="button" onclick={ parent.signin } title="%i18n:mobile.tags.mk-entrance-signup.cancel%">%fa:times%</button> - <style> - :scope - display block - margin 0 auto - padding 0 8px - max-width 350px - - > .cancel - cursor pointer - display block - position absolute - top 0 - right 0 - z-index 1 - margin 0 - padding 0 - font-size 1.2em - color #999 - border none - outline none - box-shadow none - background transparent - transition opacity 0.1s ease - - &:hover - color #555 - - &:active - color #222 - - > [data-fa] - padding 14px - - </style> -</mk-entrance-signup> diff --git a/src/web/app/mobile/tags/page/home.tag b/src/web/app/mobile/tags/page/home.tag deleted file mode 100644 index 99cc6b29bf..0000000000 --- a/src/web/app/mobile/tags/page/home.tag +++ /dev/null @@ -1,62 +0,0 @@ -<mk-home-page> - <mk-ui ref="ui"> - <mk-home ref="home"/> - </mk-ui> - <style> - :scope - display block - </style> - <script> - import ui from '../../scripts/ui-event'; - import Progress from '../../../common/scripts/loading'; - import getPostSummary from '../../../../../common/get-post-summary.ts'; - import openPostForm from '../../scripts/open-post-form'; - - this.mixin('i'); - - this.mixin('stream'); - this.connection = this.stream.getConnection(); - this.connectionId = this.stream.use(); - - this.unreadCount = 0; - - this.on('mount', () => { - document.title = 'Misskey' - ui.trigger('title', '%fa:home%%i18n:mobile.tags.mk-home.home%'); - document.documentElement.style.background = '#313a42'; - - ui.trigger('func', () => { - openPostForm(); - }, '%fa:pencil-alt%'); - - Progress.start(); - - this.connection.on('post', this.onStreamPost); - document.addEventListener('visibilitychange', this.onVisibilitychange, false); - - this.refs.ui.refs.home.on('loaded', () => { - Progress.done(); - }); - }); - - this.on('unmount', () => { - this.connection.off('post', this.onStreamPost); - this.stream.dispose(this.connectionId); - document.removeEventListener('visibilitychange', this.onVisibilitychange); - }); - - this.onStreamPost = post => { - if (document.hidden && post.user_id !== this.I.id) { - this.unreadCount++; - document.title = `(${this.unreadCount}) ${getPostSummary(post)}`; - } - }; - - this.onVisibilitychange = () => { - if (!document.hidden) { - this.unreadCount = 0; - document.title = 'Misskey'; - } - }; - </script> -</mk-home-page> diff --git a/src/web/app/mobile/tags/page/messaging-room.tag b/src/web/app/mobile/tags/page/messaging-room.tag deleted file mode 100644 index 00ee265120..0000000000 --- a/src/web/app/mobile/tags/page/messaging-room.tag +++ /dev/null @@ -1,31 +0,0 @@ -<mk-messaging-room-page> - <mk-ui ref="ui"> - <mk-messaging-room if={ !parent.fetching } user={ parent.user } is-naked={ true }/> - </mk-ui> - <style> - :scope - display block - </style> - <script> - import ui from '../../scripts/ui-event'; - - this.mixin('api'); - - this.fetching = true; - - this.on('mount', () => { - this.api('users/show', { - username: this.opts.username - }).then(user => { - this.update({ - fetching: false, - user: user - }); - - document.title = `%i18n:mobile.tags.mk-messaging-room-page.message%: ${user.name} | Misskey`; - // TODO: ユーザー名をエスケープ - ui.trigger('title', '%fa:R comments%' + user.name); - }); - }); - </script> -</mk-messaging-room-page> diff --git a/src/web/app/mobile/tags/page/messaging.tag b/src/web/app/mobile/tags/page/messaging.tag deleted file mode 100644 index 29e98ce092..0000000000 --- a/src/web/app/mobile/tags/page/messaging.tag +++ /dev/null @@ -1,23 +0,0 @@ -<mk-messaging-page> - <mk-ui ref="ui"> - <mk-messaging ref="index"/> - </mk-ui> - <style> - :scope - display block - </style> - <script> - import ui from '../../scripts/ui-event'; - - this.mixin('page'); - - this.on('mount', () => { - document.title = 'Misskey | %i18n:mobile.tags.mk-messaging-page.message%'; - ui.trigger('title', '%fa:R comments%%i18n:mobile.tags.mk-messaging-page.message%'); - - this.refs.ui.refs.index.on('navigate-user', user => { - this.page('/i/messaging/' + user.username); - }); - }); - </script> -</mk-messaging-page> diff --git a/src/web/app/mobile/tags/page/new-post.tag b/src/web/app/mobile/tags/page/new-post.tag deleted file mode 100644 index 7adde3b329..0000000000 --- a/src/web/app/mobile/tags/page/new-post.tag +++ /dev/null @@ -1,7 +0,0 @@ -<mk-new-post-page> - <mk-post-form ref="form"/> - <style> - :scope - display block - </style> -</mk-new-post-page> diff --git a/src/web/app/mobile/tags/page/notifications.tag b/src/web/app/mobile/tags/page/notifications.tag deleted file mode 100644 index 1db9c5d661..0000000000 --- a/src/web/app/mobile/tags/page/notifications.tag +++ /dev/null @@ -1,39 +0,0 @@ -<mk-notifications-page> - <mk-ui ref="ui"> - <mk-notifications ref="notifications"/> - </mk-ui> - <style> - :scope - display block - </style> - <script> - import ui from '../../scripts/ui-event'; - import Progress from '../../../common/scripts/loading'; - - this.mixin('api'); - - this.on('mount', () => { - document.title = 'Misskey | %i18n:mobile.tags.mk-notifications-page.notifications%'; - ui.trigger('title', '%fa:R bell%%i18n:mobile.tags.mk-notifications-page.notifications%'); - document.documentElement.style.background = '#313a42'; - - ui.trigger('func', () => { - this.readAll(); - }, '%fa:check%'); - - Progress.start(); - - this.refs.ui.refs.notifications.on('fetched', () => { - Progress.done(); - }); - }); - - this.readAll = () => { - const ok = window.confirm('%i18n:mobile.tags.mk-notifications-page.read-all%'); - - if (!ok) return; - - this.api('notifications/mark_as_read_all'); - }; - </script> -</mk-notifications-page> diff --git a/src/web/app/mobile/tags/page/post.tag b/src/web/app/mobile/tags/page/post.tag deleted file mode 100644 index 5303ca8d34..0000000000 --- a/src/web/app/mobile/tags/page/post.tag +++ /dev/null @@ -1,76 +0,0 @@ -<mk-post-page> - <mk-ui ref="ui"> - <main if={ !parent.fetching }> - <a if={ parent.post.next } href={ parent.post.next }>%fa:angle-up%%i18n:mobile.tags.mk-post-page.next%</a> - <div> - <mk-post-detail ref="post" post={ parent.post }/> - </div> - <a if={ parent.post.prev } href={ parent.post.prev }>%fa:angle-down%%i18n:mobile.tags.mk-post-page.prev%</a> - </main> - </mk-ui> - <style> - :scope - display block - - main - text-align center - - > div - margin 8px auto - padding 0 - max-width 500px - width calc(100% - 16px) - - @media (min-width 500px) - margin 16px auto - width calc(100% - 32px) - - > a - display inline-block - - &:first-child - margin-top 8px - - @media (min-width 500px) - margin-top 16px - - &:last-child - margin-bottom 8px - - @media (min-width 500px) - margin-bottom 16px - - > [data-fa] - margin-right 4px - - </style> - <script> - import ui from '../../scripts/ui-event'; - import Progress from '../../../common/scripts/loading'; - - this.mixin('api'); - - this.fetching = true; - this.post = null; - - this.on('mount', () => { - document.title = 'Misskey'; - ui.trigger('title', '%fa:R sticky-note%%i18n:mobile.tags.mk-post-page.title%'); - document.documentElement.style.background = '#313a42'; - - Progress.start(); - - this.api('posts/show', { - post_id: this.opts.post - }).then(post => { - - this.update({ - fetching: false, - post: post - }); - - Progress.done(); - }); - }); - </script> -</mk-post-page> diff --git a/src/web/app/mobile/tags/page/search.tag b/src/web/app/mobile/tags/page/search.tag deleted file mode 100644 index 5c39d97e51..0000000000 --- a/src/web/app/mobile/tags/page/search.tag +++ /dev/null @@ -1,26 +0,0 @@ -<mk-search-page> - <mk-ui ref="ui"> - <mk-search ref="search" query={ parent.opts.query }/> - </mk-ui> - <style> - :scope - display block - </style> - <script> - import ui from '../../scripts/ui-event'; - import Progress from '../../../common/scripts/loading'; - - this.on('mount', () => { - document.title = `%i18n:mobile.tags.mk-search-page.search%: ${this.opts.query} | Misskey` - // TODO: クエリをHTMLエスケープ - ui.trigger('title', '%fa:search%' + this.opts.query); - document.documentElement.style.background = '#313a42'; - - Progress.start(); - - this.refs.ui.refs.search.on('loaded', () => { - Progress.done(); - }); - }); - </script> -</mk-search-page> diff --git a/src/web/app/mobile/tags/page/selectdrive.tag b/src/web/app/mobile/tags/page/selectdrive.tag deleted file mode 100644 index 1a790d806c..0000000000 --- a/src/web/app/mobile/tags/page/selectdrive.tag +++ /dev/null @@ -1,87 +0,0 @@ -<mk-selectdrive-page> - <header> - <h1>%i18n:mobile.tags.mk-selectdrive-page.select-file%<span class="count" if={ files.length > 0 }>({ files.length })</span></h1> - <button class="upload" onclick={ upload }>%fa:upload%</button> - <button if={ multiple } class="ok" onclick={ ok }>%fa:check%</button> - </header> - <mk-drive ref="browser" select-file={ true } multiple={ multiple } is-naked={ true } top={ 42 }/> - - <style> - :scope - display block - width 100% - height 100% - background #fff - - > header - position fixed - top 0 - left 0 - width 100% - z-index 1000 - background #fff - box-shadow 0 1px rgba(0, 0, 0, 0.1) - - > h1 - margin 0 - padding 0 - text-align center - line-height 42px - font-size 1em - font-weight normal - - > .count - margin-left 4px - opacity 0.5 - - > .upload - position absolute - top 0 - left 0 - line-height 42px - width 42px - - > .ok - position absolute - top 0 - right 0 - line-height 42px - width 42px - - > mk-drive - top 42px - - </style> - <script> - const q = (new URL(location)).searchParams; - this.multiple = q.get('multiple') == 'true' ? true : false; - - this.on('mount', () => { - document.documentElement.style.background = '#fff'; - - this.refs.browser.on('selected', file => { - this.files = [file]; - this.ok(); - }); - - this.refs.browser.on('change-selection', files => { - this.update({ - files: files - }); - }); - }); - - this.upload = () => { - this.refs.browser.selectLocalFile(); - }; - - this.close = () => { - window.close(); - }; - - this.ok = () => { - window.opener.cb(this.multiple ? this.files : this.files[0]); - window.close(); - }; - </script> -</mk-selectdrive-page> diff --git a/src/web/app/mobile/tags/page/settings.tag b/src/web/app/mobile/tags/page/settings.tag deleted file mode 100644 index 9789782144..0000000000 --- a/src/web/app/mobile/tags/page/settings.tag +++ /dev/null @@ -1,101 +0,0 @@ -<mk-settings-page> - <mk-ui ref="ui"> - <mk-settings /> - </mk-ui> - <style> - :scope - display block - </style> - <script> - import ui from '../../scripts/ui-event'; - - this.on('mount', () => { - document.title = 'Misskey | %i18n:mobile.tags.mk-settings-page.settings%'; - ui.trigger('title', '%fa:cog%%i18n:mobile.tags.mk-settings-page.settings%'); - document.documentElement.style.background = '#313a42'; - }); - </script> -</mk-settings-page> - -<mk-settings> - <p><mk-raw content={ '%i18n:mobile.tags.mk-settings.signed-in-as%'.replace('{}', '<b>' + I.name + '</b>') }/></p> - <ul> - <li><a href="./settings/profile">%fa:user%%i18n:mobile.tags.mk-settings-page.profile%%fa:angle-right%</a></li> - <li><a href="./settings/authorized-apps">%fa:puzzle-piece%%i18n:mobile.tags.mk-settings-page.applications%%fa:angle-right%</a></li> - <li><a href="./settings/twitter">%fa:B twitter%%i18n:mobile.tags.mk-settings-page.twitter-integration%%fa:angle-right%</a></li> - <li><a href="./settings/signin-history">%fa:sign-in-alt%%i18n:mobile.tags.mk-settings-page.signin-history%%fa:angle-right%</a></li> - <li><a href="./settings/api">%fa:key%%i18n:mobile.tags.mk-settings-page.api%%fa:angle-right%</a></li> - </ul> - <ul> - <li><a onclick={ signout }>%fa:power-off%%i18n:mobile.tags.mk-settings-page.signout%</a></li> - </ul> - <p><small>ver { _VERSION_ } (葵 aoi)</small></p> - <style> - :scope - display block - - > p - display block - margin 24px - text-align center - color #cad2da - - > ul - $radius = 8px - - display block - margin 16px auto - padding 0 - max-width 500px - width calc(100% - 32px) - list-style none - background #fff - border solid 1px rgba(0, 0, 0, 0.2) - border-radius $radius - - > li - display block - border-bottom solid 1px #ddd - - &:hover - background rgba(0, 0, 0, 0.1) - - &:first-child - border-top-left-radius $radius - border-top-right-radius $radius - - &:last-child - border-bottom-left-radius $radius - border-bottom-right-radius $radius - border-bottom none - - > a - $height = 48px - - display block - position relative - padding 0 16px - line-height $height - color #4d635e - - > [data-fa]:nth-of-type(1) - margin-right 4px - - > [data-fa]:nth-of-type(2) - display block - position absolute - top 0 - right 8px - z-index 1 - padding 0 20px - font-size 1.2em - line-height $height - - </style> - <script> - import signout from '../../../common/scripts/signout'; - this.signout = signout; - - this.mixin('i'); - </script> -</mk-settings> diff --git a/src/web/app/mobile/tags/page/settings/api.tag b/src/web/app/mobile/tags/page/settings/api.tag deleted file mode 100644 index 8de0e96963..0000000000 --- a/src/web/app/mobile/tags/page/settings/api.tag +++ /dev/null @@ -1,36 +0,0 @@ -<mk-api-info-page> - <mk-ui ref="ui"> - <mk-api-info/> - </mk-ui> - <style> - :scope - display block - </style> - <script> - import ui from '../../../scripts/ui-event'; - - this.on('mount', () => { - document.title = 'Misskey | API'; - ui.trigger('title', '%fa:key%API'); - }); - </script> -</mk-api-info-page> - -<mk-api-info> - <p>Token:<code>{ I.token }</code></p> - <p>APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。</p> - <p>アカウントを乗っ取られてしまう可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。</p> - <p>万が一このトークンが漏れたりその可能性がある場合はデスクトップ版Misskeyから再生成できます。</p> - <style> - :scope - display block - color #4a535a - - code - padding 4px - background #eee - </style> - <script> - this.mixin('i'); - </script> -</mk-api-info> diff --git a/src/web/app/mobile/tags/page/settings/authorized-apps.tag b/src/web/app/mobile/tags/page/settings/authorized-apps.tag deleted file mode 100644 index 8d538eba5d..0000000000 --- a/src/web/app/mobile/tags/page/settings/authorized-apps.tag +++ /dev/null @@ -1,17 +0,0 @@ -<mk-authorized-apps-page> - <mk-ui ref="ui"> - <mk-authorized-apps/> - </mk-ui> - <style> - :scope - display block - </style> - <script> - import ui from '../../../scripts/ui-event'; - - this.on('mount', () => { - document.title = 'Misskey | %i18n:mobile.tags.mk-authorized-apps-page.application%'; - ui.trigger('title', '%fa:puzzle-piece%%i18n:mobile.tags.mk-authorized-apps-page.application%'); - }); - </script> -</mk-authorized-apps-page> diff --git a/src/web/app/mobile/tags/page/settings/profile.tag b/src/web/app/mobile/tags/page/settings/profile.tag deleted file mode 100644 index 8881e95190..0000000000 --- a/src/web/app/mobile/tags/page/settings/profile.tag +++ /dev/null @@ -1,247 +0,0 @@ -<mk-profile-setting-page> - <mk-ui ref="ui"> - <mk-profile-setting/> - </mk-ui> - <style> - :scope - display block - </style> - <script> - import ui from '../../../scripts/ui-event'; - - this.on('mount', () => { - document.title = 'Misskey | %i18n:mobile.tags.mk-profile-setting-page.title%'; - ui.trigger('title', '%fa:user%%i18n:mobile.tags.mk-profile-setting-page.title%'); - document.documentElement.style.background = '#313a42'; - }); - </script> -</mk-profile-setting-page> - -<mk-profile-setting> - <div> - <p>%fa:info-circle%%i18n:mobile.tags.mk-profile-setting.will-be-published%</p> - <div class="form"> - <div style={ I.banner_url ? 'background-image: url(' + I.banner_url + '?thumbnail&size=1024)' : '' } onclick={ clickBanner }> - <img src={ I.avatar_url + '?thumbnail&size=200' } alt="avatar" onclick={ clickAvatar }/> - </div> - <label> - <p>%i18n:mobile.tags.mk-profile-setting.name%</p> - <input ref="name" type="text" value={ I.name }/> - </label> - <label> - <p>%i18n:mobile.tags.mk-profile-setting.location%</p> - <input ref="location" type="text" value={ I.profile.location }/> - </label> - <label> - <p>%i18n:mobile.tags.mk-profile-setting.description%</p> - <textarea ref="description">{ I.description }</textarea> - </label> - <label> - <p>%i18n:mobile.tags.mk-profile-setting.birthday%</p> - <input ref="birthday" type="date" value={ I.profile.birthday }/> - </label> - <label> - <p>%i18n:mobile.tags.mk-profile-setting.avatar%</p> - <button onclick={ setAvatar } disabled={ avatarSaving }>%i18n:mobile.tags.mk-profile-setting.set-avatar%</button> - </label> - <label> - <p>%i18n:mobile.tags.mk-profile-setting.banner%</p> - <button onclick={ setBanner } disabled={ bannerSaving }>%i18n:mobile.tags.mk-profile-setting.set-banner%</button> - </label> - </div> - <button class="save" onclick={ save } disabled={ saving }>%fa:check%%i18n:mobile.tags.mk-profile-setting.save%</button> - </div> - <style> - :scope - display block - - > div - margin 8px auto - max-width 500px - width calc(100% - 16px) - - @media (min-width 500px) - margin 16px auto - width calc(100% - 32px) - - > p - display block - margin 0 0 8px 0 - padding 12px 16px - font-size 14px - color #79d4e6 - border solid 1px #71afbb - //color #276f86 - //background #f8ffff - //border solid 1px #a9d5de - border-radius 8px - - > [data-fa] - margin-right 6px - - > .form - position relative - background #fff - box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2) - border-radius 8px - - &:before - content "" - display block - position absolute - bottom -20px - left calc(50% - 10px) - border-top solid 10px rgba(0, 0, 0, 0.2) - border-right solid 10px transparent - border-bottom solid 10px transparent - border-left solid 10px transparent - - &:after - content "" - display block - position absolute - bottom -16px - left calc(50% - 8px) - border-top solid 8px #fff - border-right solid 8px transparent - border-bottom solid 8px transparent - border-left solid 8px transparent - - > div - height 128px - background-color #e4e4e4 - background-size cover - background-position center - border-radius 8px 8px 0 0 - - > img - position absolute - top 25px - left calc(50% - 40px) - width 80px - height 80px - border solid 2px #fff - border-radius 8px - - > label - display block - margin 0 - padding 16px - border-bottom solid 1px #eee - - &:last-of-type - border none - - > p:first-child - display block - margin 0 - padding 0 0 4px 0 - font-weight bold - color #2f3c42 - - > input[type="text"] - > textarea - display block - width 100% - padding 12px - font-size 16px - color #192427 - border solid 2px #ddd - border-radius 4px - - > textarea - min-height 80px - - > .save - display block - margin 8px 0 0 0 - padding 16px - width 100% - font-size 16px - color $theme-color-foreground - background $theme-color - border-radius 8px - - &:disabled - opacity 0.7 - - > [data-fa] - margin-right 4px - - </style> - <script> - this.mixin('i'); - this.mixin('api'); - - this.setAvatar = () => { - const i = riot.mount(document.body.appendChild(document.createElement('mk-drive-selector')), { - multiple: false - })[0]; - i.one('selected', file => { - this.update({ - avatarSaving: true - }); - - this.api('i/update', { - avatar_id: file.id - }).then(() => { - this.update({ - avatarSaving: false - }); - - alert('%i18n:mobile.tags.mk-profile-setting.avatar-saved%'); - }); - }); - }; - - this.setBanner = () => { - const i = riot.mount(document.body.appendChild(document.createElement('mk-drive-selector')), { - multiple: false - })[0]; - i.one('selected', file => { - this.update({ - bannerSaving: true - }); - - this.api('i/update', { - banner_id: file.id - }).then(() => { - this.update({ - bannerSaving: false - }); - - alert('%i18n:mobile.tags.mk-profile-setting.banner-saved%'); - }); - }); - }; - - this.clickAvatar = e => { - this.setAvatar(); - return false; - }; - - this.clickBanner = e => { - this.setBanner(); - return false; - }; - - this.save = () => { - this.update({ - saving: true - }); - - this.api('i/update', { - name: this.refs.name.value, - location: this.refs.location.value || null, - description: this.refs.description.value || null, - birthday: this.refs.birthday.value || null - }).then(() => { - this.update({ - saving: false - }); - - alert('%i18n:mobile.tags.mk-profile-setting.saved%'); - }); - }; - </script> -</mk-profile-setting> diff --git a/src/web/app/mobile/tags/page/settings/signin.tag b/src/web/app/mobile/tags/page/settings/signin.tag deleted file mode 100644 index 1a9e63886e..0000000000 --- a/src/web/app/mobile/tags/page/settings/signin.tag +++ /dev/null @@ -1,17 +0,0 @@ -<mk-signin-history-page> - <mk-ui ref="ui"> - <mk-signin-history/> - </mk-ui> - <style> - :scope - display block - </style> - <script> - import ui from '../../../scripts/ui-event'; - - this.on('mount', () => { - document.title = 'Misskey | %i18n:mobile.tags.mk-signin-history-page.signin-history%'; - ui.trigger('title', '%fa:sign-in-alt%%i18n:mobile.tags.mk-signin-history-page.signin-history%'); - }); - </script> -</mk-signin-history-page> diff --git a/src/web/app/mobile/tags/page/settings/twitter.tag b/src/web/app/mobile/tags/page/settings/twitter.tag deleted file mode 100644 index 02661d3b6b..0000000000 --- a/src/web/app/mobile/tags/page/settings/twitter.tag +++ /dev/null @@ -1,17 +0,0 @@ -<mk-twitter-setting-page> - <mk-ui ref="ui"> - <mk-twitter-setting/> - </mk-ui> - <style> - :scope - display block - </style> - <script> - import ui from '../../../scripts/ui-event'; - - this.on('mount', () => { - document.title = 'Misskey | %i18n:mobile.tags.mk-twitter-setting-page.twitter-integration%'; - ui.trigger('title', '%fa:B twitter%%i18n:mobile.tags.mk-twitter-setting-page.twitter-integration%'); - }); - </script> -</mk-twitter-setting-page> diff --git a/src/web/app/mobile/tags/page/user-followers.tag b/src/web/app/mobile/tags/page/user-followers.tag deleted file mode 100644 index cffb2b58c4..0000000000 --- a/src/web/app/mobile/tags/page/user-followers.tag +++ /dev/null @@ -1,40 +0,0 @@ -<mk-user-followers-page> - <mk-ui ref="ui"> - <mk-user-followers ref="list" if={ !parent.fetching } user={ parent.user }/> - </mk-ui> - <style> - :scope - display block - </style> - <script> - import ui from '../../scripts/ui-event'; - import Progress from '../../../common/scripts/loading'; - - this.mixin('api'); - - this.fetching = true; - this.user = null; - - this.on('mount', () => { - Progress.start(); - - this.api('users/show', { - username: this.opts.user - }).then(user => { - this.update({ - fetching: false, - user: user - }); - - document.title = '%i18n:mobile.tags.mk-user-followers-page.followers-of%'.replace('{}', user.name) + ' | Misskey'; - // TODO: ユーザー名をエスケープ - ui.trigger('title', '<img src="' + user.avatar_url + '?thumbnail&size=64">' + '%i18n:mobile.tags.mk-user-followers-page.followers-of%'.replace('{}', user.name)); - document.documentElement.style.background = '#313a42'; - - this.refs.ui.refs.list.on('loaded', () => { - Progress.done(); - }); - }); - }); - </script> -</mk-user-followers-page> diff --git a/src/web/app/mobile/tags/page/user-following.tag b/src/web/app/mobile/tags/page/user-following.tag deleted file mode 100644 index 369cb46422..0000000000 --- a/src/web/app/mobile/tags/page/user-following.tag +++ /dev/null @@ -1,40 +0,0 @@ -<mk-user-following-page> - <mk-ui ref="ui"> - <mk-user-following ref="list" if={ !parent.fetching } user={ parent.user }/> - </mk-ui> - <style> - :scope - display block - </style> - <script> - import ui from '../../scripts/ui-event'; - import Progress from '../../../common/scripts/loading'; - - this.mixin('api'); - - this.fetching = true; - this.user = null; - - this.on('mount', () => { - Progress.start(); - - this.api('users/show', { - username: this.opts.user - }).then(user => { - this.update({ - fetching: false, - user: user - }); - - document.title = '%i18n:mobile.tags.mk-user-following-page.following-of%'.replace('{}', user.name) + ' | Misskey'; - // TODO: ユーザー名をエスケープ - ui.trigger('title', '<img src="' + user.avatar_url + '?thumbnail&size=64">' + '%i18n:mobile.tags.mk-user-following-page.following-of%'.replace('{}', user.name)); - document.documentElement.style.background = '#313a42'; - - this.refs.ui.refs.list.on('loaded', () => { - Progress.done(); - }); - }); - }); - </script> -</mk-user-following-page> diff --git a/src/web/app/mobile/tags/page/user.tag b/src/web/app/mobile/tags/page/user.tag deleted file mode 100644 index 78ca534eb0..0000000000 --- a/src/web/app/mobile/tags/page/user.tag +++ /dev/null @@ -1,27 +0,0 @@ -<mk-user-page> - <mk-ui ref="ui"> - <mk-user ref="user" user={ parent.user } page={ parent.opts.page }/> - </mk-ui> - <style> - :scope - display block - </style> - <script> - import ui from '../../scripts/ui-event'; - import Progress from '../../../common/scripts/loading'; - - this.user = this.opts.user; - - this.on('mount', () => { - document.documentElement.style.background = '#313a42'; - Progress.start(); - - this.refs.ui.refs.user.on('loaded', user => { - Progress.done(); - document.title = user.name + ' | Misskey'; - // TODO: ユーザー名をエスケープ - ui.trigger('title', '%fa:user%' + user.name); - }); - }); - </script> -</mk-user-page> diff --git a/src/web/app/mobile/tags/post-detail.tag b/src/web/app/mobile/tags/post-detail.tag deleted file mode 100644 index 9f212a2496..0000000000 --- a/src/web/app/mobile/tags/post-detail.tag +++ /dev/null @@ -1,448 +0,0 @@ -<mk-post-detail> - <button class="read-more" if={ p.reply && p.reply.reply_id && context == null } onclick={ loadContext } disabled={ loadingContext }> - <virtual if={ !contextFetching }>%fa:ellipsis-v%</virtual> - <virtual if={ contextFetching }>%fa:spinner .pulse%</virtual> - </button> - <div class="context"> - <virtual each={ post in context }> - <mk-post-detail-sub post={ post }/> - </virtual> - </div> - <div class="reply-to" if={ p.reply }> - <mk-post-detail-sub post={ p.reply }/> - </div> - <div class="repost" if={ isRepost }> - <p> - <a class="avatar-anchor" href={ '/' + post.user.username }> - <img class="avatar" src={ post.user.avatar_url + '?thumbnail&size=32' } alt="avatar"/></a> - %fa:retweet%<a class="name" href={ '/' + post.user.username }> - { post.user.name } - </a> - がRepost - </p> - </div> - <article> - <header> - <a class="avatar-anchor" href={ '/' + p.user.username }> - <img class="avatar" src={ p.user.avatar_url + '?thumbnail&size=64' } alt="avatar"/> - </a> - <div> - <a class="name" href={ '/' + p.user.username }>{ p.user.name }</a> - <span class="username">@{ p.user.username }</span> - </div> - </header> - <div class="body"> - <div class="text" ref="text"></div> - <div class="media" if={ p.media }> - <virtual each={ file in p.media }><img src={ file.url + '?thumbnail&size=512' } alt={ file.name } title={ file.name }/></virtual> - </div> - <mk-poll if={ p.poll } post={ p }/> - </div> - <a class="time" href={ '/' + p.user.username + '/' + p.id }> - <mk-time time={ p.created_at } mode="detail"/> - </a> - <footer> - <mk-reactions-viewer post={ p }/> - <button onclick={ reply } title="%i18n:mobile.tags.mk-post-detail.reply%"> - %fa:reply%<p class="count" if={ p.replies_count > 0 }>{ p.replies_count }</p> - </button> - <button onclick={ repost } title="Repost"> - %fa:retweet%<p class="count" if={ p.repost_count > 0 }>{ p.repost_count }</p> - </button> - <button class={ reacted: p.my_reaction != null } onclick={ react } ref="reactButton" title="%i18n:mobile.tags.mk-post-detail.reaction%"> - %fa:plus%<p class="count" if={ p.reactions_count > 0 }>{ p.reactions_count }</p> - </button> - <button onclick={ menu } ref="menuButton"> - %fa:ellipsis-h% - </button> - </footer> - </article> - <div class="replies" if={ !compact }> - <virtual each={ post in replies }> - <mk-post-detail-sub post={ post }/> - </virtual> - </div> - <style> - :scope - display block - overflow hidden - margin 0 auto - padding 0 - width 100% - text-align left - background #fff - border-radius 8px - box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2) - - > .fetching - padding 64px 0 - - > .read-more - display block - margin 0 - padding 10px 0 - width 100% - font-size 1em - text-align center - color #999 - cursor pointer - background #fafafa - outline none - border none - border-bottom solid 1px #eef0f2 - border-radius 6px 6px 0 0 - box-shadow none - - &:hover - background #f6f6f6 - - &:active - background #f0f0f0 - - &:disabled - color #ccc - - > .context - > * - border-bottom 1px solid #eef0f2 - - > .repost - color #9dbb00 - background linear-gradient(to bottom, #edfde2 0%, #fff 100%) - - > p - margin 0 - padding 16px 32px - - .avatar-anchor - display inline-block - - .avatar - vertical-align bottom - min-width 28px - min-height 28px - max-width 28px - max-height 28px - margin 0 8px 0 0 - border-radius 6px - - [data-fa] - margin-right 4px - - .name - font-weight bold - - & + article - padding-top 8px - - > .reply-to - border-bottom 1px solid #eef0f2 - - > article - padding 14px 16px 9px 16px - - @media (min-width 500px) - padding 28px 32px 18px 32px - - &:after - content "" - display block - clear both - - &:hover - > .main > footer > button - color #888 - - > header - display flex - line-height 1.1 - - > .avatar-anchor - display block - padding 0 .5em 0 0 - - > .avatar - display block - width 54px - height 54px - margin 0 - border-radius 8px - vertical-align bottom - - @media (min-width 500px) - width 60px - height 60px - - > div - - > .name - display inline-block - margin .4em 0 - color #777 - font-size 16px - font-weight bold - text-align left - text-decoration none - - &:hover - text-decoration underline - - > .username - display block - text-align left - margin 0 - color #ccc - - > .body - padding 8px 0 - - > .text - cursor default - display block - margin 0 - padding 0 - overflow-wrap break-word - font-size 16px - color #717171 - - @media (min-width 500px) - font-size 24px - - > mk-url-preview - margin-top 8px - - > .media - > img - display block - max-width 100% - - > .time - font-size 16px - color #c0c0c0 - - > footer - font-size 1.2em - - > button - margin 0 - padding 8px - background transparent - border none - box-shadow none - font-size 1em - color #ddd - cursor pointer - - &:not(:last-child) - margin-right 28px - - &:hover - color #666 - - > .count - display inline - margin 0 0 0 8px - color #999 - - &.reacted - color $theme-color - - > .replies - > * - border-top 1px solid #eef0f2 - - </style> - <script> - import compile from '../../common/scripts/text-compiler'; - import getPostSummary from '../../../../common/get-post-summary.ts'; - import openPostForm from '../scripts/open-post-form'; - - this.mixin('api'); - - this.compact = this.opts.compact; - this.post = this.opts.post; - this.isRepost = this.post.repost != null; - this.p = this.isRepost ? this.post.repost : this.post; - this.p.reactions_count = this.p.reaction_counts ? Object.keys(this.p.reaction_counts).map(key => this.p.reaction_counts[key]).reduce((a, b) => a + b) : 0; - this.summary = getPostSummary(this.p); - - this.loadingContext = false; - this.context = null; - - this.on('mount', () => { - if (this.p.text) { - const tokens = this.p.ast; - - this.refs.text.innerHTML = compile(tokens); - - Array.from(this.refs.text.children).forEach(e => { - if (e.tagName == 'MK-URL') riot.mount(e); - }); - - // URLをプレビュー - tokens - .filter(t => (t.type == 'url' || t.type == 'link') && !t.silent) - .map(t => { - riot.mount(this.refs.text.appendChild(document.createElement('mk-url-preview')), { - url: t.url - }); - }); - } - - // Get replies - if (!this.compact) { - this.api('posts/replies', { - post_id: this.p.id, - limit: 8 - }).then(replies => { - this.update({ - replies: replies - }); - }); - } - }); - - this.reply = () => { - openPostForm({ - reply: this.p - }); - }; - - this.repost = () => { - const text = window.prompt(`「${this.summary}」をRepost`); - if (text == null) return; - this.api('posts/create', { - repost_id: this.p.id, - text: text == '' ? undefined : text - }); - }; - - this.react = () => { - riot.mount(document.body.appendChild(document.createElement('mk-reaction-picker')), { - source: this.refs.reactButton, - post: this.p, - compact: true - }); - }; - - this.menu = () => { - riot.mount(document.body.appendChild(document.createElement('mk-post-menu')), { - source: this.refs.menuButton, - post: this.p, - compact: true - }); - }; - - this.loadContext = () => { - this.contextFetching = true; - - // Fetch context - this.api('posts/context', { - post_id: this.p.reply_id - }).then(context => { - this.update({ - contextFetching: false, - context: context.reverse() - }); - }); - }; - </script> -</mk-post-detail> - -<mk-post-detail-sub> - <article> - <a class="avatar-anchor" href={ '/' + post.user.username }> - <img class="avatar" src={ post.user.avatar_url + '?thumbnail&size=64' } alt="avatar"/> - </a> - <div class="main"> - <header> - <a class="name" href={ '/' + post.user.username }>{ post.user.name }</a> - <span class="username">@{ post.user.username }</span> - <a class="time" href={ '/' + post.user.username + '/' + post.id }> - <mk-time time={ post.created_at }/> - </a> - </header> - <div class="body"> - <mk-sub-post-content class="text" post={ post }/> - </div> - </div> - </article> - <style> - :scope - display block - margin 0 - padding 8px - font-size 0.9em - background #fdfdfd - - @media (min-width 500px) - padding 12px - - > article - &:after - content "" - display block - clear both - - &:hover - > .main > footer > button - color #888 - - > .avatar-anchor - display block - float left - margin 0 12px 0 0 - - > .avatar - display block - width 48px - height 48px - margin 0 - border-radius 8px - vertical-align bottom - - > .main - float left - width calc(100% - 60px) - - > header - display flex - margin-bottom 4px - white-space nowrap - - > .name - display block - margin 0 .5em 0 0 - padding 0 - overflow hidden - color #607073 - font-size 1em - font-weight 700 - text-align left - text-decoration none - text-overflow ellipsis - - &:hover - text-decoration underline - - > .username - text-align left - margin 0 .5em 0 0 - color #d1d8da - - > .time - margin-left auto - color #b2b8bb - - > .body - - > .text - cursor default - margin 0 - padding 0 - font-size 1.1em - color #717171 - - </style> - <script>this.post = this.opts.post</script> -</mk-post-detail-sub> diff --git a/src/web/app/mobile/tags/post-form.tag b/src/web/app/mobile/tags/post-form.tag deleted file mode 100644 index 3ac7296f73..0000000000 --- a/src/web/app/mobile/tags/post-form.tag +++ /dev/null @@ -1,291 +0,0 @@ -<mk-post-form> - <header> - <button class="cancel" onclick={ cancel }>%fa:times%</button> - <div> - <span if={ refs.text } class="text-count { over: refs.text.value.length > 1000 }">{ 1000 - refs.text.value.length }</span> - <button class="submit" onclick={ post }>%i18n:mobile.tags.mk-post-form.submit%</button> - </div> - </header> - <div class="form"> - <mk-post-preview if={ opts.reply } post={ opts.reply }/> - <textarea ref="text" disabled={ wait } oninput={ update } onkeydown={ onkeydown } onpaste={ onpaste } placeholder={ opts.reply ? '%i18n:mobile.tags.mk-post-form.reply-placeholder%' : '%i18n:mobile.tags.mk-post-form.post-placeholder%' }></textarea> - <div class="attaches" if={ files.length != 0 }> - <ul class="files" ref="attaches"> - <li class="file" each={ files }> - <div class="img" style="background-image: url({ url + '?thumbnail&size=64' })" title={ name }></div> - </li> - <li class="add" if={ files.length < 4 } title="%i18n:mobile.tags.mk-post-form.attach-media-from-local%" onclick={ selectFile }>%fa:plus%</li> - </ul> - </div> - <mk-poll-editor if={ poll } ref="poll" ondestroy={ onPollDestroyed }/> - <mk-uploader ref="uploader"/> - <button ref="upload" onclick={ selectFile }>%fa:upload%</button> - <button ref="drive" onclick={ selectFileFromDrive }>%fa:cloud%</button> - <button class="kao" onclick={ kao }>%fa:R smile%</button> - <button class="poll" onclick={ addPoll }>%fa:chart-pie%</button> - <input ref="file" type="file" accept="image/*" multiple="multiple" onchange={ changeFile }/> - </div> - <style> - :scope - display block - max-width 500px - width calc(100% - 16px) - margin 8px auto - background #fff - border-radius 8px - box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2) - - @media (min-width 500px) - margin 16px auto - width calc(100% - 32px) - - > header - z-index 1 - height 50px - box-shadow 0 1px 0 0 rgba(0, 0, 0, 0.1) - - > .cancel - width 50px - line-height 50px - font-size 24px - color #555 - - > div - position absolute - top 0 - right 0 - - > .text-count - line-height 50px - color #657786 - - > .submit - margin 8px - padding 0 16px - line-height 34px - color $theme-color-foreground - background $theme-color - border-radius 4px - - &:disabled - opacity 0.7 - - > .form - max-width 500px - margin 0 auto - - > mk-post-preview - padding 16px - - > .attaches - - > .files - display block - margin 0 - padding 4px - list-style none - - &:after - content "" - display block - clear both - - > .file - display block - float left - margin 4px - padding 0 - cursor move - - &:hover > .remove - display block - - > .img - width 64px - height 64px - background-size cover - background-position center center - - > .remove - display none - position absolute - top -6px - right -6px - width 16px - height 16px - cursor pointer - - > .add - display block - float left - margin 4px - padding 0 - border dashed 2px rgba($theme-color, 0.2) - cursor pointer - - &:hover - border-color rgba($theme-color, 0.3) - - > [data-fa] - color rgba($theme-color, 0.4) - - > [data-fa] - display block - width 60px - height 60px - line-height 60px - text-align center - font-size 1.2em - color rgba($theme-color, 0.2) - - > mk-uploader - margin 8px 0 0 0 - padding 8px - - > [ref='file'] - display none - - > [ref='text'] - display block - padding 12px - margin 0 - width 100% - max-width 100% - min-width 100% - min-height 80px - font-size 16px - color #333 - border none - border-bottom solid 1px #ddd - border-radius 0 - - &:disabled - opacity 0.5 - - > [ref='upload'] - > [ref='drive'] - .kao - .poll - display inline-block - padding 0 - margin 0 - width 48px - height 48px - font-size 20px - color #657786 - background transparent - outline none - border none - border-radius 0 - box-shadow none - - </style> - <script> - import getKao from '../../common/scripts/get-kao'; - - this.mixin('api'); - - this.wait = false; - this.uploadings = []; - this.files = []; - this.poll = false; - - this.on('mount', () => { - this.refs.uploader.on('uploaded', file => { - this.addFile(file); - }); - - this.refs.uploader.on('change-uploads', uploads => { - this.trigger('change-uploading-files', uploads); - }); - - this.refs.text.focus(); - }); - - this.onkeydown = e => { - if ((e.which == 10 || e.which == 13) && (e.ctrlKey || e.metaKey)) this.post(); - }; - - this.onpaste = e => { - Array.from(e.clipboardData.items).forEach(item => { - if (item.kind == 'file') { - this.upload(item.getAsFile()); - } - }); - }; - - this.selectFile = () => { - this.refs.file.click(); - }; - - this.selectFileFromDrive = () => { - const i = riot.mount(document.body.appendChild(document.createElement('mk-drive-selector')), { - multiple: true - })[0]; - i.one('selected', files => { - files.forEach(this.addFile); - }); - }; - - this.changeFile = () => { - Array.from(this.refs.file.files).forEach(this.upload); - }; - - this.upload = file => { - this.refs.uploader.upload(file); - }; - - this.addFile = file => { - file._remove = () => { - this.files = this.files.filter(x => x.id != file.id); - this.trigger('change-files', this.files); - this.update(); - }; - - this.files.push(file); - this.trigger('change-files', this.files); - this.update(); - }; - - this.addPoll = () => { - this.poll = true; - }; - - this.onPollDestroyed = () => { - this.update({ - poll: false - }); - }; - - this.post = () => { - this.wait = true; - - const files = this.files && this.files.length > 0 - ? this.files.map(f => f.id) - : undefined; - - this.api('posts/create', { - text: this.refs.text.value == '' ? undefined : this.refs.text.value, - media_ids: files, - reply_id: opts.reply ? opts.reply.id : undefined, - poll: this.poll ? this.refs.poll.get() : undefined - }).then(data => { - this.trigger('post'); - this.unmount(); - }).catch(err => { - this.update({ - wait: false - }); - }); - }; - - this.cancel = () => { - this.trigger('cancel'); - this.unmount(); - }; - - this.kao = () => { - this.refs.text.value += getKao(); - }; - </script> -</mk-post-form> diff --git a/src/web/app/mobile/tags/post-preview.tag b/src/web/app/mobile/tags/post-preview.tag deleted file mode 100644 index aaf8467039..0000000000 --- a/src/web/app/mobile/tags/post-preview.tag +++ /dev/null @@ -1,94 +0,0 @@ -<mk-post-preview> - <article> - <a class="avatar-anchor" href={ '/' + post.user.username }> - <img class="avatar" src={ post.user.avatar_url + '?thumbnail&size=64' } alt="avatar"/> - </a> - <div class="main"> - <header> - <a class="name" href={ '/' + post.user.username }>{ post.user.name }</a> - <span class="username">@{ post.user.username }</span> - <a class="time" href={ '/' + post.user.username + '/' + post.id }> - <mk-time time={ post.created_at }/> - </a> - </header> - <div class="body"> - <mk-sub-post-content class="text" post={ post }/> - </div> - </div> - </article> - <style> - :scope - display block - margin 0 - padding 0 - font-size 0.9em - background #fff - - > article - &:after - content "" - display block - clear both - - &:hover - > .main > footer > button - color #888 - - > .avatar-anchor - display block - float left - margin 0 12px 0 0 - - > .avatar - display block - width 48px - height 48px - margin 0 - border-radius 8px - vertical-align bottom - - > .main - float left - width calc(100% - 60px) - - > header - display flex - margin-bottom 4px - white-space nowrap - - > .name - display block - margin 0 .5em 0 0 - padding 0 - overflow hidden - color #607073 - font-size 1em - font-weight 700 - text-align left - text-decoration none - text-overflow ellipsis - - &:hover - text-decoration underline - - > .username - text-align left - margin 0 .5em 0 0 - color #d1d8da - - > .time - margin-left auto - color #b2b8bb - - > .body - - > .text - cursor default - margin 0 - padding 0 - font-size 1.1em - color #717171 - - </style> - <script>this.post = this.opts.post</script> -</mk-post-preview> diff --git a/src/web/app/mobile/tags/search-posts.tag b/src/web/app/mobile/tags/search-posts.tag deleted file mode 100644 index 967764bc2c..0000000000 --- a/src/web/app/mobile/tags/search-posts.tag +++ /dev/null @@ -1,44 +0,0 @@ -<mk-search-posts> - <mk-timeline init={ init } more={ more } empty={ '%i18n:mobile.tags.mk-search-posts.empty%'.replace('{}', query) }/> - <style> - :scope - display block - margin 8px auto - max-width 500px - width calc(100% - 16px) - background #fff - border-radius 8px - box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2) - - @media (min-width 500px) - margin 16px auto - width calc(100% - 32px) - </style> - <script> - this.mixin('api'); - - this.max = 30; - this.offset = 0; - - this.query = this.opts.query; - this.withMedia = this.opts.withMedia; - - this.init = new Promise((res, rej) => { - this.api('posts/search', { - query: this.query - }).then(posts => { - res(posts); - this.trigger('loaded'); - }); - }); - - this.more = () => { - this.offset += this.max; - return this.api('posts/search', { - query: this.query, - max: this.max, - offset: this.offset - }); - }; - </script> -</mk-search-posts> diff --git a/src/web/app/mobile/tags/search.tag b/src/web/app/mobile/tags/search.tag deleted file mode 100644 index 2d299e0a77..0000000000 --- a/src/web/app/mobile/tags/search.tag +++ /dev/null @@ -1,16 +0,0 @@ -<mk-search> - <mk-search-posts ref="posts" query={ query }/> - <style> - :scope - display block - </style> - <script> - this.query = this.opts.query; - - this.on('mount', () => { - this.refs.posts.on('loaded', () => { - this.trigger('loaded'); - }); - }); - </script> -</mk-search> diff --git a/src/web/app/mobile/tags/sub-post-content.tag b/src/web/app/mobile/tags/sub-post-content.tag deleted file mode 100644 index 9436b6c1d7..0000000000 --- a/src/web/app/mobile/tags/sub-post-content.tag +++ /dev/null @@ -1,46 +0,0 @@ -<mk-sub-post-content> - <div class="body"><a class="reply" if={ post.reply_id }>%fa:reply%</a><span ref="text"></span><a class="quote" if={ post.repost_id } href={ '/post:' + post.repost_id }>RP: ...</a></div> - <details if={ post.media }> - <summary>({ post.media.length }個のメディア)</summary> - <mk-images-viewer images={ post.media }/> - </details> - <details if={ post.poll }> - <summary>%i18n:mobile.tags.mk-sub-post-content.poll%</summary> - <mk-poll post={ post }/> - </details> - <style> - :scope - display block - overflow-wrap break-word - - > .body - > .reply - margin-right 6px - color #717171 - - > .quote - margin-left 4px - font-style oblique - color #a0bf46 - - mk-poll - font-size 80% - - </style> - <script> - import compile from '../../common/scripts/text-compiler'; - - this.post = this.opts.post; - - this.on('mount', () => { - if (this.post.text) { - const tokens = this.post.ast; - this.refs.text.innerHTML = compile(tokens, false); - - Array.from(this.refs.text.children).forEach(e => { - if (e.tagName == 'MK-URL') riot.mount(e); - }); - } - }); - </script> -</mk-sub-post-content> diff --git a/src/web/app/mobile/tags/timeline.tag b/src/web/app/mobile/tags/timeline.tag deleted file mode 100644 index 19f90a1c11..0000000000 --- a/src/web/app/mobile/tags/timeline.tag +++ /dev/null @@ -1,688 +0,0 @@ -<mk-timeline> - <div class="init" if={ init }> - %fa:spinner .pulse%%i18n:common.loading% - </div> - <div class="empty" if={ !init && posts.length == 0 }> - %fa:R comments%{ opts.empty || '%i18n:mobile.tags.mk-timeline.empty%' } - </div> - <virtual each={ post, i in posts }> - <mk-timeline-post post={ post }/> - <p class="date" if={ i != posts.length - 1 && post._date != posts[i + 1]._date }> - <span>%fa:angle-up%{ post._datetext }</span> - <span>%fa:angle-down%{ posts[i + 1]._datetext }</span> - </p> - </virtual> - <footer if={ !init }> - <button if={ canFetchMore } onclick={ more } disabled={ fetching }> - <span if={ !fetching }>%i18n:mobile.tags.mk-timeline.load-more%</span> - <span if={ fetching }>%i18n:common.loading%<mk-ellipsis/></span> - </button> - </footer> - <style> - :scope - display block - background #fff - border-radius 8px - box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2) - - > .init - padding 64px 0 - text-align center - color #999 - - > [data-fa] - margin-right 4px - - > .empty - margin 0 auto - padding 32px - max-width 400px - text-align center - color #999 - - > [data-fa] - display block - margin-bottom 16px - font-size 3em - color #ccc - - > .date - display block - margin 0 - line-height 32px - text-align center - font-size 0.9em - color #aaa - background #fdfdfd - border-bottom solid 1px #eaeaea - - span - margin 0 16px - - [data-fa] - margin-right 8px - - > footer - text-align center - border-top solid 1px #eaeaea - border-bottom-left-radius 4px - border-bottom-right-radius 4px - - > button - margin 0 - padding 16px - width 100% - color $theme-color - border-radius 0 0 8px 8px - - &:disabled - opacity 0.7 - - </style> - <script> - this.posts = []; - this.init = true; - this.fetching = false; - this.canFetchMore = true; - - this.on('mount', () => { - this.opts.init.then(posts => { - this.init = false; - this.setPosts(posts); - }); - }); - - this.on('update', () => { - this.posts.forEach(post => { - const date = new Date(post.created_at).getDate(); - const month = new Date(post.created_at).getMonth() + 1; - post._date = date; - post._datetext = `${month}月 ${date}日`; - }); - }); - - this.more = () => { - if (this.init || this.fetching || this.posts.length == 0) return; - this.update({ - fetching: true - }); - this.opts.more().then(posts => { - this.fetching = false; - this.prependPosts(posts); - }); - }; - - this.setPosts = posts => { - this.update({ - posts: posts - }); - }; - - this.prependPosts = posts => { - posts.forEach(post => { - this.posts.push(post); - this.update(); - }); - } - - this.addPost = post => { - this.posts.unshift(post); - this.update(); - }; - - this.tail = () => { - return this.posts[this.posts.length - 1]; - }; - </script> -</mk-timeline> - -<mk-timeline-post class={ repost: isRepost }> - <div class="reply-to" if={ p.reply }> - <mk-timeline-post-sub post={ p.reply }/> - </div> - <div class="repost" if={ isRepost }> - <p> - <a class="avatar-anchor" href={ '/' + post.user.username }> - <img class="avatar" src={ post.user.avatar_url + '?thumbnail&size=64' } alt="avatar"/> - </a> - %fa:retweet%{'%i18n:mobile.tags.mk-timeline-post.reposted-by%'.substr(0, '%i18n:mobile.tags.mk-timeline-post.reposted-by%'.indexOf('{'))}<a class="name" href={ '/' + post.user.username }>{ post.user.name }</a>{'%i18n:mobile.tags.mk-timeline-post.reposted-by%'.substr('%i18n:mobile.tags.mk-timeline-post.reposted-by%'.indexOf('}') + 1)} - </p> - <mk-time time={ post.created_at }/> - </div> - <article> - <a class="avatar-anchor" href={ '/' + p.user.username }> - <img class="avatar" src={ p.user.avatar_url + '?thumbnail&size=96' } alt="avatar"/> - </a> - <div class="main"> - <header> - <a class="name" href={ '/' + p.user.username }>{ p.user.name }</a> - <span class="is-bot" if={ p.user.is_bot }>bot</span> - <span class="username">@{ p.user.username }</span> - <a class="created-at" href={ url }> - <mk-time time={ p.created_at }/> - </a> - </header> - <div class="body"> - <div class="text" ref="text"> - <p class="channel" if={ p.channel != null }><a href={ _CH_URL_ + '/' + p.channel.id } target="_blank">{ p.channel.title }</a>:</p> - <a class="reply" if={ p.reply }> - %fa:reply% - </a> - <p class="dummy"></p> - <a class="quote" if={ p.repost != null }>RP:</a> - </div> - <div class="media" if={ p.media }> - <mk-images-viewer images={ p.media }/> - </div> - <mk-poll if={ p.poll } post={ p } ref="pollViewer"/> - <span class="app" if={ p.app }>via <b>{ p.app.name }</b></span> - <div class="repost" if={ p.repost }>%fa:quote-right -flip-h% - <mk-post-preview class="repost" post={ p.repost }/> - </div> - </div> - <footer> - <mk-reactions-viewer post={ p } ref="reactionsViewer"/> - <button onclick={ reply }> - %fa:reply%<p class="count" if={ p.replies_count > 0 }>{ p.replies_count }</p> - </button> - <button onclick={ repost } title="Repost"> - %fa:retweet%<p class="count" if={ p.repost_count > 0 }>{ p.repost_count }</p> - </button> - <button class={ reacted: p.my_reaction != null } onclick={ react } ref="reactButton"> - %fa:plus%<p class="count" if={ p.reactions_count > 0 }>{ p.reactions_count }</p> - </button> - <button class="menu" onclick={ menu } ref="menuButton"> - %fa:ellipsis-h% - </button> - </footer> - </div> - </article> - <style> - :scope - display block - margin 0 - padding 0 - font-size 12px - border-bottom solid 1px #eaeaea - - &:first-child - border-radius 8px 8px 0 0 - - > .repost - border-radius 8px 8px 0 0 - - &:last-of-type - border-bottom none - - @media (min-width 350px) - font-size 14px - - @media (min-width 500px) - font-size 16px - - > .repost - color #9dbb00 - background linear-gradient(to bottom, #edfde2 0%, #fff 100%) - - > p - margin 0 - padding 8px 16px - line-height 28px - - @media (min-width 500px) - padding 16px - - .avatar-anchor - display inline-block - - .avatar - vertical-align bottom - width 28px - height 28px - margin 0 8px 0 0 - border-radius 6px - - [data-fa] - margin-right 4px - - .name - font-weight bold - - > mk-time - position absolute - top 8px - right 16px - font-size 0.9em - line-height 28px - - @media (min-width 500px) - top 16px - - & + article - padding-top 8px - - > .reply-to - background rgba(0, 0, 0, 0.0125) - - > mk-post-preview - background transparent - - > article - padding 14px 16px 9px 16px - - &:after - content "" - display block - clear both - - > .avatar-anchor - display block - float left - margin 0 10px 8px 0 - position -webkit-sticky - position sticky - top 62px - - @media (min-width 500px) - margin-right 16px - - > .avatar - display block - width 48px - height 48px - margin 0 - border-radius 6px - vertical-align bottom - - @media (min-width 500px) - width 58px - height 58px - border-radius 8px - - > .main - float left - width calc(100% - 58px) - - @media (min-width 500px) - width calc(100% - 74px) - - > header - display flex - white-space nowrap - - @media (min-width 500px) - margin-bottom 2px - - > .name - display block - margin 0 0.5em 0 0 - padding 0 - overflow hidden - color #777 - font-size 1em - font-weight 700 - text-align left - text-decoration none - text-overflow ellipsis - - &:hover - text-decoration underline - - > .is-bot - text-align left - margin 0 0.5em 0 0 - padding 1px 6px - font-size 12px - color #aaa - border solid 1px #ddd - border-radius 3px - - > .username - text-align left - margin 0 0.5em 0 0 - color #ccc - - > .created-at - margin-left auto - font-size 0.9em - color #c0c0c0 - - > .body - - > .text - cursor default - display block - margin 0 - padding 0 - overflow-wrap break-word - font-size 1.1em - color #717171 - - > .dummy - display none - - mk-url-preview - margin-top 8px - - > .channel - margin 0 - - > .reply - margin-right 8px - color #717171 - - > .quote - margin-left 4px - font-style oblique - color #a0bf46 - - code - padding 4px 8px - margin 0 0.5em - font-size 80% - color #525252 - background #f8f8f8 - border-radius 2px - - pre > code - padding 16px - margin 0 - - [data-is-me]:after - content "you" - padding 0 4px - margin-left 4px - font-size 80% - color $theme-color-foreground - background $theme-color - border-radius 4px - - > .media - > img - display block - max-width 100% - - > .app - font-size 12px - color #ccc - - > mk-poll - font-size 80% - - > .repost - margin 8px 0 - - > [data-fa]:first-child - position absolute - top -8px - left -8px - z-index 1 - color #c0dac6 - font-size 28px - background #fff - - > mk-post-preview - padding 16px - border dashed 1px #c0dac6 - border-radius 8px - - > footer - > button - margin 0 - padding 8px - background transparent - border none - box-shadow none - font-size 1em - color #ddd - cursor pointer - - &:not(:last-child) - margin-right 28px - - &:hover - color #666 - - > .count - display inline - margin 0 0 0 8px - color #999 - - &.reacted - color $theme-color - - &.menu - @media (max-width 350px) - display none - - </style> - <script> - import compile from '../../common/scripts/text-compiler'; - import getPostSummary from '../../../../common/get-post-summary.ts'; - import openPostForm from '../scripts/open-post-form'; - - this.mixin('i'); - this.mixin('api'); - - this.mixin('stream'); - this.connection = this.stream.getConnection(); - this.connectionId = this.stream.use(); - - this.set = post => { - this.post = post; - this.isRepost = this.post.repost != null && this.post.text == null; - this.p = this.isRepost ? this.post.repost : this.post; - this.p.reactions_count = this.p.reaction_counts ? Object.keys(this.p.reaction_counts).map(key => this.p.reaction_counts[key]).reduce((a, b) => a + b) : 0; - this.summary = getPostSummary(this.p); - this.url = `/${this.p.user.username}/${this.p.id}`; - }; - - this.set(this.opts.post); - - this.refresh = post => { - this.set(post); - this.update(); - if (this.refs.reactionsViewer) this.refs.reactionsViewer.update({ - post - }); - if (this.refs.pollViewer) this.refs.pollViewer.init(post); - }; - - this.onStreamPostUpdated = data => { - const post = data.post; - if (post.id == this.post.id) { - this.refresh(post); - } - }; - - this.onStreamConnected = () => { - this.capture(); - }; - - this.capture = withHandler => { - if (this.SIGNIN) { - this.connection.send({ - type: 'capture', - id: this.post.id - }); - if (withHandler) this.connection.on('post-updated', this.onStreamPostUpdated); - } - }; - - this.decapture = withHandler => { - if (this.SIGNIN) { - this.connection.send({ - type: 'decapture', - id: this.post.id - }); - if (withHandler) this.connection.off('post-updated', this.onStreamPostUpdated); - } - }; - - this.on('mount', () => { - this.capture(true); - - if (this.SIGNIN) { - this.connection.on('_connected_', this.onStreamConnected); - } - - if (this.p.text) { - const tokens = this.p.ast; - - this.refs.text.innerHTML = this.refs.text.innerHTML.replace('<p class="dummy"></p>', compile(tokens)); - - Array.from(this.refs.text.children).forEach(e => { - if (e.tagName == 'MK-URL') riot.mount(e); - }); - - // URLをプレビュー - tokens - .filter(t => (t.type == 'url' || t.type == 'link') && !t.silent) - .map(t => { - riot.mount(this.refs.text.appendChild(document.createElement('mk-url-preview')), { - url: t.url - }); - }); - } - }); - - this.on('unmount', () => { - this.decapture(true); - this.connection.off('_connected_', this.onStreamConnected); - this.stream.dispose(this.connectionId); - }); - - this.reply = () => { - openPostForm({ - reply: this.p - }); - }; - - this.repost = () => { - const text = window.prompt(`「${this.summary}」をRepost`); - if (text == null) return; - this.api('posts/create', { - repost_id: this.p.id, - text: text == '' ? undefined : text - }); - }; - - this.react = () => { - riot.mount(document.body.appendChild(document.createElement('mk-reaction-picker')), { - source: this.refs.reactButton, - post: this.p, - compact: true - }); - }; - - this.menu = () => { - riot.mount(document.body.appendChild(document.createElement('mk-post-menu')), { - source: this.refs.menuButton, - post: this.p, - compact: true - }); - }; - </script> -</mk-timeline-post> - -<mk-timeline-post-sub> - <article><a class="avatar-anchor" href={ '/' + post.user.username }><img class="avatar" src={ post.user.avatar_url + '?thumbnail&size=96' } alt="avatar"/></a> - <div class="main"> - <header><a class="name" href={ '/' + post.user.username }>{ post.user.name }</a><span class="username">@{ post.user.username }</span><a class="created-at" href={ '/' + post.user.username + '/' + post.id }> - <mk-time time={ post.created_at }/></a></header> - <div class="body"> - <mk-sub-post-content class="text" post={ post }/> - </div> - </div> - </article> - <style> - :scope - display block - margin 0 - padding 0 - font-size 0.9em - - > article - padding 16px - - &:after - content "" - display block - clear both - - &:hover - > .main > footer > button - color #888 - - > .avatar-anchor - display block - float left - margin 0 10px 0 0 - - @media (min-width 500px) - margin-right 16px - - > .avatar - display block - width 44px - height 44px - margin 0 - border-radius 8px - vertical-align bottom - - @media (min-width 500px) - width 52px - height 52px - - > .main - float left - width calc(100% - 54px) - - @media (min-width 500px) - width calc(100% - 68px) - - > header - display flex - margin-bottom 2px - white-space nowrap - - > .name - display block - margin 0 0.5em 0 0 - padding 0 - overflow hidden - color #607073 - font-size 1em - font-weight 700 - text-align left - text-decoration none - text-overflow ellipsis - - &:hover - text-decoration underline - - > .username - text-align left - margin 0 - color #d1d8da - - > .created-at - margin-left auto - color #b2b8bb - - > .body - - > .text - cursor default - margin 0 - padding 0 - font-size 1.1em - color #717171 - - pre - max-height 120px - font-size 80% - - </style> - <script>this.post = this.opts.post</script> -</mk-timeline-post-sub> diff --git a/src/web/app/mobile/tags/ui.tag b/src/web/app/mobile/tags/ui.tag deleted file mode 100644 index 62e128489a..0000000000 --- a/src/web/app/mobile/tags/ui.tag +++ /dev/null @@ -1,417 +0,0 @@ -<mk-ui> - <mk-ui-header/> - <mk-ui-nav ref="nav"/> - <div class="content"> - <yield /> - </div> - <mk-stream-indicator if={ SIGNIN }/> - <style> - :scope - display block - padding-top 48px - </style> - <script> - this.mixin('i'); - - this.mixin('stream'); - this.connection = this.stream.getConnection(); - this.connectionId = this.stream.use(); - - this.isDrawerOpening = false; - - this.on('mount', () => { - this.connection.on('notification', this.onStreamNotification); - }); - - this.on('unmount', () => { - this.connection.off('notification', this.onStreamNotification); - this.stream.dispose(this.connectionId); - }); - - this.toggleDrawer = () => { - this.isDrawerOpening = !this.isDrawerOpening; - this.refs.nav.root.style.display = this.isDrawerOpening ? 'block' : 'none'; - }; - - this.onStreamNotification = notification => { - // TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない - this.connection.send({ - type: 'read_notification', - id: notification.id - }); - - riot.mount(document.body.appendChild(document.createElement('mk-notify')), { - notification: notification - }); - }; - </script> -</mk-ui> - -<mk-ui-header> - <mk-special-message/> - <div class="main"> - <div class="backdrop"></div> - <div class="content"> - <button class="nav" onclick={ parent.toggleDrawer }>%fa:bars%</button> - <virtual if={ hasUnreadNotifications || hasUnreadMessagingMessages }>%fa:circle%</virtual> - <h1 ref="title">Misskey</h1> - <button if={ func } onclick={ func }><mk-raw content={ funcIcon }/></button> - </div> - </div> - <style> - :scope - $height = 48px - - display block - position fixed - top 0 - z-index 1024 - width 100% - box-shadow 0 1px 0 rgba(#000, 0.075) - - > .main - color rgba(#fff, 0.9) - - > .backdrop - position absolute - top 0 - z-index 1023 - width 100% - height $height - -webkit-backdrop-filter blur(12px) - backdrop-filter blur(12px) - background-color rgba(#1b2023, 0.75) - - > .content - z-index 1024 - - > h1 - display block - margin 0 auto - padding 0 - width 100% - max-width calc(100% - 112px) - text-align center - font-size 1.1em - font-weight normal - line-height $height - white-space nowrap - overflow hidden - text-overflow ellipsis - - [data-fa] - margin-right 8px - - > img - display inline-block - vertical-align bottom - width ($height - 16px) - height ($height - 16px) - margin 8px - border-radius 6px - - > .nav - display block - position absolute - top 0 - left 0 - width $height - font-size 1.4em - line-height $height - border-right solid 1px rgba(#000, 0.1) - - > [data-fa] - transition all 0.2s ease - - > [data-fa].circle - position absolute - top 8px - left 8px - pointer-events none - font-size 10px - color $theme-color - - > button:last-child - display block - position absolute - top 0 - right 0 - width $height - text-align center - font-size 1.4em - color inherit - line-height $height - border-left solid 1px rgba(#000, 0.1) - - </style> - <script> - import ui from '../scripts/ui-event'; - - this.mixin('api'); - - this.mixin('stream'); - this.connection = this.stream.getConnection(); - this.connectionId = this.stream.use(); - - this.func = null; - this.funcIcon = null; - - this.on('mount', () => { - this.connection.on('read_all_notifications', this.onReadAllNotifications); - this.connection.on('read_all_messaging_messages', this.onReadAllMessagingMessages); - this.connection.on('unread_messaging_message', this.onUnreadMessagingMessage); - - // Fetch count of unread notifications - this.api('notifications/get_unread_count').then(res => { - if (res.count > 0) { - this.update({ - hasUnreadNotifications: true - }); - } - }); - - // Fetch count of unread messaging messages - this.api('messaging/unread').then(res => { - if (res.count > 0) { - this.update({ - hasUnreadMessagingMessages: true - }); - } - }); - }); - - this.on('unmount', () => { - this.connection.off('read_all_notifications', this.onReadAllNotifications); - this.connection.off('read_all_messaging_messages', this.onReadAllMessagingMessages); - this.connection.off('unread_messaging_message', this.onUnreadMessagingMessage); - this.stream.dispose(this.connectionId); - - ui.off('title', this.setTitle); - ui.off('func', this.setFunc); - }); - - this.onReadAllNotifications = () => { - this.update({ - hasUnreadNotifications: false - }); - }; - - this.onReadAllMessagingMessages = () => { - this.update({ - hasUnreadMessagingMessages: false - }); - }; - - this.onUnreadMessagingMessage = () => { - this.update({ - hasUnreadMessagingMessages: true - }); - }; - - this.setTitle = title => { - this.refs.title.innerHTML = title; - }; - - this.setFunc = (fn, icon) => { - this.update({ - func: fn, - funcIcon: icon - }); - }; - - ui.on('title', this.setTitle); - ui.on('func', this.setFunc); - </script> -</mk-ui-header> - -<mk-ui-nav> - <div class="backdrop" onclick={ parent.toggleDrawer }></div> - <div class="body"> - <a class="me" if={ SIGNIN } href={ '/' + I.username }> - <img class="avatar" src={ I.avatar_url + '?thumbnail&size=128' } alt="avatar"/> - <p class="name">{ I.name }</p> - </a> - <div class="links"> - <ul> - <li><a href="/">%fa:home%%i18n:mobile.tags.mk-ui-nav.home%%fa:angle-right%</a></li> - <li><a href="/i/notifications">%fa:R bell%%i18n:mobile.tags.mk-ui-nav.notifications%<virtual if={ hasUnreadNotifications }>%fa:circle%</virtual>%fa:angle-right%</a></li> - <li><a href="/i/messaging">%fa:R comments%%i18n:mobile.tags.mk-ui-nav.messaging%<virtual if={ hasUnreadMessagingMessages }>%fa:circle%</virtual>%fa:angle-right%</a></li> - </ul> - <ul> - <li><a href={ _CH_URL_ } target="_blank">%fa:tv%%i18n:mobile.tags.mk-ui-nav.ch%%fa:angle-right%</a></li> - <li><a href="/i/drive">%fa:cloud%%i18n:mobile.tags.mk-ui-nav.drive%%fa:angle-right%</a></li> - </ul> - <ul> - <li><a onclick={ search }>%fa:search%%i18n:mobile.tags.mk-ui-nav.search%%fa:angle-right%</a></li> - </ul> - <ul> - <li><a href="/i/settings">%fa:cog%%i18n:mobile.tags.mk-ui-nav.settings%%fa:angle-right%</a></li> - </ul> - </div> - <a href={ _ABOUT_URL_ }><p class="about">%i18n:mobile.tags.mk-ui-nav.about%</p></a> - </div> - <style> - :scope - display none - - .backdrop - position fixed - top 0 - left 0 - z-index 1025 - width 100% - height 100% - background rgba(0, 0, 0, 0.2) - - .body - position fixed - top 0 - left 0 - z-index 1026 - width 240px - height 100% - overflow auto - -webkit-overflow-scrolling touch - color #777 - background #fff - - .me - display block - margin 0 - padding 16px - - .avatar - display inline - max-width 64px - border-radius 32px - vertical-align middle - - .name - display block - margin 0 16px - position absolute - top 0 - left 80px - padding 0 - width calc(100% - 112px) - color #777 - line-height 96px - overflow hidden - text-overflow ellipsis - white-space nowrap - - ul - display block - margin 16px 0 - padding 0 - list-style none - - &:first-child - margin-top 0 - - li - display block - font-size 1em - line-height 1em - - a - display block - padding 0 20px - line-height 3rem - line-height calc(1rem + 30px) - color #777 - text-decoration none - - > [data-fa]:first-child - margin-right 0.5em - - > [data-fa].circle - margin-left 6px - font-size 10px - color $theme-color - - > [data-fa]:last-child - position absolute - top 0 - right 0 - padding 0 20px - font-size 1.2em - line-height calc(1rem + 30px) - color #ccc - - .about - margin 0 - padding 1em 0 - text-align center - font-size 0.8em - opacity 0.5 - - a - color #777 - - </style> - <script> - this.mixin('i'); - this.mixin('page'); - this.mixin('api'); - - this.mixin('stream'); - this.connection = this.stream.getConnection(); - this.connectionId = this.stream.use(); - - this.on('mount', () => { - this.connection.on('read_all_notifications', this.onReadAllNotifications); - this.connection.on('read_all_messaging_messages', this.onReadAllMessagingMessages); - this.connection.on('unread_messaging_message', this.onUnreadMessagingMessage); - - // Fetch count of unread notifications - this.api('notifications/get_unread_count').then(res => { - if (res.count > 0) { - this.update({ - hasUnreadNotifications: true - }); - } - }); - - // Fetch count of unread messaging messages - this.api('messaging/unread').then(res => { - if (res.count > 0) { - this.update({ - hasUnreadMessagingMessages: true - }); - } - }); - }); - - this.on('unmount', () => { - this.connection.off('read_all_notifications', this.onReadAllNotifications); - this.connection.off('read_all_messaging_messages', this.onReadAllMessagingMessages); - this.connection.off('unread_messaging_message', this.onUnreadMessagingMessage); - this.stream.dispose(this.connectionId); - }); - - this.onReadAllNotifications = () => { - this.update({ - hasUnreadNotifications: false - }); - }; - - this.onReadAllMessagingMessages = () => { - this.update({ - hasUnreadMessagingMessages: false - }); - }; - - this.onUnreadMessagingMessage = () => { - this.update({ - hasUnreadMessagingMessages: true - }); - }; - - this.search = () => { - const query = window.prompt('%i18n:mobile.tags.mk-ui-nav.search%'); - if (query == null || query == '') return; - this.page('/search:' + query); - }; - </script> -</mk-ui-nav> diff --git a/src/web/app/mobile/tags/user-card.tag b/src/web/app/mobile/tags/user-card.tag deleted file mode 100644 index d0c79698c5..0000000000 --- a/src/web/app/mobile/tags/user-card.tag +++ /dev/null @@ -1,55 +0,0 @@ -<mk-user-card> - <header style={ user.banner_url ? 'background-image: url(' + user.banner_url + '?thumbnail&size=1024)' : '' }> - <a href={ '/' + user.username }> - <img src={ user.avatar_url + '?thumbnail&size=200' } alt="avatar"/> - </a> - </header> - <a class="name" href={ '/' + user.username } target="_blank">{ user.name }</a> - <p class="username">@{ user.username }</p> - <mk-follow-button user={ user }/> - <style> - :scope - display inline-block - width 200px - text-align center - border-radius 8px - background #fff - - > header - display block - height 80px - background-color #ddd - background-size cover - background-position center - border-radius 8px 8px 0 0 - - > a - > img - position absolute - top 20px - left calc(50% - 40px) - width 80px - height 80px - border solid 2px #fff - border-radius 8px - - > .name - display block - margin 24px 0 0 0 - font-size 16px - color #555 - - > .username - margin 0 - font-size 15px - color #ccc - - > mk-follow-button - display inline-block - margin 8px 0 16px 0 - - </style> - <script> - this.user = this.opts.user; - </script> -</mk-user-card> diff --git a/src/web/app/mobile/tags/user-followers.tag b/src/web/app/mobile/tags/user-followers.tag deleted file mode 100644 index b710e376c6..0000000000 --- a/src/web/app/mobile/tags/user-followers.tag +++ /dev/null @@ -1,28 +0,0 @@ -<mk-user-followers> - <mk-users-list ref="list" fetch={ fetch } count={ user.followers_count } you-know-count={ user.followers_you_know_count } no-users={ '%i18n:mobile.tags.mk-user-followers.no-users%' }/> - <style> - :scope - display block - - </style> - <script> - this.mixin('api'); - - this.user = this.opts.user; - - this.fetch = (iknow, limit, cursor, cb) => { - this.api('users/followers', { - user_id: this.user.id, - iknow: iknow, - limit: limit, - cursor: cursor ? cursor : undefined - }).then(cb); - }; - - this.on('mount', () => { - this.refs.list.on('loaded', () => { - this.trigger('loaded'); - }); - }); - </script> -</mk-user-followers> diff --git a/src/web/app/mobile/tags/user-following.tag b/src/web/app/mobile/tags/user-following.tag deleted file mode 100644 index 62ca091812..0000000000 --- a/src/web/app/mobile/tags/user-following.tag +++ /dev/null @@ -1,28 +0,0 @@ -<mk-user-following> - <mk-users-list ref="list" fetch={ fetch } count={ user.following_count } you-know-count={ user.following_you_know_count } no-users={ '%i18n:mobile.tags.mk-user-following.no-users%' }/> - <style> - :scope - display block - - </style> - <script> - this.mixin('api'); - - this.user = this.opts.user; - - this.fetch = (iknow, limit, cursor, cb) => { - this.api('users/following', { - user_id: this.user.id, - iknow: iknow, - limit: limit, - cursor: cursor ? cursor : undefined - }).then(cb); - }; - - this.on('mount', () => { - this.refs.list.on('loaded', () => { - this.trigger('loaded'); - }); - }); - </script> -</mk-user-following> diff --git a/src/web/app/mobile/tags/user-preview.tag b/src/web/app/mobile/tags/user-preview.tag deleted file mode 100644 index 48bf88a892..0000000000 --- a/src/web/app/mobile/tags/user-preview.tag +++ /dev/null @@ -1,95 +0,0 @@ -<mk-user-preview> - <a class="avatar-anchor" href={ '/' + user.username }> - <img class="avatar" src={ user.avatar_url + '?thumbnail&size=64' } alt="avatar"/> - </a> - <div class="main"> - <header> - <a class="name" href={ '/' + user.username }>{ user.name }</a> - <span class="username">@{ user.username }</span> - </header> - <div class="body"> - <div class="description">{ user.description }</div> - </div> - </div> - <style> - :scope - display block - margin 0 - padding 16px - font-size 12px - - @media (min-width 350px) - font-size 14px - - @media (min-width 500px) - font-size 16px - - &:after - content "" - display block - clear both - - > .avatar-anchor - display block - float left - margin 0 10px 0 0 - - @media (min-width 500px) - margin-right 16px - - > .avatar - display block - width 48px - height 48px - margin 0 - border-radius 6px - vertical-align bottom - - @media (min-width 500px) - width 58px - height 58px - border-radius 8px - - > .main - float left - width calc(100% - 58px) - - @media (min-width 500px) - width calc(100% - 74px) - - > header - @media (min-width 500px) - margin-bottom 2px - - > .name - display inline - margin 0 - padding 0 - color #777 - font-size 1em - font-weight 700 - text-align left - text-decoration none - - &:hover - text-decoration underline - - > .username - text-align left - margin 0 0 0 8px - color #ccc - - > .body - - > .description - cursor default - display block - margin 0 - padding 0 - overflow-wrap break-word - font-size 1.1em - color #717171 - - </style> - <script>this.user = this.opts.user</script> -</mk-user-preview> diff --git a/src/web/app/mobile/tags/user-timeline.tag b/src/web/app/mobile/tags/user-timeline.tag deleted file mode 100644 index 4dbe719f5a..0000000000 --- a/src/web/app/mobile/tags/user-timeline.tag +++ /dev/null @@ -1,33 +0,0 @@ -<mk-user-timeline> - <mk-timeline ref="timeline" init={ init } more={ more } empty={ withMedia ? '%i18n:mobile.tags.mk-user-timeline.no-posts-with-media%' : '%i18n:mobile.tags.mk-user-timeline.no-posts%' }/> - <style> - :scope - display block - max-width 600px - margin 0 auto - </style> - <script> - this.mixin('api'); - - this.user = this.opts.user; - this.withMedia = this.opts.withMedia; - - this.init = new Promise((res, rej) => { - this.api('users/posts', { - user_id: this.user.id, - with_media: this.withMedia - }).then(posts => { - res(posts); - this.trigger('loaded'); - }); - }); - - this.more = () => { - return this.api('users/posts', { - user_id: this.user.id, - with_media: this.withMedia, - max_id: this.refs.timeline.tail().id - }); - }; - </script> -</mk-user-timeline> diff --git a/src/web/app/mobile/tags/user.tag b/src/web/app/mobile/tags/user.tag deleted file mode 100644 index b3a2f1a147..0000000000 --- a/src/web/app/mobile/tags/user.tag +++ /dev/null @@ -1,735 +0,0 @@ -<mk-user> - <div class="user" if={ !fetching }> - <header> - <div class="banner" style={ user.banner_url ? 'background-image: url(' + user.banner_url + '?thumbnail&size=1024)' : '' }></div> - <div class="body"> - <div class="top"> - <a class="avatar"> - <img src={ user.avatar_url + '?thumbnail&size=200' } alt="avatar"/> - </a> - <mk-follow-button if={ SIGNIN && I.id != user.id } user={ user }/> - </div> - <div class="title"> - <h1>{ user.name }</h1> - <span class="username">@{ user.username }</span> - <span class="followed" if={ user.is_followed }>%i18n:mobile.tags.mk-user.follows-you%</span> - </div> - <div class="description">{ user.description }</div> - <div class="info"> - <p class="location" if={ user.profile.location }> - %fa:map-marker%{ user.profile.location } - </p> - <p class="birthday" if={ user.profile.birthday }> - %fa:birthday-cake%{ user.profile.birthday.replace('-', '年').replace('-', '月') + '日' } ({ age(user.profile.birthday) }歳) - </p> - </div> - <div class="status"> - <a> - <b>{ user.posts_count }</b> - <i>%i18n:mobile.tags.mk-user.posts%</i> - </a> - <a href="{ user.username }/following"> - <b>{ user.following_count }</b> - <i>%i18n:mobile.tags.mk-user.following%</i> - </a> - <a href="{ user.username }/followers"> - <b>{ user.followers_count }</b> - <i>%i18n:mobile.tags.mk-user.followers%</i> - </a> - </div> - </div> - <nav> - <a data-is-active={ page == 'overview' } onclick={ go.bind(null, 'overview') }>%i18n:mobile.tags.mk-user.overview%</a> - <a data-is-active={ page == 'posts' } onclick={ go.bind(null, 'posts') }>%i18n:mobile.tags.mk-user.timeline%</a> - <a data-is-active={ page == 'media' } onclick={ go.bind(null, 'media') }>%i18n:mobile.tags.mk-user.media%</a> - </nav> - </header> - <div class="body"> - <mk-user-overview if={ page == 'overview' } user={ user }/> - <mk-user-timeline if={ page == 'posts' } user={ user }/> - <mk-user-timeline if={ page == 'media' } user={ user } with-media={ true }/> - </div> - </div> - <style> - :scope - display block - - > .user - > header - box-shadow 0 4px 4px rgba(0, 0, 0, 0.3) - - > .banner - padding-bottom 33.3% - background-color #1b1b1b - background-size cover - background-position center - - > .body - padding 12px - margin 0 auto - max-width 600px - - > .top - &:after - content '' - display block - clear both - - > .avatar - display block - float left - width 25% - height 40px - - > img - display block - position absolute - left -2px - bottom -2px - width 100% - border 2px solid #313a42 - border-radius 6px - - @media (min-width 500px) - left -4px - bottom -4px - border 4px solid #313a42 - border-radius 12px - - > mk-follow-button - float right - height 40px - - > .title - margin 8px 0 - - > h1 - margin 0 - line-height 22px - font-size 20px - color #fff - - > .username - display inline-block - line-height 20px - font-size 16px - font-weight bold - color #657786 - - > .followed - margin-left 8px - padding 2px 4px - font-size 12px - color #657786 - background #f8f8f8 - border-radius 4px - - > .description - margin 8px 0 - color #fff - - > .info - margin 8px 0 - - > p - display inline - margin 0 16px 0 0 - color #a9b9c1 - - > i - margin-right 4px - - > .status - > a - color #657786 - - &:not(:last-child) - margin-right 16px - - > b - margin-right 4px - font-size 16px - color #fff - - > i - font-size 14px - - > mk-activity-table - margin 12px 0 0 0 - - > nav - display flex - justify-content center - margin 0 auto - max-width 600px - - > a - display block - flex 1 1 - text-align center - line-height 52px - font-size 14px - text-decoration none - color #657786 - border-bottom solid 2px transparent - - &[data-is-active] - font-weight bold - color $theme-color - border-color $theme-color - - > .body - padding 8px - - @media (min-width 500px) - padding 16px - - </style> - <script> - this.age = require('s-age'); - - this.mixin('i'); - this.mixin('api'); - - this.username = this.opts.user; - this.page = this.opts.page ? this.opts.page : 'overview'; - this.fetching = true; - - this.on('mount', () => { - this.api('users/show', { - username: this.username - }).then(user => { - this.fetching = false; - this.user = user; - this.trigger('loaded', user); - this.update(); - }); - }); - - this.go = page => { - this.update({ - page: page - }); - }; - </script> -</mk-user> - -<mk-user-overview> - <mk-post-detail if={ user.pinned_post } post={ user.pinned_post } compact={ true }/> - <section class="recent-posts"> - <h2>%fa:R comments%%i18n:mobile.tags.mk-user-overview.recent-posts%</h2> - <div> - <mk-user-overview-posts user={ user }/> - </div> - </section> - <section class="images"> - <h2>%fa:image%%i18n:mobile.tags.mk-user-overview.images%</h2> - <div> - <mk-user-overview-photos user={ user }/> - </div> - </section> - <section class="activity"> - <h2>%fa:chart-bar%%i18n:mobile.tags.mk-user-overview.activity%</h2> - <div> - <mk-user-overview-activity-chart user={ user }/> - </div> - </section> - <section class="keywords"> - <h2>%fa:R comment%%i18n:mobile.tags.mk-user-overview.keywords%</h2> - <div> - <mk-user-overview-keywords user={ user }/> - </div> - </section> - <section class="domains"> - <h2>%fa:globe%%i18n:mobile.tags.mk-user-overview.domains%</h2> - <div> - <mk-user-overview-domains user={ user }/> - </div> - </section> - <section class="frequently-replied-users"> - <h2>%fa:users%%i18n:mobile.tags.mk-user-overview.frequently-replied-users%</h2> - <div> - <mk-user-overview-frequently-replied-users user={ user }/> - </div> - </section> - <section class="followers-you-know" if={ SIGNIN && I.id !== user.id }> - <h2>%fa:users%%i18n:mobile.tags.mk-user-overview.followers-you-know%</h2> - <div> - <mk-user-overview-followers-you-know user={ user }/> - </div> - </section> - <p>%i18n:mobile.tags.mk-user-overview.last-used-at%: <b><mk-time time={ user.last_used_at }/></b></p> - <style> - :scope - display block - max-width 600px - margin 0 auto - - > mk-post-detail - margin 0 0 8px 0 - - > section - background #eee - border-radius 8px - box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2) - - &:not(:last-child) - margin-bottom 8px - - > h2 - margin 0 - padding 8px 10px - font-size 15px - font-weight normal - color #465258 - background #fff - border-radius 8px 8px 0 0 - - > i - margin-right 6px - - > .activity - > div - padding 8px - - > p - display block - margin 16px - text-align center - color #cad2da - - </style> - <script> - this.mixin('i'); - - this.user = this.opts.user; - </script> -</mk-user-overview> - -<mk-user-overview-posts> - <p class="initializing" if={ initializing }>%fa:spinner .pulse .fw%%i18n:mobile.tags.mk-user-overview-posts.loading%<mk-ellipsis/></p> - <div if={ !initializing && posts.length > 0 }> - <virtual each={ posts }> - <mk-user-overview-posts-post-card post={ this }/> - </virtual> - </div> - <p class="empty" if={ !initializing && posts.length == 0 }>%i18n:mobile.tags.mk-user-overview-posts.no-posts%</p> - <style> - :scope - display block - - > div - overflow-x scroll - -webkit-overflow-scrolling touch - white-space nowrap - padding 8px - - > * - vertical-align top - - &:not(:last-child) - margin-right 8px - - > .initializing - > .empty - margin 0 - padding 16px - text-align center - color #aaa - - > i - margin-right 4px - - </style> - <script> - this.mixin('api'); - - this.user = this.opts.user; - this.initializing = true; - - this.on('mount', () => { - this.api('users/posts', { - user_id: this.user.id - }).then(posts => { - this.update({ - posts: posts, - initializing: false - }); - }); - }); - </script> -</mk-user-overview-posts> - -<mk-user-overview-posts-post-card> - <a href={ '/' + post.user.username + '/' + post.id }> - <header> - <img src={ post.user.avatar_url + '?thumbnail&size=64' } alt="avatar"/><h3>{ post.user.name }</h3> - </header> - <div> - { text } - </div> - <mk-time time={ post.created_at }/> - </a> - <style> - :scope - display inline-block - width 150px - //height 120px - font-size 12px - background #fff - border-radius 4px - - > a - display block - color #2c3940 - - &:hover - text-decoration none - - > header - > img - position absolute - top 8px - left 8px - width 28px - height 28px - border-radius 6px - - > h3 - display inline-block - overflow hidden - width calc(100% - 45px) - margin 8px 0 0 42px - line-height 28px - white-space nowrap - text-overflow ellipsis - font-size 12px - - > div - padding 2px 8px 8px 8px - height 60px - overflow hidden - white-space normal - - &:after - content "" - display block - position absolute - top 40px - left 0 - width 100% - height 20px - background linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, #fff 100%) - - > mk-time - display inline-block - padding 8px - color #aaa - - </style> - <script> - import summary from '../../../../common/get-post-summary.ts'; - - this.post = this.opts.post; - this.text = summary(this.post); - </script> -</mk-user-overview-posts-post-card> - -<mk-user-overview-photos> - <p class="initializing" if={ initializing }>%fa:spinner .pulse .fw%%i18n:mobile.tags.mk-user-overview-photos.loading%<mk-ellipsis/></p> - <div class="stream" if={ !initializing && images.length > 0 }> - <virtual each={ image in images }> - <a class="img" style={ 'background-image: url(' + image.media.url + '?thumbnail&size=256)' } href={ '/' + image.post.user.username + '/' + image.post.id }></a> - </virtual> - </div> - <p class="empty" if={ !initializing && images.length == 0 }>%i18n:mobile.tags.mk-user-overview-photos.no-photos%</p> - <style> - :scope - display block - - > .stream - display -webkit-flex - display -moz-flex - display -ms-flex - display flex - justify-content center - flex-wrap wrap - padding 8px - - > .img - flex 1 1 33% - width 33% - height 80px - background-position center center - background-size cover - background-clip content-box - border solid 2px transparent - border-radius 4px - - > .initializing - > .empty - margin 0 - padding 16px - text-align center - color #aaa - - > i - margin-right 4px - - </style> - <script> - this.mixin('api'); - - this.images = []; - this.initializing = true; - this.user = this.opts.user; - - this.on('mount', () => { - this.api('users/posts', { - user_id: this.user.id, - with_media: true, - limit: 6 - }).then(posts => { - this.initializing = false; - posts.forEach(post => { - post.media.forEach(media => { - if (this.images.length < 9) this.images.push({ - post, - media - }); - }); - }); - this.update(); - }); - }); - </script> -</mk-user-overview-photos> - -<mk-user-overview-activity-chart> - <svg if={ data } ref="canvas" viewBox="0 0 30 1" preserveAspectRatio="none"> - <g each={ d, i in data.reverse() }> - <rect width="0.8" riot-height={ d.postsH } - riot-x={ i + 0.1 } riot-y={ 1 - d.postsH - d.repliesH - d.repostsH } - fill="#41ddde"/> - <rect width="0.8" riot-height={ d.repliesH } - riot-x={ i + 0.1 } riot-y={ 1 - d.repliesH - d.repostsH } - fill="#f7796c"/> - <rect width="0.8" riot-height={ d.repostsH } - riot-x={ i + 0.1 } riot-y={ 1 - d.repostsH } - fill="#a1de41"/> - </g> - </svg> - <style> - :scope - display block - max-width 600px - margin 0 auto - - > svg - display block - width 100% - height 80px - - > rect - transform-origin center - - </style> - <script> - this.mixin('api'); - - this.user = this.opts.user; - - this.on('mount', () => { - this.api('aggregation/users/activity', { - user_id: this.user.id, - limit: 30 - }).then(data => { - data.forEach(d => d.total = d.posts + d.replies + d.reposts); - this.peak = Math.max.apply(null, data.map(d => d.total)); - data.forEach(d => { - d.postsH = d.posts / this.peak; - d.repliesH = d.replies / this.peak; - d.repostsH = d.reposts / this.peak; - }); - this.update({ data }); - }); - }); - </script> -</mk-user-overview-activity-chart> - -<mk-user-overview-keywords> - <div if={ user.keywords != null && user.keywords.length > 1 }> - <virtual each={ keyword in user.keywords }> - <a>{ keyword }</a> - </virtual> - </div> - <p class="empty" if={ user.keywords == null || user.keywords.length == 0 }>%i18n:mobile.tags.mk-user-overview-keywords.no-keywords%</p> - <style> - :scope - display block - - > div - padding 4px - - > a - display inline-block - margin 4px - color #555 - - > .empty - margin 0 - padding 16px - text-align center - color #aaa - - > i - margin-right 4px - - </style> - <script> - this.user = this.opts.user; - </script> -</mk-user-overview-keywords> - -<mk-user-overview-domains> - <div if={ user.domains != null && user.domains.length > 1 }> - <virtual each={ domain in user.domains }> - <a style="opacity: { 0.5 + (domain.weight / 2) }">{ domain.domain }</a> - </virtual> - </div> - <p class="empty" if={ user.domains == null || user.domains.length == 0 }>%i18n:mobile.tags.mk-user-overview-domains.no-domains%</p> - <style> - :scope - display block - - > div - padding 4px - - > a - display inline-block - margin 4px - color #555 - - > .empty - margin 0 - padding 16px - text-align center - color #aaa - - > i - margin-right 4px - - </style> - <script> - this.user = this.opts.user; - </script> -</mk-user-overview-domains> - -<mk-user-overview-frequently-replied-users> - <p class="initializing" if={ initializing }>%fa:spinner .pulse .fw%%i18n:mobile.tags.mk-user-overview-frequently-replied-users.loading%<mk-ellipsis/></p> - <div if={ !initializing && users.length > 0 }> - <virtual each={ users }> - <mk-user-card user={ this.user }/> - </virtual> - </div> - <p class="empty" if={ !initializing && users.length == 0 }>%i18n:mobile.tags.mk-user-overview-frequently-replied-users.no-users%</p> - <style> - :scope - display block - - > div - overflow-x scroll - -webkit-overflow-scrolling touch - white-space nowrap - padding 8px - - > mk-user-card - &:not(:last-child) - margin-right 8px - - > .initializing - > .empty - margin 0 - padding 16px - text-align center - color #aaa - - > i - margin-right 4px - - </style> - <script> - this.mixin('api'); - - this.user = this.opts.user; - this.initializing = true; - - this.on('mount', () => { - this.api('users/get_frequently_replied_users', { - user_id: this.user.id - }).then(x => { - this.update({ - users: x, - initializing: false - }); - }); - }); - </script> -</mk-user-overview-frequently-replied-users> - -<mk-user-overview-followers-you-know> - <p class="initializing" if={ initializing }>%fa:spinner .pulse .fw%%i18n:mobile.tags.mk-user-overview-followers-you-know.loading%<mk-ellipsis/></p> - <div if={ !initializing && users.length > 0 }> - <virtual each={ user in users }> - <a href={ '/' + user.username }><img src={ user.avatar_url + '?thumbnail&size=64' } alt={ user.name }/></a> - </virtual> - </div> - <p class="empty" if={ !initializing && users.length == 0 }>%i18n:mobile.tags.mk-user-overview-followers-you-know.no-users%</p> - <style> - :scope - display block - - > div - padding 4px - - > a - display inline-block - margin 4px - - > img - width 48px - height 48px - vertical-align bottom - border-radius 100% - - > .initializing - > .empty - margin 0 - padding 16px - text-align center - color #aaa - - > i - margin-right 4px - - </style> - <script> - this.mixin('api'); - - this.user = this.opts.user; - this.initializing = true; - - this.on('mount', () => { - this.api('users/followers', { - user_id: this.user.id, - iknow: true, - limit: 30 - }).then(x => { - this.update({ - users: x.users, - initializing: false - }); - }); - }); - </script> -</mk-user-overview-followers-you-know> diff --git a/src/web/app/mobile/tags/users-list.tag b/src/web/app/mobile/tags/users-list.tag deleted file mode 100644 index 1dec33dddc..0000000000 --- a/src/web/app/mobile/tags/users-list.tag +++ /dev/null @@ -1,127 +0,0 @@ -<mk-users-list> - <nav> - <span data-is-active={ mode == 'all' } onclick={ setMode.bind(this, 'all') }>%i18n:mobile.tags.mk-users-list.all%<span>{ opts.count }</span></span> - <span if={ SIGNIN && opts.youKnowCount } data-is-active={ mode == 'iknow' } onclick={ setMode.bind(this, 'iknow') }>%i18n:mobile.tags.mk-users-list.known%<span>{ opts.youKnowCount }</span></span> - </nav> - <div class="users" if={ !fetching && users.length != 0 }> - <mk-user-preview each={ users } user={ this }/> - </div> - <button class="more" if={ !fetching && next != null } onclick={ more } disabled={ moreFetching }> - <span if={ !moreFetching }>%i18n:mobile.tags.mk-users-list.load-more%</span> - <span if={ moreFetching }>%i18n:common.loading%<mk-ellipsis/></span></button> - <p class="no" if={ !fetching && users.length == 0 }>{ opts.noUsers }</p> - <p class="fetching" if={ fetching }>%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p> - <style> - :scope - display block - - > nav - display flex - justify-content center - margin 0 auto - max-width 600px - border-bottom solid 1px rgba(0, 0, 0, 0.2) - - > span - display block - flex 1 1 - text-align center - line-height 52px - font-size 14px - color #657786 - border-bottom solid 2px transparent - - &[data-is-active] - font-weight bold - color $theme-color - border-color $theme-color - - > span - display inline-block - margin-left 4px - padding 2px 5px - font-size 12px - line-height 1 - color #fff - background rgba(0, 0, 0, 0.3) - border-radius 20px - - > .users - margin 8px auto - max-width 500px - width calc(100% - 16px) - background #fff - border-radius 8px - box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2) - - @media (min-width 500px) - margin 16px auto - width calc(100% - 32px) - - > * - border-bottom solid 1px rgba(0, 0, 0, 0.05) - - > .no - margin 0 - padding 16px - text-align center - color #aaa - - > .fetching - margin 0 - padding 16px - text-align center - color #aaa - - > [data-fa] - margin-right 4px - - </style> - <script> - this.mixin('i'); - - this.limit = 30; - this.mode = 'all'; - - this.fetching = true; - this.moreFetching = false; - - this.on('mount', () => { - this.fetch(() => this.trigger('loaded')); - }); - - this.fetch = cb => { - this.update({ - fetching: true - }); - this.opts.fetch(this.mode == 'iknow', this.limit, null, obj => { - this.update({ - fetching: false, - users: obj.users, - next: obj.next - }); - if (cb) cb(); - }); - }; - - this.more = () => { - this.update({ - moreFetching: true - }); - this.opts.fetch(this.mode == 'iknow', this.limit, this.next, obj => { - this.update({ - moreFetching: false, - users: this.users.concat(obj.users), - next: obj.next - }); - }); - }; - - this.setMode = mode => { - this.update({ - mode: mode - }); - this.fetch(); - }; - </script> -</mk-users-list> diff --git a/src/web/app/reset.styl b/src/web/app/reset.styl deleted file mode 100644 index 3d4b06dbdf..0000000000 --- a/src/web/app/reset.styl +++ /dev/null @@ -1,30 +0,0 @@ -input:not([type]) -input[type='text'] -input[type='password'] -input[type='search'] -input[type='email'] -textarea -button -progress - -webkit-appearance none - -moz-appearance none - appearance none - box-shadow none - -button - margin 0 - padding 0 - background transparent - border none - cursor pointer - color inherit - - * - pointer-events none - - &[disabled] - cursor default - -pre - overflow auto - white-space pre diff --git a/src/web/app/safe.js b/src/web/app/safe.js deleted file mode 100644 index 2fd5361725..0000000000 --- a/src/web/app/safe.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * ブラウザの検証 - */ - -// Detect an old browser -if (!('fetch' in window)) { - alert( - 'お使いのブラウザが古いためMisskeyを動作させることができません。' + - 'バージョンを最新のものに更新するか、別のブラウザをお試しください。' + - '\n\n' + - 'Your browser seems outdated. ' + - 'To run Misskey, please update your browser to latest version or try other browsers.'); -} - -// Detect Edge -if (navigator.userAgent.toLowerCase().indexOf('edge') != -1) { - alert( - '現在、お使いのブラウザ(Microsoft Edge)ではMisskeyは正しく動作しません。' + - 'サポートしているブラウザ: Google Chrome, Mozilla Firefox, Apple Safari など' + - '\n\n' + - 'Currently, Misskey cannot run correctly on your browser (Microsoft Edge). ' + - 'Supported browsers: Google Chrome, Mozilla Firefox, Apple Safari, etc'); -} - -// Check whether cookie enabled -if (!navigator.cookieEnabled) { - alert( - 'Misskeyを利用するにはCookieを有効にしてください。' + - '\n\n' + - 'To use Misskey, please enable Cookie.'); -} diff --git a/src/web/app/stats/script.ts b/src/web/app/stats/script.ts deleted file mode 100644 index 3bbd80c339..0000000000 --- a/src/web/app/stats/script.ts +++ /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(() => { - mount(document.createElement('mk-index')); -}); - -function mount(content) { - riot.mount(document.getElementById('app').appendChild(content)); -} diff --git a/src/web/app/stats/style.styl b/src/web/app/stats/style.styl deleted file mode 100644 index 5ae230ea56..0000000000 --- a/src/web/app/stats/style.styl +++ /dev/null @@ -1,10 +0,0 @@ -@import "../app" -@import "../reset" - -html - color #456267 - background #fff - -body - margin 0 - padding 0 diff --git a/src/web/app/stats/tags/index.tag b/src/web/app/stats/tags/index.tag deleted file mode 100644 index 4b5415b2fd..0000000000 --- a/src/web/app/stats/tags/index.tag +++ /dev/null @@ -1,209 +0,0 @@ -<mk-index> - <h1>Misskey<i>Statistics</i></h1> - <main if={ !initializing }> - <mk-users stats={ stats }/> - <mk-posts stats={ stats }/> - </main> - <footer><a href={ _URL_ }>{ _HOST_ }</a></footer> - <style> - :scope - display block - margin 0 auto - padding 0 16px - max-width 700px - - > h1 - margin 0 - padding 24px 0 0 0 - font-size 24px - font-weight normal - - > i - font-style normal - color #f43b16 - - > main - > * - margin 24px 0 - padding-top 24px - border-top solid 1px #eee - - > h2 - margin 0 0 12px 0 - font-size 18px - font-weight normal - - > footer - margin 24px 0 - text-align center - - > a - color #546567 - </style> - <script> - this.mixin('api'); - - this.initializing = true; - - this.on('mount', () => { - this.api('stats').then(stats => { - this.update({ - initializing: false, - stats - }); - }); - }); - </script> -</mk-index> - -<mk-posts> - <h2>%i18n:stats.posts-count% <b>{ stats.posts_count }</b></h2> - <mk-posts-chart if={ !initializing } data={ data }/> - <style> - :scope - display block - </style> - <script> - this.mixin('api'); - - this.initializing = true; - this.stats = this.opts.stats; - - this.on('mount', () => { - this.api('aggregation/posts', { - limit: 365 - }).then(data => { - this.update({ - initializing: false, - data - }); - }); - }); - </script> -</mk-posts> - -<mk-users> - <h2>%i18n:stats.users-count% <b>{ stats.users_count }</b></h2> - <mk-users-chart if={ !initializing } data={ data }/> - <style> - :scope - display block - </style> - <script> - this.mixin('api'); - - this.initializing = true; - this.stats = this.opts.stats; - - this.on('mount', () => { - this.api('aggregation/users', { - limit: 365 - }).then(data => { - this.update({ - initializing: false, - data - }); - }); - }); - </script> -</mk-users> - -<mk-posts-chart> - <svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none"> - <title>Black ... Total<br/>Blue ... Posts<br/>Red ... Replies<br/>Green ... Reposts</title> - <polyline - riot-points={ pointsPost } - fill="none" - stroke-width="1" - stroke="#41ddde"/> - <polyline - riot-points={ pointsReply } - fill="none" - stroke-width="1" - stroke="#f7796c"/> - <polyline - riot-points={ pointsRepost } - fill="none" - stroke-width="1" - stroke="#a1de41"/> - <polyline - riot-points={ pointsTotal } - fill="none" - stroke-width="1" - stroke="#555" - stroke-dasharray="2 2"/> - </svg> - <style> - :scope - display block - - > svg - display block - padding 1px - width 100% - </style> - <script> - this.viewBoxX = 365; - this.viewBoxY = 80; - - this.data = this.opts.data.reverse(); - this.data.forEach(d => d.total = d.posts + d.replies + d.reposts); - const peak = Math.max.apply(null, this.data.map(d => d.total)); - - this.on('mount', () => { - this.render(); - }); - - this.render = () => { - this.update({ - pointsPost: this.data.map((d, i) => `${i},${(1 - (d.posts / peak)) * this.viewBoxY}`).join(' '), - pointsReply: this.data.map((d, i) => `${i},${(1 - (d.replies / peak)) * this.viewBoxY}`).join(' '), - pointsRepost: this.data.map((d, i) => `${i},${(1 - (d.reposts / peak)) * this.viewBoxY}`).join(' '), - pointsTotal: this.data.map((d, i) => `${i},${(1 - (d.total / peak)) * this.viewBoxY}`).join(' ') - }); - }; - </script> -</mk-posts-chart> - -<mk-users-chart> - <svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none"> - <polyline - riot-points={ createdPoints } - fill="none" - stroke-width="1" - stroke="#1cde84"/> - <polyline - riot-points={ totalPoints } - fill="none" - stroke-width="1" - stroke="#555"/> - </svg> - <style> - :scope - display block - - > svg - display block - padding 1px - width 100% - </style> - <script> - this.viewBoxX = 365; - this.viewBoxY = 80; - - this.data = this.opts.data.reverse(); - const totalPeak = Math.max.apply(null, this.data.map(d => d.total)); - const createdPeak = Math.max.apply(null, this.data.map(d => d.created)); - - this.on('mount', () => { - this.render(); - }); - - this.render = () => { - this.update({ - totalPoints: this.data.map((d, i) => `${i},${(1 - (d.total / totalPeak)) * this.viewBoxY}`).join(' '), - createdPoints: this.data.map((d, i) => `${i},${(1 - (d.created / createdPeak)) * this.viewBoxY}`).join(' ') - }); - }; - </script> -</mk-users-chart> diff --git a/src/web/app/stats/tags/index.ts b/src/web/app/stats/tags/index.ts deleted file mode 100644 index f41151949f..0000000000 --- a/src/web/app/stats/tags/index.ts +++ /dev/null @@ -1 +0,0 @@ -require('./index.tag'); diff --git a/src/web/app/status/script.ts b/src/web/app/status/script.ts deleted file mode 100644 index 84483acab7..0000000000 --- a/src/web/app/status/script.ts +++ /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(() => { - mount(document.createElement('mk-index')); -}); - -function mount(content) { - riot.mount(document.getElementById('app').appendChild(content)); -} diff --git a/src/web/app/status/style.styl b/src/web/app/status/style.styl deleted file mode 100644 index 5ae230ea56..0000000000 --- a/src/web/app/status/style.styl +++ /dev/null @@ -1,10 +0,0 @@ -@import "../app" -@import "../reset" - -html - color #456267 - background #fff - -body - margin 0 - padding 0 diff --git a/src/web/app/status/tags/index.tag b/src/web/app/status/tags/index.tag deleted file mode 100644 index dcadc66172..0000000000 --- a/src/web/app/status/tags/index.tag +++ /dev/null @@ -1,201 +0,0 @@ -<mk-index> - <h1>Misskey<i>Status</i></h1> - <p>%fa:info-circle%%i18n:status.all-systems-maybe-operational%</p> - <main> - <mk-cpu-usage connection={ connection }/> - <mk-mem-usage connection={ connection }/> - </main> - <footer><a href={ _URL_ }>{ _HOST_ }</a></footer> - <style> - :scope - display block - margin 0 auto - padding 0 16px - max-width 700px - - > h1 - margin 0 - padding 24px 0 16px 0 - font-size 24px - font-weight normal - - > [data-fa] - font-style normal - color #f43b16 - - > p - display block - margin 0 - padding 12px 16px - background #eaf4ef - //border solid 1px #99ccb2 - border-radius 4px - - > [data-fa] - margin-right 5px - - > main - > * - margin 24px 0 - - > h2 - margin 0 0 12px 0 - font-size 18px - font-weight normal - - > footer - margin 24px 0 - text-align center - - > a - color #546567 - </style> - <script> - import Connection from '../../common/scripts/streaming/server-stream'; - - this.mixin('api'); - - this.initializing = true; - this.connection = new Connection(); - - this.on('mount', () => { - this.api('meta').then(meta => { - this.update({ - initializing: false, - meta - }); - }); - }); - - this.on('unmount', () => { - this.connection.close(); - }); - - </script> -</mk-index> - -<mk-cpu-usage> - <h2>CPU <b>{ percentage }%</b></h2> - <mk-line-chart ref="chart"/> - <style> - :scope - display block - </style> - <script> - this.connection = this.opts.connection; - - this.on('mount', () => { - this.connection.on('stats', this.onStats); - }); - - this.on('unmount', () => { - this.connection.off('stats', this.onStats); - }); - - this.onStats = stats => { - this.refs.chart.addData(1 - stats.cpu_usage); - - const percentage = (stats.cpu_usage * 100).toFixed(0); - - this.update({ - percentage - }); - }; - </script> -</mk-cpu-usage> - -<mk-mem-usage> - <h2>MEM <b>{ percentage }%</b></h2> - <mk-line-chart ref="chart"/> - <style> - :scope - display block - </style> - <script> - this.connection = this.opts.connection; - - this.on('mount', () => { - this.connection.on('stats', this.onStats); - }); - - this.on('unmount', () => { - this.connection.off('stats', this.onStats); - }); - - this.onStats = stats => { - stats.mem.used = stats.mem.total - stats.mem.free; - this.refs.chart.addData(1 - (stats.mem.used / stats.mem.total)); - - const percentage = (stats.mem.used / stats.mem.total * 100).toFixed(0); - - this.update({ - percentage - }); - }; - </script> -</mk-mem-usage> - -<mk-line-chart> - <svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none"> - <defs> - <linearGradient id={ gradientId } x1="0" x2="0" y1="1" y2="0"> - <stop offset="0%" stop-color="rgba(244, 59, 22, 0)"></stop> - <stop offset="100%" stop-color="#f43b16"></stop> - </linearGradient> - <mask id={ maskId } x="0" y="0" riot-width={ viewBoxX } riot-height={ viewBoxY }> - <polygon - riot-points={ polygonPoints } - fill="#fff" - fill-opacity="0.5"/> - </mask> - </defs> - <line x1="0" y1="0" riot-x2={ viewBoxX } y2="0" stroke="rgba(255, 255, 255, 0.1)" stroke-width="0.25" stroke-dasharray="1"/> - <line x1="0" y1="25%" riot-x2={ viewBoxX } y2="25%" stroke="rgba(255, 255, 255, 0.1)" stroke-width="0.25" stroke-dasharray="1"/> - <line x1="0" y1="50%" riot-x2={ viewBoxX } y2="50%" stroke="rgba(255, 255, 255, 0.1)" stroke-width="0.25" stroke-dasharray="1"/> - <line x1="0" y1="75%" riot-x2={ viewBoxX } y2="75%" stroke="rgba(255, 255, 255, 0.1)" stroke-width="0.25" stroke-dasharray="1"/> - <line x1="0" y1="100%" riot-x2={ viewBoxX } y2="100%" stroke="rgba(255, 255, 255, 0.1)" stroke-width="0.25" stroke-dasharray="1"/> - <rect - x="-1" y="-1" - riot-width={ viewBoxX + 2 } riot-height={ viewBoxY + 2 } - style="stroke: none; fill: url(#{ gradientId }); mask: url(#{ maskId })"/> - <polyline - riot-points={ polylinePoints } - fill="none" - stroke="#f43b16" - stroke-width="0.5"/> - </svg> - <style> - :scope - display block - padding 16px - border-radius 8px - background #1c2531 - - > svg - display block - padding 1px - width 100% - </style> - <script> - import uuid from 'uuid'; - - this.viewBoxX = 100; - this.viewBoxY = 30; - this.data = []; - this.gradientId = uuid(); - this.maskId = uuid(); - - this.addData = data => { - this.data.push(data); - if (this.data.length > 100) this.data.shift(); - - const polylinePoints = this.data.map((d, i) => `${this.viewBoxX - ((this.data.length - 1) - i)},${d * this.viewBoxY}`).join(' '); - const polygonPoints = `${this.viewBoxX - (this.data.length - 1)},${ this.viewBoxY } ${ polylinePoints } ${ this.viewBoxX },${ this.viewBoxY }`; - - this.update({ - polylinePoints, - polygonPoints - }); - }; - </script> -</mk-line-chart> diff --git a/src/web/app/status/tags/index.ts b/src/web/app/status/tags/index.ts deleted file mode 100644 index f41151949f..0000000000 --- a/src/web/app/status/tags/index.ts +++ /dev/null @@ -1 +0,0 @@ -require('./index.tag'); diff --git a/src/web/app/sw.js b/src/web/app/sw.js deleted file mode 100644 index 669703b16c..0000000000 --- a/src/web/app/sw.js +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Service Worker - */ - -import composeNotification from './common/scripts/compose-notification'; - -// キャッシュするリソース -const cachee = [ - '/' -]; - -// インストールされたとき -self.addEventListener('install', ev => { - console.info('installed'); - - ev.waitUntil(Promise.all([ - self.skipWaiting(), // Force activate - caches.open(_VERSION_).then(cache => cache.addAll(cachee)) // Cache - ])); -}); - -// アクティベートされたとき -self.addEventListener('activate', ev => { - // Clean up old caches - ev.waitUntil( - caches.keys().then(keys => Promise.all( - keys - .filter(key => key != _VERSION_) - .map(key => caches.delete(key)) - )) - ); -}); - -// リクエストが発生したとき -self.addEventListener('fetch', ev => { - ev.respondWith( - // キャッシュがあるか確認してあればそれを返す - caches.match(ev.request).then(response => - response || fetch(ev.request) - ) - ); -}); - -// プッシュ通知を受け取ったとき -self.addEventListener('push', ev => { - console.log('pushed'); - - // クライアント取得 - ev.waitUntil(self.clients.matchAll({ - includeUncontrolled: true - }).then(clients => { - // クライアントがあったらストリームに接続しているということなので通知しない - if (clients.length != 0) return; - - const { type, body } = ev.data.json(); - - console.log(type, body); - - const n = composeNotification(type, body); - return self.registration.showNotification(n.title, { - body: n.body, - icon: n.icon, - }); - })); -}); - -self.addEventListener('message', ev => { - if (ev.data == 'clear') { - caches.keys().then(keys => keys.forEach(key => caches.delete(key))); - } -}); diff --git a/src/web/assets/404.js b/src/web/assets/404.js deleted file mode 100644 index 285704d113..0000000000 --- a/src/web/assets/404.js +++ /dev/null @@ -1,19 +0,0 @@ -const yn = window.confirm( - 'サーバー上に存在しないスクリプトがリクエストされました。お使いのMisskeyのバージョンが古いことが原因の可能性があります。Misskeyを更新しますか?'); - -if (yn) { - // Clear cache (serive worker) - try { - navigator.serviceWorker.controller.postMessage('clear'); - - navigator.serviceWorker.getRegistrations().then(registrations => { - registrations.forEach(registration => registration.unregister()); - }); - } catch (e) { - console.error(e); - } - - location.reload(true); -} else { - alert('問題が解決しない場合はサーバー管理者までお問い合せください。'); -} diff --git a/src/web/assets/code-highlight.css b/src/web/assets/code-highlight.css deleted file mode 100644 index f0807dc9c3..0000000000 --- a/src/web/assets/code-highlight.css +++ /dev/null @@ -1,93 +0,0 @@ -.hljs { - font-family: Consolas, 'Courier New', Courier, Monaco, monospace; -} - -.hljs, -.hljs-subst { - color: #444; -} - -.hljs-comment { - color: #888888; -} - -.hljs-keyword { - color: #2973b7; -} - -.hljs-number { - color: #ae81ff; -} - -.hljs-string { - color: #e96900; -} - -.hljs-regexp { - color: #e9003f; -} - -.hljs-attribute, -.hljs-selector-tag, -.hljs-meta-keyword, -.hljs-doctag, -.hljs-name { - font-weight: bold; -} - -.hljs-type, -.hljs-selector-id, -.hljs-selector-class, -.hljs-quote, -.hljs-template-tag, -.hljs-deletion { - color: #880000; -} - -.hljs-title, -.hljs-section { - color: #880000; - font-weight: bold; -} - -.hljs-symbol, -.hljs-variable, -.hljs-template-variable, -.hljs-link, -.hljs-selector-attr, -.hljs-selector-pseudo { - color: #BC6060; -} - -/* Language color: hue: 90; */ - -.hljs-literal { - color: #78A960; -} - -.hljs-built_in, -.hljs-bullet, -.hljs-code, -.hljs-addition { - color: #397300; -} - -/* Meta color: hue: 200 */ - -.hljs-meta { - color: #1f7199; -} - -.hljs-meta-string { - color: #4d99bf; -} - -/* Misc effects */ - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/web/assets/error.jpg b/src/web/assets/error.jpg Binary files differdeleted file mode 100644 index 872b1a3f5d..0000000000 --- a/src/web/assets/error.jpg +++ /dev/null diff --git a/src/web/assets/favicon.ico b/src/web/assets/favicon.ico Binary files differdeleted file mode 100644 index ed9820d5f4..0000000000 --- a/src/web/assets/favicon.ico +++ /dev/null diff --git a/src/web/assets/label.svg b/src/web/assets/label.svg deleted file mode 100644 index b1f85f3c07..0000000000 --- a/src/web/assets/label.svg +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
- y="0px" width="96px" height="96px" viewBox="0 0 96 96" enable-background="new 0 0 96 96" xml:space="preserve">
-<polygon fill="#0B8AEA" points="0,45.255 45.254,0 84.854,0 0,84.854 "/>
-</svg>
diff --git a/src/web/assets/manifest.json b/src/web/assets/manifest.json deleted file mode 100644 index 783d0539ac..0000000000 --- a/src/web/assets/manifest.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "short_name": "Misskey", - "name": "Misskey", - "start_url": "/", - "display": "standalone", - "background_color": "#313a42" -} diff --git a/src/web/assets/reactions/angry.png b/src/web/assets/reactions/angry.png Binary files differdeleted file mode 100644 index d81c431a25..0000000000 --- a/src/web/assets/reactions/angry.png +++ /dev/null diff --git a/src/web/assets/reactions/confused.png b/src/web/assets/reactions/confused.png Binary files differdeleted file mode 100644 index cfaa60146f..0000000000 --- a/src/web/assets/reactions/confused.png +++ /dev/null diff --git a/src/web/assets/reactions/congrats.png b/src/web/assets/reactions/congrats.png Binary files differdeleted file mode 100644 index 350adda322..0000000000 --- a/src/web/assets/reactions/congrats.png +++ /dev/null diff --git a/src/web/assets/reactions/hmm.png b/src/web/assets/reactions/hmm.png Binary files differdeleted file mode 100644 index a9a7e9ac88..0000000000 --- a/src/web/assets/reactions/hmm.png +++ /dev/null diff --git a/src/web/assets/reactions/laugh.png b/src/web/assets/reactions/laugh.png Binary files differdeleted file mode 100644 index cd2225ffe1..0000000000 --- a/src/web/assets/reactions/laugh.png +++ /dev/null diff --git a/src/web/assets/reactions/like.png b/src/web/assets/reactions/like.png Binary files differdeleted file mode 100644 index 9fe67c9109..0000000000 --- a/src/web/assets/reactions/like.png +++ /dev/null diff --git a/src/web/assets/reactions/love.png b/src/web/assets/reactions/love.png Binary files differdeleted file mode 100644 index b8a7532ef0..0000000000 --- a/src/web/assets/reactions/love.png +++ /dev/null diff --git a/src/web/assets/reactions/pudding.png b/src/web/assets/reactions/pudding.png Binary files differdeleted file mode 100644 index 27a6b048e8..0000000000 --- a/src/web/assets/reactions/pudding.png +++ /dev/null diff --git a/src/web/assets/reactions/surprise.png b/src/web/assets/reactions/surprise.png Binary files differdeleted file mode 100644 index 5904cb2c6c..0000000000 --- a/src/web/assets/reactions/surprise.png +++ /dev/null diff --git a/src/web/assets/recover.html b/src/web/assets/recover.html deleted file mode 100644 index 4922b68d35..0000000000 --- a/src/web/assets/recover.html +++ /dev/null @@ -1,36 +0,0 @@ -<!DOCTYPE html> - -<html> - <head> - <meta charset="utf-8"> - <title>Misskeyのリカバリ</title> - <script> - - const yn = window.confirm('キャッシュをクリアしますか?(他のタブでMisskeyを開いている状態だと正常にクリアできないので、他のMisskeyのタブをすべて閉じてから行ってください)\n\nDo you want to clear caches?'); - - if (yn) { - try { - navigator.serviceWorker.controller.postMessage('clear'); - - navigator.serviceWorker.getRegistrations().then(registrations => { - registrations.forEach(registration => registration.unregister()); - }); - - } catch (e) { - console.error(e); - } - - alert('キャッシュをクリアしました。'); - - alert('まもなくページを再度読み込みします。再度読み込みが終わると、再度キャッシュをクリアするか尋ねられるので、「キャンセル」を選択して抜けてください。'); - - setTimeout(() => { - location.reload(true); - }, 100); - } else { - location.href = '/'; - } - - </script> - </head> -</html> diff --git a/src/web/assets/title.svg b/src/web/assets/title.svg deleted file mode 100644 index 747fcd38b1..0000000000 --- a/src/web/assets/title.svg +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 16.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
- y="0px" width="256px" height="256px" viewBox="0 0 256 256" enable-background="new 0 0 256 256" xml:space="preserve">
-<circle fill="#2B2F2D" cx="128" cy="153.6" r="19.201"/>
-<circle fill="#2B2F2D" cx="51.2" cy="153.6" r="19.2"/>
-<circle fill="#2B2F2D" cx="204.8" cy="153.6" r="19.2"/>
-<polyline fill="none" stroke="#2B2F2D" stroke-width="16" stroke-linejoin="round" stroke-miterlimit="10" points="51.2,153.6
- 89.601,102.4 128,153.6 166.4,102.4 204.799,153.6 "/>
-<circle fill="#2B2F2D" cx="89.6" cy="102.4" r="19.2"/>
-<circle fill="#2B2F2D" cx="166.4" cy="102.4" r="19.199"/>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-</svg>
diff --git a/src/web/assets/unread.svg b/src/web/assets/unread.svg deleted file mode 100644 index 8c3cc9f475..0000000000 --- a/src/web/assets/unread.svg +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 16.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
- y="0px" width="32px" height="32px" viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve">
-<circle fill="#3AA2DC" cx="16.5" cy="16.5" r="6"/>
-</svg>
diff --git a/src/web/server.ts b/src/web/server.ts deleted file mode 100644 index 1d3687f89e..0000000000 --- a/src/web/server.ts +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Web Server - */ - -import * as path from 'path'; -import ms = require('ms'); - -// express modules -import * as express from 'express'; -import * as bodyParser from 'body-parser'; -import * as favicon from 'serve-favicon'; -import * as compression from 'compression'; - -/** - * Init app - */ -const app = express(); -app.disable('x-powered-by'); - -app.use(bodyParser.urlencoded({ extended: true })); -app.use(bodyParser.json({ - type: ['application/json', 'text/plain'] -})); -app.use(compression()); - -/** - * Initialize requests - */ -app.use((req, res, next) => { - res.header('X-Frame-Options', 'DENY'); - next(); -}); - -/** - * Static assets - */ -app.use(favicon(`${__dirname}/assets/favicon.ico`)); -app.get('/apple-touch-icon.png', (req, res) => res.sendFile(`${__dirname}/assets/apple-touch-icon.png`)); -app.use('/assets', express.static(`${__dirname}/assets`, { - maxAge: ms('7 days') -})); -app.use('/assets/*.js', (req, res) => res.sendFile(`${__dirname}/assets/404.js`)); -app.use('/assets', (req, res) => { - res.sendStatus(404); -}); - -app.use('/recover', (req, res) => res.sendFile(`${__dirname}/assets/recover.html`)); - -/** - * ServiceWroker - */ -app.get(/^\/sw\.(.+?)\.js$/, (req, res) => - res.sendFile(`${__dirname}/assets/sw.${req.params[0]}.js`)); - -/** - * Manifest - */ -app.get('/manifest.json', (req, res) => - res.sendFile(`${__dirname}/assets/manifest.json`)); - -/** - * Common API - */ -app.get(/\/api:url/, require('./service/url-preview')); - -/** - * Routing - */ -app.get('*', (req, res) => { - res.sendFile(path.resolve(`${__dirname}/app/base.html`), { - maxAge: ms('7 days') - }); -}); - -module.exports = app; diff --git a/src/web/service/url-preview.ts b/src/web/service/url-preview.ts deleted file mode 100644 index 0c5fd8a78e..0000000000 --- a/src/web/service/url-preview.ts +++ /dev/null @@ -1,15 +0,0 @@ -import * as express from 'express'; -import summaly from 'summaly'; - -module.exports = async (req: express.Request, res: express.Response) => { - const summary = await summaly(req.query.url); - summary.icon = wrap(summary.icon); - summary.thumbnail = wrap(summary.thumbnail); - res.send(summary); -}; - -function wrap(url: string): string { - return url != null - ? `https://images.weserv.nl/?url=${url.replace(/^https?:\/\//, '')}` - : null; -} |