summaryrefslogtreecommitdiff
path: root/src/web/app
diff options
context:
space:
mode:
authorsyuilo <syuilotan@yahoo.co.jp>2018-02-05 03:59:29 +0900
committersyuilo <syuilotan@yahoo.co.jp>2018-02-05 03:59:29 +0900
commitdbd3cdb308d2edf600b20a8b632045c6163ae326 (patch)
treede3630065fcddeb1916668ef3b0b43a219340e2e /src/web/app
parentFix (diff)
parentMerge pull request #1097 from syuilo/refactor (diff)
downloadsharkey-dbd3cdb308d2edf600b20a8b632045c6163ae326.tar.gz
sharkey-dbd3cdb308d2edf600b20a8b632045c6163ae326.tar.bz2
sharkey-dbd3cdb308d2edf600b20a8b632045c6163ae326.zip
Merge remote-tracking branch 'refs/remotes/origin/master' into vue-#972
Diffstat (limited to 'src/web/app')
-rw-r--r--src/web/app/app.styl38
-rw-r--r--src/web/app/base.pug3
-rw-r--r--src/web/app/common/scripts/api.ts2
-rw-r--r--src/web/app/common/scripts/parse-search-query.ts53
-rw-r--r--src/web/app/common/tags/authorized-apps.tag4
-rw-r--r--src/web/app/common/tags/copyright.tag7
-rw-r--r--src/web/app/common/tags/index.ts1
-rw-r--r--src/web/app/common/tags/introduction.tag2
-rw-r--r--src/web/app/common/tags/messaging/room.tag2
-rw-r--r--src/web/app/common/tags/nav-links.tag5
-rw-r--r--src/web/app/common/tags/signin-history.tag120
-rw-r--r--src/web/app/common/tags/signin.tag11
-rw-r--r--src/web/app/common/tags/signup.tag4
-rw-r--r--src/web/app/common/tags/twitter-setting.tag2
-rw-r--r--src/web/app/desktop/router.ts4
-rw-r--r--src/web/app/desktop/style.styl65
-rw-r--r--src/web/app/desktop/tags/drive/browser.tag6
-rw-r--r--src/web/app/desktop/tags/drive/file.tag16
-rw-r--r--src/web/app/desktop/tags/home-widgets/mentions.tag2
-rw-r--r--src/web/app/desktop/tags/home-widgets/timeline.tag4
-rw-r--r--src/web/app/desktop/tags/image-dialog.tag61
-rw-r--r--src/web/app/desktop/tags/images-viewer.tag45
-rw-r--r--src/web/app/desktop/tags/images.tag172
-rw-r--r--src/web/app/desktop/tags/index.ts3
-rw-r--r--src/web/app/desktop/tags/notifications.tag2
-rw-r--r--src/web/app/desktop/tags/pages/entrance.tag6
-rw-r--r--src/web/app/desktop/tags/post-detail-sub.tag11
-rw-r--r--src/web/app/desktop/tags/post-detail.tag7
-rw-r--r--src/web/app/desktop/tags/post-form.tag69
-rw-r--r--src/web/app/desktop/tags/search-posts.tag21
-rw-r--r--src/web/app/desktop/tags/select-file-from-drive-window.tag10
-rw-r--r--src/web/app/desktop/tags/settings.tag369
-rw-r--r--src/web/app/desktop/tags/sub-post-content.tag2
-rw-r--r--src/web/app/desktop/tags/timeline.tag7
-rw-r--r--src/web/app/desktop/tags/ui.tag7
-rw-r--r--src/web/app/desktop/tags/user-timeline.tag4
-rw-r--r--src/web/app/desktop/tags/user.tag27
-rw-r--r--src/web/app/desktop/ui.styl122
-rw-r--r--src/web/app/mobile/router.ts9
-rw-r--r--src/web/app/mobile/tags/drive.tag17
-rw-r--r--src/web/app/mobile/tags/drive/file-viewer.tag59
-rw-r--r--src/web/app/mobile/tags/drive/file.tag202
-rw-r--r--src/web/app/mobile/tags/drive/folder.tag60
-rw-r--r--src/web/app/mobile/tags/home-timeline.tag2
-rw-r--r--src/web/app/mobile/tags/images-viewer.tag26
-rw-r--r--src/web/app/mobile/tags/images.tag82
-rw-r--r--src/web/app/mobile/tags/index.ts3
-rw-r--r--src/web/app/mobile/tags/notifications.tag2
-rw-r--r--src/web/app/mobile/tags/page/entrance.tag4
-rw-r--r--src/web/app/mobile/tags/page/settings.tag1
-rw-r--r--src/web/app/mobile/tags/page/settings/api.tag36
-rw-r--r--src/web/app/mobile/tags/post-detail.tag2
-rw-r--r--src/web/app/mobile/tags/post-form.tag76
-rw-r--r--src/web/app/mobile/tags/search-posts.tag18
-rw-r--r--src/web/app/mobile/tags/sub-post-content.tag2
-rw-r--r--src/web/app/mobile/tags/timeline.tag2
-rw-r--r--src/web/app/mobile/tags/ui.tag6
-rw-r--r--src/web/app/mobile/tags/user-timeline.tag2
58 files changed, 1164 insertions, 743 deletions
diff --git a/src/web/app/app.styl b/src/web/app/app.styl
index de66df74d4..22043b8833 100644
--- a/src/web/app/app.styl
+++ b/src/web/app/app.styl
@@ -1,29 +1,4 @@
-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
+@import "../style"
html
&.progress
@@ -96,17 +71,6 @@ body
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
diff --git a/src/web/app/base.pug b/src/web/app/base.pug
index 140286a768..d7c7f0aed4 100644
--- a/src/web/app/base.pug
+++ b/src/web/app/base.pug
@@ -24,6 +24,9 @@ html
//- FontAwesome style
style #{facss}
+ //- highlight.js style
+ style #{hljscss}
+
body
noscript: p
| JavaScriptを有効にしてください
diff --git a/src/web/app/common/scripts/api.ts b/src/web/app/common/scripts/api.ts
index 2008e6f5ac..bba838f56b 100644
--- a/src/web/app/common/scripts/api.ts
+++ b/src/web/app/common/scripts/api.ts
@@ -40,7 +40,7 @@ export default (i, endpoint, data = {}): Promise<{ [x: string]: any }> => {
} else {
res.json().then(err => {
reject(err.error);
- });
+ }, reject);
}
}).catch(reject);
});
diff --git a/src/web/app/common/scripts/parse-search-query.ts b/src/web/app/common/scripts/parse-search-query.ts
new file mode 100644
index 0000000000..512791ecb0
--- /dev/null
+++ b/src/web/app/common/scripts/parse-search-query.ts
@@ -0,0 +1,53 @@
+export default function(qs: string) {
+ const q = {
+ text: ''
+ };
+
+ qs.split(' ').forEach(x => {
+ if (/^([a-z_]+?):(.+?)$/.test(x)) {
+ const [key, value] = x.split(':');
+ switch (key) {
+ case 'user':
+ q['include_user_usernames'] = value.split(',');
+ break;
+ case 'exclude_user':
+ q['exclude_user_usernames'] = value.split(',');
+ break;
+ case 'follow':
+ q['following'] = value == 'null' ? null : value == 'true';
+ break;
+ case 'reply':
+ q['reply'] = value == 'null' ? null : value == 'true';
+ break;
+ case 'repost':
+ q['repost'] = value == 'null' ? null : value == 'true';
+ break;
+ case 'media':
+ q['media'] = value == 'null' ? null : value == 'true';
+ break;
+ case 'poll':
+ q['poll'] = value == 'null' ? null : value == 'true';
+ break;
+ case 'until':
+ case 'since':
+ // YYYY-MM-DD
+ if (/^[0-9]+\-[0-9]+\-[0-9]+$/) {
+ const [yyyy, mm, dd] = value.split('-');
+ q[`${key}_date`] = (new Date(parseInt(yyyy, 10), parseInt(mm, 10) - 1, parseInt(dd, 10))).getTime();
+ }
+ break;
+ default:
+ q[key] = value;
+ break;
+ }
+ } else {
+ q.text += x + ' ';
+ }
+ });
+
+ if (q.text) {
+ q.text = q.text.trim();
+ }
+
+ return q;
+}
diff --git a/src/web/app/common/tags/authorized-apps.tag b/src/web/app/common/tags/authorized-apps.tag
index 0078a18636..0594032de6 100644
--- a/src/web/app/common/tags/authorized-apps.tag
+++ b/src/web/app/common/tags/authorized-apps.tag
@@ -1,5 +1,7 @@
<mk-authorized-apps>
- <p class="none" if={ !fetching && apps.length == 0 }>%i18n:common.tags.mk-authorized-apps.no-apps%</p>
+ <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>
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/index.ts b/src/web/app/common/tags/index.ts
index 2f4e1181d4..df99d93cc5 100644
--- a/src/web/app/common/tags/index.ts
+++ b/src/web/app/common/tags/index.ts
@@ -12,7 +12,6 @@ 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');
diff --git a/src/web/app/common/tags/introduction.tag b/src/web/app/common/tags/introduction.tag
index 3256688d10..28afc6fa46 100644
--- a/src/web/app/common/tags/introduction.tag
+++ b/src/web/app/common/tags/introduction.tag
@@ -3,7 +3,7 @@
<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>
+ <p><a href={ _DOCS_URL_ } target="_blank">もっと知りたい方はこちら</a></p>
</article>
<style>
:scope
diff --git a/src/web/app/common/tags/messaging/room.tag b/src/web/app/common/tags/messaging/room.tag
index a149e1de22..7b4d1be569 100644
--- a/src/web/app/common/tags/messaging/room.tag
+++ b/src/web/app/common/tags/messaging/room.tag
@@ -254,7 +254,7 @@
this.api('messaging/messages', {
user_id: this.user.id,
limit: max + 1,
- max_id: this.moreMessagesIsInStock ? this.messages[0].id : undefined
+ until_id: this.moreMessagesIsInStock ? this.messages[0].id : undefined
}).then(messages => {
if (messages.length == max + 1) {
this.moreMessagesIsInStock = true;
diff --git a/src/web/app/common/tags/nav-links.tag b/src/web/app/common/tags/nav-links.tag
index 71f0453db0..ea122575aa 100644
--- a/src/web/app/common/tags/nav-links.tag
+++ b/src/web/app/common/tags/nav-links.tag
@@ -1,7 +1,10 @@
<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>
+ <a href={ aboutUrl }>%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>
+ <script>
+ this.aboutUrl = `${_DOCS_URL_}/${_LANG_}/about`;
+ </script>
</mk-nav-links>
diff --git a/src/web/app/common/tags/signin-history.tag b/src/web/app/common/tags/signin-history.tag
index 03afd72326..cdd58c4c67 100644
--- a/src/web/app/common/tags/signin-history.tag
+++ b/src/web/app/common/tags/signin-history.tag
@@ -1,55 +1,11 @@
<mk-signin-history>
<div class="records" if={ history.length != 0 }>
- <div each={ history }>
- <mk-time time={ created_at }/>
- <header>
- <virtual if={ success }>%fa:check%</virtual>
- <virtual if={ !success }>%fa:times%</virtual>
- <span class="ip">{ ip }</span>
- </header>
- <pre><code>{ JSON.stringify(headers, null, ' ') }</code></pre>
- </div>
+ <mk-signin-record each={ rec in history } rec={ rec }/>
</div>
<style>
:scope
display block
- > .records
- > div
- padding 16px 0 0 0
- border-bottom solid 1px #eee
-
- > header
-
- > [data-fa]
- margin-right 8px
-
- &.check
- color #0fda82
-
- &.times
- color #ff3100
-
- > .ip
- display inline-block
- color #444
- background #f8f8f8
-
- > mk-time
- position absolute
- top 16px
- right 0
- color #777
-
- > pre
- overflow auto
- max-height 100px
-
- > code
- white-space pre-wrap
- word-break break-all
- color #4a535a
-
</style>
<script>
this.mixin('i');
@@ -84,3 +40,77 @@
};
</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
index f25d99974b..f5a2be94ed 100644
--- a/src/web/app/common/tags/signin.tag
+++ b/src/web/app/common/tags/signin.tag
@@ -6,6 +6,9 @@
<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>
@@ -39,6 +42,7 @@
input[type=text]
input[type=password]
+ input[type=number]
user-select text
display inline-block
cursor auto
@@ -123,6 +127,10 @@
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
@@ -130,7 +138,8 @@
this.api('signin', {
username: this.refs.username.value,
- password: this.refs.password.value
+ password: this.refs.password.value,
+ token: this.user && this.user.two_factor_enabled ? this.refs.token.value : undefined
}).then(() => {
location.reload();
}).catch(() => {
diff --git a/src/web/app/common/tags/signup.tag b/src/web/app/common/tags/signup.tag
index 4816fe66db..b488efb927 100644
--- a/src/web/app/common/tags/signup.tag
+++ b/src/web/app/common/tags/signup.tag
@@ -34,7 +34,7 @@
</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>
+ <p><a href={ touUrl } target="_blank">利用規約</a>に同意する</p>
</label>
<button onclick={ onsubmit }>%i18n:common.tags.mk-signup.create%</button>
</form>
@@ -182,6 +182,8 @@
this.passwordRetypeState = null;
this.recaptchaed = false;
+ this.aboutUrl = `${_DOCS_URL_}/${_LANG_}/tou`;
+
window.onRecaptchaed = () => {
this.recaptchaed = true;
this.update();
diff --git a/src/web/app/common/tags/twitter-setting.tag b/src/web/app/common/tags/twitter-setting.tag
index 3b70505ba2..4d57cfa55a 100644
--- a/src/web/app/common/tags/twitter-setting.tag
+++ b/src/web/app/common/tags/twitter-setting.tag
@@ -1,5 +1,5 @@
<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>%i18n:common.tags.mk-twitter-setting.description%<a href={ _DOCS_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>
diff --git a/src/web/app/desktop/router.ts b/src/web/app/desktop/router.ts
index 27b63ab2ef..ce68c4f2d1 100644
--- a/src/web/app/desktop/router.ts
+++ b/src/web/app/desktop/router.ts
@@ -16,7 +16,7 @@ export default (mios: MiOS) => {
route('/i/messaging/:user', messaging);
route('/i/mentions', mentions);
route('/post::post', post);
- route('/search::query', search);
+ route('/search', search);
route('/:user', user.bind(null, 'home'));
route('/:user/graphs', user.bind(null, 'graphs'));
route('/:user/:post', post);
@@ -47,7 +47,7 @@ export default (mios: MiOS) => {
function search(ctx) {
const el = document.createElement('mk-search-page');
- el.setAttribute('query', ctx.params.query);
+ el.setAttribute('query', ctx.querystring.substr(2));
mount(el);
}
diff --git a/src/web/app/desktop/style.styl b/src/web/app/desktop/style.styl
index d99e5df2b4..c893e2ed67 100644
--- a/src/web/app/desktop/style.styl
+++ b/src/web/app/desktop/style.styl
@@ -2,6 +2,8 @@
@import "../reset"
@import "../../../../node_modules/cropperjs/dist/cropper.css"
+@import "./ui"
+
*::input-placeholder
color #D8CBC5
@@ -47,66 +49,3 @@ html
#wait
right auto
left 15px
-
-button
- font-family sans-serif
-
- *
- pointer-events none
-
- &.style-normal
- &.style-primary
- display block
- cursor pointer
- padding 0 16px
- margin 0
- min-width 100px
- 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
-
- &.style-normal
- 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-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
diff --git a/src/web/app/desktop/tags/drive/browser.tag b/src/web/app/desktop/tags/drive/browser.tag
index 901daabfd8..a60a46b790 100644
--- a/src/web/app/desktop/tags/drive/browser.tag
+++ b/src/web/app/desktop/tags/drive/browser.tag
@@ -18,14 +18,16 @@
<virtual each={ folder in folders }>
<mk-drive-browser-folder class="folder" folder={ folder }/>
</virtual>
- <div class="padding" each={ folders }></div>
+ <!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
+ <div class="padding" each={ Array(10).fill(16) }></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>
+ <!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
+ <div class="padding" each={ Array(10).fill(16) }></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 }>
diff --git a/src/web/app/desktop/tags/drive/file.tag b/src/web/app/desktop/tags/drive/file.tag
index 0f019d95bf..8b3d36b3f3 100644
--- a/src/web/app/desktop/tags/drive/file.tag
+++ b/src/web/app/desktop/tags/drive/file.tag
@@ -5,7 +5,9 @@
<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>
+ <div class="thumbnail" ref="thumbnail" style="background-color:{ file.properties.average_color ? 'rgb(' + file.properties.average_color.join(',') + ')' : 'transparent' }">
+ <img src={ file.url + '?thumbnail&size=128' } alt="" onload={ onload }/>
+ </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
@@ -139,6 +141,7 @@
</style>
<script>
+ import anime from 'animejs';
import bytesToSize from '../../../common/scripts/bytes-to-size';
this.mixin('i');
@@ -199,5 +202,16 @@
this.isDragging = false;
this.browser.isDragSource = false;
};
+
+ this.onload = () => {
+ if (this.file.properties.average_color) {
+ anime({
+ targets: this.refs.thumbnail,
+ backgroundColor: `rgba(${this.file.properties.average_color.join(',')}, 0)`,
+ duration: 100,
+ easing: 'linear'
+ });
+ }
+ };
</script>
</mk-drive-browser-file>
diff --git a/src/web/app/desktop/tags/home-widgets/mentions.tag b/src/web/app/desktop/tags/home-widgets/mentions.tag
index a48c7239a1..2687283079 100644
--- a/src/web/app/desktop/tags/home-widgets/mentions.tag
+++ b/src/web/app/desktop/tags/home-widgets/mentions.tag
@@ -101,7 +101,7 @@
});
this.api('posts/mentions', {
following: this.mode == 'following',
- max_id: this.refs.timeline.tail().id
+ until_id: this.refs.timeline.tail().id
}).then(posts => {
this.update({
moreLoading: false
diff --git a/src/web/app/desktop/tags/home-widgets/timeline.tag b/src/web/app/desktop/tags/home-widgets/timeline.tag
index 4c58aa4aa8..9571b09f34 100644
--- a/src/web/app/desktop/tags/home-widgets/timeline.tag
+++ b/src/web/app/desktop/tags/home-widgets/timeline.tag
@@ -86,7 +86,7 @@
});
this.api('posts/timeline', {
- max_date: this.date ? this.date.getTime() : undefined
+ until_date: this.date ? this.date.getTime() : undefined
}).then(posts => {
this.update({
isLoading: false,
@@ -103,7 +103,7 @@
moreLoading: true
});
this.api('posts/timeline', {
- max_id: this.refs.timeline.tail().id
+ until_id: this.refs.timeline.tail().id
}).then(posts => {
this.update({
moreLoading: false
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/images.tag b/src/web/app/desktop/tags/images.tag
new file mode 100644
index 0000000000..0cd408576f
--- /dev/null
+++ b/src/web/app/desktop/tags/images.tag
@@ -0,0 +1,172 @@
+<mk-images>
+ <virtual each={ image in images }>
+ <mk-images-image image={ image }/>
+ </virtual>
+ <style>
+ :scope
+ display grid
+ grid-gap 4px
+ height 256px
+ </style>
+ <script>
+ this.images = this.opts.images;
+
+ this.on('mount', () => {
+ if (this.images.length == 1) {
+ this.root.style.gridTemplateRows = '1fr';
+
+ this.tags['mk-images-image'].root.style.gridColumn = '1 / 2';
+ this.tags['mk-images-image'].root.style.gridRow = '1 / 2';
+ } else if (this.images.length == 2) {
+ this.root.style.gridTemplateColumns = '1fr 1fr';
+ this.root.style.gridTemplateRows = '1fr';
+
+ this.tags['mk-images-image'][0].root.style.gridColumn = '1 / 2';
+ this.tags['mk-images-image'][0].root.style.gridRow = '1 / 2';
+ this.tags['mk-images-image'][1].root.style.gridColumn = '2 / 3';
+ this.tags['mk-images-image'][1].root.style.gridRow = '1 / 2';
+ } else if (this.images.length == 3) {
+ this.root.style.gridTemplateColumns = '1fr 0.5fr';
+ this.root.style.gridTemplateRows = '1fr 1fr';
+
+ this.tags['mk-images-image'][0].root.style.gridColumn = '1 / 2';
+ this.tags['mk-images-image'][0].root.style.gridRow = '1 / 3';
+ this.tags['mk-images-image'][1].root.style.gridColumn = '2 / 3';
+ this.tags['mk-images-image'][1].root.style.gridRow = '1 / 2';
+ this.tags['mk-images-image'][2].root.style.gridColumn = '2 / 3';
+ this.tags['mk-images-image'][2].root.style.gridRow = '2 / 3';
+ } else if (this.images.length == 4) {
+ this.root.style.gridTemplateColumns = '1fr 1fr';
+ this.root.style.gridTemplateRows = '1fr 1fr';
+
+ this.tags['mk-images-image'][0].root.style.gridColumn = '1 / 2';
+ this.tags['mk-images-image'][0].root.style.gridRow = '1 / 2';
+ this.tags['mk-images-image'][1].root.style.gridColumn = '2 / 3';
+ this.tags['mk-images-image'][1].root.style.gridRow = '1 / 2';
+ this.tags['mk-images-image'][2].root.style.gridColumn = '1 / 2';
+ this.tags['mk-images-image'][2].root.style.gridRow = '2 / 3';
+ this.tags['mk-images-image'][3].root.style.gridColumn = '2 / 3';
+ this.tags['mk-images-image'][3].root.style.gridRow = '2 / 3';
+ }
+ });
+ </script>
+</mk-images>
+
+<mk-images-image>
+ <a ref="view"
+ href={ image.url }
+ onmousemove={ mousemove }
+ onmouseleave={ mouseleave }
+ style={ styles }
+ onclick={ click }
+ title={ image.name }></a>
+ <style>
+ :scope
+ display block
+ overflow hidden
+ border-radius 4px
+
+ > a
+ display block
+ cursor zoom-in
+ overflow hidden
+ width 100%
+ height 100%
+ background-position center
+
+ &:not(:hover)
+ background-size cover
+
+ </style>
+ <script>
+ this.image = this.opts.image;
+ this.styles = {
+ 'background-color': this.image.properties.average_color ? `rgb(${this.image.properties.average_color.join(',')})` : 'transparent',
+ 'background-image': `url(${this.image.url}?thumbnail&size=512)`
+ };
+
+ 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.refs.view.style.backgroundImage = 'url("' + this.image.url + '?thumbnail")';
+ };
+
+ this.mouseleave = () => {
+ this.refs.view.style.backgroundPosition = '';
+ };
+
+ this.click = ev => {
+ ev.preventDefault();
+ riot.mount(document.body.appendChild(document.createElement('mk-image-dialog')), {
+ image: this.image
+ });
+ return false;
+ };
+ </script>
+</mk-images-image>
+
+<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/index.ts b/src/web/app/desktop/tags/index.ts
index 3ec1d108aa..4edda83534 100644
--- a/src/web/app/desktop/tags/index.ts
+++ b/src/web/app/desktop/tags/index.ts
@@ -76,8 +76,7 @@ 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('./images.tag');
require('./donation.tag');
require('./users-list.tag');
require('./user-following.tag');
diff --git a/src/web/app/desktop/tags/notifications.tag b/src/web/app/desktop/tags/notifications.tag
index 3218c00f6a..39862487e9 100644
--- a/src/web/app/desktop/tags/notifications.tag
+++ b/src/web/app/desktop/tags/notifications.tag
@@ -283,7 +283,7 @@
this.api('i/notifications', {
limit: max + 1,
- max_id: this.notifications[this.notifications.length - 1].id
+ until_id: this.notifications[this.notifications.length - 1].id
}).then(notifications => {
if (notifications.length == max + 1) {
this.moreNotifications = true;
diff --git a/src/web/app/desktop/tags/pages/entrance.tag b/src/web/app/desktop/tags/pages/entrance.tag
index 44548e4183..974f49a4fe 100644
--- a/src/web/app/desktop/tags/pages/entrance.tag
+++ b/src/web/app/desktop/tags/pages/entrance.tag
@@ -18,7 +18,7 @@
<footer>
<div>
<mk-nav-links/>
- <mk-copyright/>
+ <p class="c">{ _COPYRIGHT_ }</p>
</div>
</footer>
<!-- ↓ https://github.com/riot/riot/issues/2134 (将来的)-->
@@ -101,7 +101,7 @@
text-align center
border-top solid 1px #fff
- > mk-copyright
+ > .c
margin 0
line-height 64px
font-size 10px
@@ -150,7 +150,7 @@
</mk-entrance>
<mk-entrance-signin>
- <a class="help" href={ _ABOUT_URL_ + '/help' } title="お困りですか?">%fa:question%</a>
+ <a class="help" href={ _DOCS_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>
diff --git a/src/web/app/desktop/tags/post-detail-sub.tag b/src/web/app/desktop/tags/post-detail-sub.tag
index e22386df91..cccd85c474 100644
--- a/src/web/app/desktop/tags/post-detail-sub.tag
+++ b/src/web/app/desktop/tags/post-detail-sub.tag
@@ -9,7 +9,7 @@
<span class="username">@{ post.user.username }</span>
</div>
<div class="right">
- <a class="time" href={ '/' + this.post.user.username + '/' + this.post.id }>
+ <a class="time" href={ '/' + post.user.username + '/' + post.id }>
<mk-time time={ post.created_at }/>
</a>
</div>
@@ -17,9 +17,7 @@
<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>
+ <mk-images images={ post.media }/>
</div>
</div>
</div>
@@ -107,11 +105,6 @@
> mk-url-preview
margin-top 8px
- > .media
- > img
- display block
- max-width 100%
-
</style>
<script>
import compile from '../../common/scripts/text-compiler';
diff --git a/src/web/app/desktop/tags/post-detail.tag b/src/web/app/desktop/tags/post-detail.tag
index 37f90a6ffb..47c71a6c12 100644
--- a/src/web/app/desktop/tags/post-detail.tag
+++ b/src/web/app/desktop/tags/post-detail.tag
@@ -37,7 +37,7 @@
<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>
+ <mk-images images={ p.media }/>
</div>
<mk-poll if={ p.poll } post={ p }/>
</div>
@@ -208,11 +208,6 @@
> mk-url-preview
margin-top 8px
- > .media
- > img
- display block
- max-width 100%
-
> footer
font-size 1.2em
diff --git a/src/web/app/desktop/tags/post-form.tag b/src/web/app/desktop/tags/post-form.tag
index 8e5171c83e..0b4c07906a 100644
--- a/src/web/app/desktop/tags/post-form.tag
+++ b/src/web/app/desktop/tags/post-form.tag
@@ -1,13 +1,12 @@
<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="medias { with: poll }" show={ files.length != 0 }>
+ <ul ref="media">
+ <li each={ files } data-id={ id }>
<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>
@@ -118,8 +117,9 @@
> li
display block
float left
- margin 4px
+ margin 0
padding 0
+ border solid 4px transparent
cursor move
&:hover > .remove
@@ -140,29 +140,6 @@
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)
@@ -306,6 +283,7 @@
</style>
<script>
+ import Sortable from 'sortablejs';
import getKao from '../../common/scripts/get-kao';
import notify from '../scripts/notify';
import Autocomplete from '../scripts/autocomplete';
@@ -365,6 +343,10 @@
this.trigger('change-files', this.files);
this.update();
}
+
+ new Sortable(this.refs.media, {
+ animation: 150
+ });
});
this.on('unmount', () => {
@@ -413,14 +395,17 @@
const data = e.dataTransfer.getData('text');
if (data == null) return false;
- // パース
- // TODO: Validate JSON
- const obj = JSON.parse(data);
+ try {
+ // パース
+ const obj = JSON.parse(data);
+
+ // (ドライブの)ファイルだったら
+ if (obj.type == 'file') {
+ this.files.push(obj.file);
+ this.update();
+ }
+ } catch (e) {
- // (ドライブの)ファイルだったら
- if (obj.type == 'file') {
- this.files.push(obj.file);
- this.update();
}
};
@@ -483,13 +468,19 @@
this.post = e => {
this.wait = true;
- const files = this.files && this.files.length > 0
- ? this.files.map(f => f.id)
- : undefined;
+ const files = [];
+
+ if (this.files.length > 0) {
+ Array.from(this.refs.media.children).forEach(el => {
+ const id = el.getAttribute('data-id');
+ const file = this.files.find(f => f.id == id);
+ files.push(file);
+ });
+ }
this.api('posts/create', {
text: this.refs.text.value == '' ? undefined : this.refs.text.value,
- media_ids: files,
+ media_ids: this.files.length > 0 ? files.map(f => f.id) : undefined,
reply_id: this.inReplyToPost ? this.inReplyToPost.id : undefined,
repost_id: this.repost ? this.repost.id : undefined,
poll: this.poll ? this.refs.poll.get() : undefined
diff --git a/src/web/app/desktop/tags/search-posts.tag b/src/web/app/desktop/tags/search-posts.tag
index 52f765d1a1..f7ec85a4fe 100644
--- a/src/web/app/desktop/tags/search-posts.tag
+++ b/src/web/app/desktop/tags/search-posts.tag
@@ -33,21 +33,22 @@
</style>
<script>
+ import parse from '../../common/scripts/parse-search-query';
+
this.mixin('api');
this.query = this.opts.query;
this.isLoading = true;
this.isEmpty = false;
this.moreLoading = false;
- this.page = 0;
+ this.limit = 30;
+ this.offset = 0;
this.on('mount', () => {
document.addEventListener('keydown', this.onDocumentKeydown);
window.addEventListener('scroll', this.onScroll);
- this.api('posts/search', {
- query: this.query
- }).then(posts => {
+ this.api('posts/search', parse(this.query)).then(posts => {
this.update({
isLoading: false,
isEmpty: posts.length == 0
@@ -72,16 +73,16 @@
this.more = () => {
if (this.moreLoading || this.isLoading || this.timeline.posts.length == 0) return;
+ this.offset += this.limit;
this.update({
moreLoading: true
});
- this.api('posts/search', {
- query: this.query,
- page: this.page + 1
- }).then(posts => {
+ return this.api('posts/search', Object.assign({}, parse(this.query), {
+ limit: this.limit,
+ offset: this.offset
+ })).then(posts => {
this.update({
- moreLoading: false,
- page: page + 1
+ moreLoading: false
});
this.refs.timeline.prependPosts(posts);
});
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
index 17cc607988..c660a2fe90 100644
--- a/src/web/app/desktop/tags/select-file-from-drive-window.tag
+++ b/src/web/app/desktop/tags/select-file-from-drive-window.tag
@@ -33,7 +33,7 @@
height 72px
background lighten($theme-color, 95%)
- .upload
+ > .upload
display inline-block
position absolute
top 8px
@@ -72,8 +72,8 @@
border 2px solid rgba($theme-color, 0.3)
border-radius 8px
- .ok
- .cancel
+ > .ok
+ > .cancel
display block
position absolute
bottom 16px
@@ -102,7 +102,7 @@
opacity 0.7
cursor default
- .ok
+ > .ok
right 16px
color $theme-color-foreground
background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%)
@@ -119,7 +119,7 @@
background $theme-color
border-color $theme-color
- .cancel
+ > .cancel
right 148px
color #888
background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%)
diff --git a/src/web/app/desktop/tags/settings.tag b/src/web/app/desktop/tags/settings.tag
index 46cd405520..457b7e2276 100644
--- a/src/web/app/desktop/tags/settings.tag
+++ b/src/web/app/desktop/tags/settings.tag
@@ -1,47 +1,35 @@
<mk-settings>
<div class="nav">
- <p class={ active: page == 'account' } onmousedown={ setPage.bind(null, 'account') }>%fa:user .fw%アカウント</p>
+ <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%ドライブ</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 == 'mute' } onmousedown={ setPage.bind(null, 'mute') }>%fa:ban .fw%%i18n:desktop.tags.mk-settings.mute%</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 == 'signin' } onmousedown={ setPage.bind(null, 'signin') }>%fa:sign-in-alt .fw%ログイン履歴</p>
- <p class={ active: page == 'password' } onmousedown={ setPage.bind(null, 'password') }>%fa:unlock-alt .fw%%i18n:desktop.tags.mk-settings.password%</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>
+ <p class={ active: page == 'other' } onmousedown={ setPage.bind(null, 'other') }>%fa:cogs .fw%%i18n:desktop.tags.mk-settings.other%</p>
</div>
<div class="pages">
- <section class="account" show={ page == 'account' }>
- <h1>アカウント</h1>
- <label class="avatar">
- <p>アバター</p><img class="avatar" src={ I.avatar_url + '?thumbnail&size=64' } alt="avatar"/>
- <button class="style-normal" onclick={ avatar }>画像を選択</button>
- </label>
- <label>
- <p>名前</p>
- <input ref="accountName" type="text" value={ I.name }/>
- </label>
- <label>
- <p>場所</p>
- <input ref="accountLocation" type="text" value={ I.profile.location }/>
- </label>
- <label>
- <p>自己紹介</p>
- <textarea ref="accountDescription">{ I.description }</textarea>
- </label>
- <label>
- <p>誕生日</p>
- <input ref="accountBirthday" type="date" value={ I.profile.birthday }/>
- </label>
- <button class="style-primary" onclick={ updateAccount }>保存</button>
+ <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">ホームをカスタマイズ</a>
+ <a href="/i/customize-home" class="ui button">ホームをカスタマイズ</a>
</section>
- <section class="web" show={ page == 'web' }>
+ <section class="drive" show={ page == 'drive' }>
+ <h1>%i18n:desktop.tags.mk-settings.drive%</h1>
+ <mk-drive-setting/>
+ </section>
+
+ <section class="mute" show={ page == 'mute' }>
+ <h1>%i18n:desktop.tags.mk-settings.mute%</h1>
+ <mk-mute-setting/>
</section>
<section class="apps" show={ page == 'apps' }>
@@ -54,20 +42,30 @@
<mk-twitter-setting/>
</section>
- <section class="signin" show={ page == 'signin' }>
- <h1>ログイン履歴</h1>
- <mk-signin-history/>
- </section>
-
- <section class="password" show={ page == 'password' }>
+ <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>
+
+ <section class="other" show={ page == 'other' }>
+ <h1>%i18n:desktop.tags.mk-settings.license%</h1>
+ %license%
+ </section>
</div>
<style>
:scope
@@ -75,25 +73,6 @@
width 100%
height 100%
- input:not([type])
- input[type='text']
- input[type='password']
- input[type='email']
- input[type='date']
- textarea
- padding 8px
- width 100%
- font-size 16px
- color #55595c
- border solid 1px #dadada
- border-radius 4px
-
- &:hover
- border-color #aeaeae
-
- &:focus
- border-color #aeaeae
-
> .nav
flex 0 0 200px
width 100%
@@ -128,64 +107,63 @@
overflow auto
> section
- padding 32px
+ margin 32px
+ color #4a535a
- // & + section
- // margin-top 16px
-
- h1
+ > h1
display block
- margin 0
+ margin 0 0 1em 0
padding 0 0 8px 0
font-size 1em
color #555
border-bottom solid 1px #eee
- label
- display block
- margin 16px 0
-
- &:after
- content ""
- display block
- clear both
-
- > p
- margin 0 0 8px 0
- font-weight bold
- color #373a3c
-
- &.checkbox
- > input
- position absolute
- top 0
- left 0
-
- &:checked + p
- color $theme-color
+ </style>
+ <script>
+ this.page = 'profile';
- > p
- width calc(100% - 32px)
- margin 0 0 0 32px
- font-weight bold
+ this.setPage = page => {
+ this.page = page;
+ };
+ </script>
+</mk-settings>
- &:last-child
- font-weight normal
- color #999
+<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
- &.account
- > .general
- > .avatar
- > img
- display block
- float left
- width 64px
- height 64px
- border-radius 4px
+ > .avatar
+ > img
+ display inline-block
+ vertical-align top
+ width 64px
+ height 64px
+ border-radius 4px
- > button
- float left
- margin-left 8px
+ > button
+ margin-left 8px
</style>
<script>
@@ -195,12 +173,6 @@
this.mixin('i');
this.mixin('api');
- this.page = 'account';
-
- this.setPage = page => {
- this.page = page;
- };
-
this.avatar = () => {
updateAvatar(this.I);
};
@@ -216,21 +188,25 @@
});
};
</script>
-</mk-settings>
+</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>
+ <p>Token: <code>{ I.token }</code></p>
+ <p>%i18n:desktop.tags.mk-api-info.intro%</p>
+ <div class="ui info warn"><p>%fa:exclamation-triangle%%i18n:desktop.tags.mk-api-info.caution%</p></div>
+ <p>%i18n:desktop.tags.mk-api-info.regeneration-of-token%</p>
+ <button class="ui" onclick={ regenerateToken }>%i18n:desktop.tags.mk-api-info.regenerate-token%</button>
<style>
:scope
display block
color #4a535a
code
- padding 4px
+ display inline-block
+ padding 4px 6px
+ color #555
background #eee
+ border-radius 2px
</style>
<script>
import passwordDialog from '../scripts/password-dialog';
@@ -239,7 +215,7 @@
this.mixin('api');
this.regenerateToken = () => {
- passwordDialog('%i18n:desktop.tags.mk-api-info.regenerate-token%', password => {
+ passwordDialog('%i18n:desktop.tags.mk-api-info.enter-password%', password => {
this.api('i/regenerate_token', {
password: password
});
@@ -249,7 +225,7 @@
</mk-api-info>
<mk-password-setting>
- <button onclick={ reset }>%i18n:desktop.tags.mk-password-setting.reset%</button>
+ <button onclick={ reset } class="ui primary">%i18n:desktop.tags.mk-password-setting.reset%</button>
<style>
:scope
display block
@@ -285,3 +261,166 @@
};
</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>
+
+<mk-mute-setting>
+ <div class="none ui info" if={ !fetching && users.length == 0 }>
+ <p>%fa:info-circle%%i18n:desktop.tags.mk-mute-setting.no-users%</p>
+ </div>
+ <div class="users" if={ users.length != 0 }>
+ <div each={ user in users }>
+ <p><b>{ user.name }</b> @{ user.username }</p>
+ </div>
+ </div>
+
+ <style>
+ :scope
+ display block
+
+ </style>
+ <script>
+ this.mixin('api');
+
+ this.apps = [];
+ this.fetching = true;
+
+ this.on('mount', () => {
+ this.api('mute/list').then(x => {
+ this.update({
+ fetching: false,
+ users: x.users
+ });
+ });
+ });
+ </script>
+</mk-mute-setting>
diff --git a/src/web/app/desktop/tags/sub-post-content.tag b/src/web/app/desktop/tags/sub-post-content.tag
index 8989ff1c5b..1a81b545b6 100644
--- a/src/web/app/desktop/tags/sub-post-content.tag
+++ b/src/web/app/desktop/tags/sub-post-content.tag
@@ -8,7 +8,7 @@
</div>
<details if={ post.media }>
<summary>({ post.media.length }つのメディア)</summary>
- <mk-images-viewer images={ post.media }/>
+ <mk-images images={ post.media }/>
</details>
<details if={ post.poll }>
<summary>投票</summary>
diff --git a/src/web/app/desktop/tags/timeline.tag b/src/web/app/desktop/tags/timeline.tag
index 08e658a3c6..ed77a9e608 100644
--- a/src/web/app/desktop/tags/timeline.tag
+++ b/src/web/app/desktop/tags/timeline.tag
@@ -120,7 +120,7 @@
<a class="quote" if={ p.repost != null }>RP:</a>
</div>
<div class="media" if={ p.media }>
- <mk-images-viewer images={ p.media }/>
+ <mk-images images={ p.media }/>
</div>
<mk-poll if={ p.poll } post={ p } ref="pollViewer"/>
<div class="repost" if={ p.repost }>%fa:quote-right -flip-h%
@@ -357,11 +357,6 @@
background $theme-color
border-radius 4px
- > .media
- > img
- display block
- max-width 100%
-
> mk-poll
font-size 80%
diff --git a/src/web/app/desktop/tags/ui.tag b/src/web/app/desktop/tags/ui.tag
index 052568062a..3dfdeec01c 100644
--- a/src/web/app/desktop/tags/ui.tag
+++ b/src/web/app/desktop/tags/ui.tag
@@ -146,6 +146,9 @@
color #9eaba8
pointer-events none
+ > *
+ vertical-align middle
+
> input
user-select text
cursor auto
@@ -162,7 +165,7 @@
transition color 0.5s ease, border 0.5s ease
font-family FontAwesome, sans-serif
- &:placeholder-shown
+ &::placeholder
color #9eaba8
&:hover
@@ -177,7 +180,7 @@
this.onsubmit = e => {
e.preventDefault();
- this.page('/search:' + this.refs.q.value);
+ this.page('/search?q=' + encodeURIComponent(this.refs.q.value));
};
</script>
</mk-ui-header-search>
diff --git a/src/web/app/desktop/tags/user-timeline.tag b/src/web/app/desktop/tags/user-timeline.tag
index 2b05f6b5cf..134aeee28c 100644
--- a/src/web/app/desktop/tags/user-timeline.tag
+++ b/src/web/app/desktop/tags/user-timeline.tag
@@ -96,7 +96,7 @@
this.fetch = cb => {
this.api('users/posts', {
user_id: this.user.id,
- max_date: this.date ? this.date.getTime() : undefined,
+ until_date: this.date ? this.date.getTime() : undefined,
with_replies: this.mode == 'with-replies'
}).then(posts => {
this.update({
@@ -116,7 +116,7 @@
this.api('users/posts', {
user_id: this.user.id,
with_replies: this.mode == 'with-replies',
- max_id: this.refs.timeline.tail().id
+ until_id: this.refs.timeline.tail().id
}).then(posts => {
this.update({
moreLoading: false
diff --git a/src/web/app/desktop/tags/user.tag b/src/web/app/desktop/tags/user.tag
index b4db47f9dd..b29d1eaebc 100644
--- a/src/web/app/desktop/tags/user.tag
+++ b/src/web/app/desktop/tags/user.tag
@@ -226,7 +226,9 @@
<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>
+ <p class="followed" if={ user.is_followed }>%i18n:desktop.tags.mk-user.follows-you%</p>
+ <p if={ user.is_muted }>%i18n:desktop.tags.mk-user.muted% <a onclick={ unmute }>%i18n:desktop.tags.mk-user.unmute%</a></p>
+ <p if={ !user.is_muted }><a onclick={ mute }>%i18n:desktop.tags.mk-user.mute%</a></p>
</div>
<div class="description" if={ user.description }>{ user.description }</div>
<div class="birthday" if={ user.profile.birthday }>
@@ -311,6 +313,7 @@
this.age = require('s-age');
this.mixin('i');
+ this.mixin('api');
this.user = this.opts.user;
@@ -325,6 +328,28 @@
user: this.user
});
};
+
+ this.mute = () => {
+ this.api('mute/create', {
+ user_id: this.user.id
+ }).then(() => {
+ this.user.is_muted = true;
+ this.update();
+ }, e => {
+ alert('error');
+ });
+ };
+
+ this.unmute = () => {
+ this.api('mute/delete', {
+ user_id: this.user.id
+ }).then(() => {
+ this.user.is_muted = false;
+ this.update();
+ }, e => {
+ alert('error');
+ });
+ };
</script>
</mk-user-profile>
diff --git a/src/web/app/desktop/ui.styl b/src/web/app/desktop/ui.styl
new file mode 100644
index 0000000000..058271876b
--- /dev/null
+++ b/src/web/app/desktop/ui.styl
@@ -0,0 +1,122 @@
+@import "../../const"
+
+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/mobile/router.ts b/src/web/app/mobile/router.ts
index 0358d10e9e..afb9aa6201 100644
--- a/src/web/app/mobile/router.ts
+++ b/src/web/app/mobile/router.ts
@@ -19,12 +19,11 @@ export default (mios: MiOS) => {
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('/search', search);
route('/:user', user.bind(null, 'overview'));
route('/:user/graphs', user.bind(null, 'graphs'));
route('/:user/followers', userFollowers);
@@ -74,10 +73,6 @@ export default (mios: MiOS) => {
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'));
}
@@ -88,7 +83,7 @@ export default (mios: MiOS) => {
function search(ctx) {
const el = document.createElement('mk-search-page');
- el.setAttribute('query', ctx.params.query);
+ el.setAttribute('query', ctx.querystring.substr(2));
mount(el);
}
diff --git a/src/web/app/mobile/tags/drive.tag b/src/web/app/mobile/tags/drive.tag
index 8350ce07e1..2a3ff23bfa 100644
--- a/src/web/app/mobile/tags/drive.tag
+++ b/src/web/app/mobile/tags/drive.tag
@@ -1,9 +1,9 @@
<mk-drive>
<nav ref="nav">
- <p onclick={ goRoot }>%fa:cloud%%i18n:mobile.tags.mk-drive.drive%</p>
+ <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>
- <p onclick={ move }>{ folder.name }</p>
+ <a onclick={ move } href="/i/drive/folder/{ folder.id }">{ folder.name }</a>
</virtual>
<virtual if={ folder != null }>
<span>%fa:angle-right%</span>
@@ -74,9 +74,12 @@
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
@@ -245,7 +248,9 @@
};
this.move = ev => {
+ ev.preventDefault();
this.cd(ev.item.folder);
+ return false;
};
this.cd = (target, silent = false) => {
@@ -329,7 +334,9 @@
this.prependFile = file => this.addFile(file, true);
this.prependFolder = file => this.addFolder(file, true);
- this.goRoot = () => {
+ this.goRoot = ev => {
+ ev.preventDefault();
+
if (this.folder || this.file) {
this.update({
file: null,
@@ -339,6 +346,8 @@
this.trigger('move-root');
this.fetch();
}
+
+ return false;
};
this.fetch = () => {
@@ -421,7 +430,7 @@
this.api('drive/files', {
folder_id: this.folder ? this.folder.id : null,
limit: max + 1,
- max_id: this.files[this.files.length - 1].id
+ until_id: this.files[this.files.length - 1].id
}).then(files => {
if (files.length == max + 1) {
this.moreFiles = true;
diff --git a/src/web/app/mobile/tags/drive/file-viewer.tag b/src/web/app/mobile/tags/drive/file-viewer.tag
index da895359dc..259873d95c 100644
--- a/src/web/app/mobile/tags/drive/file-viewer.tag
+++ b/src/web/app/mobile/tags/drive/file-viewer.tag
@@ -1,6 +1,11 @@
<mk-drive-file-viewer>
<div class="preview">
- <img if={ kind == 'image' } src={ file.url } alt={ file.name } title={ file.name }>
+ <img if={ kind == 'image' } ref="img"
+ src={ file.url }
+ alt={ file.name }
+ title={ file.name }
+ onload={ onImageLoaded }
+ style="background-color:rgb({ file.properties.average_color.join(',') })">
<virtual if={ kind != 'image' }>%fa:file%</virtual>
<footer if={ kind == 'image' && file.properties && file.properties.width && file.properties.height }>
<span class="size">
@@ -39,6 +44,14 @@
</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>
@@ -178,12 +191,45 @@
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';
@@ -195,6 +241,17 @@
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;
diff --git a/src/web/app/mobile/tags/drive/file.tag b/src/web/app/mobile/tags/drive/file.tag
index 93a8dba7e5..684df7dd08 100644
--- a/src/web/app/mobile/tags/drive/file.tag
+++ b/src/web/app/mobile/tags/drive/file.tag
@@ -1,119 +1,123 @@
-<mk-drive-file onclick={ onclick } data-is-selected={ isSelected }>
- <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>
+<mk-drive-file data-is-selected={ isSelected }>
+ <a onclick={ onclick } href="/i/drive/file/{ file.id }">
+ <div class="container">
+ <div class="thumbnail" style={ thumbnail }></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>
- </div>
+ </a>
<style>
:scope
display block
- &, *
- user-select none
+ > a
+ display block
+ text-decoration none !important
- *
- pointer-events none
+ *
+ user-select none
+ pointer-events none
- > .container
- max-width 500px
- margin 0 auto
- padding 16px
+ > .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
+ &:after
+ content ""
display block
- margin 0
- padding 0
- font-size 0.9em
- font-weight bold
- color #555
- text-overflow ellipsis
- overflow-wrap break-word
+ clear both
- > .ext
- opacity 0.5
-
- > .tags
+ > .thumbnail
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
+ float left
+ width 64px
+ height 64px
+ background-size cover
+ background-position center center
- > footer
+ > .body
display block
- margin 4px 0 0 0
- font-size 0.7em
+ float left
+ width calc(100% - 74px)
+ margin-left 10px
- > .separator
- display inline
- margin 0
- padding 0 4px
- color #CDCDCD
-
- > .type
- display inline
+ > .name
+ display block
margin 0
padding 0
- color #9D9D9D
+ font-size 0.9em
+ font-weight bold
+ color #555
+ text-overflow ellipsis
+ overflow-wrap break-word
- > mk-file-type-icon
- margin-right 4px
+ > .ext
+ opacity 0.5
- > .data-size
- display inline
- margin 0
+ > .tags
+ display block
+ margin 4px 0 0 0
padding 0
- color #9D9D9D
+ list-style none
+ font-size 0.5em
- > .created-at
- display inline
- margin 0
- padding 0
- color #BDBDBD
+ > .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
- > [data-fa]
- margin-right 2px
+ > 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
@@ -128,14 +132,20 @@
this.browser = this.parent;
this.file = this.opts.file;
+ this.thumbnail = {
+ 'background-color': this.file.properties.average_color ? `rgb(${this.file.properties.average_color.join(',')})` : 'transparent',
+ 'background-image': `url(${this.file.url}?thumbnail&size=128)`
+ };
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 = () => {
+ 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
index 196e7e326b..6125e0b254 100644
--- a/src/web/app/mobile/tags/drive/folder.tag
+++ b/src/web/app/mobile/tags/drive/folder.tag
@@ -1,47 +1,53 @@
-<mk-drive-folder onclick={ onclick }>
- <div class="container">
- <p class="name">%fa:folder%{ folder.name }</p>%fa:angle-right%
- </div>
+<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
- color #777
- &, *
- user-select none
+ > a
+ display block
+ color #777
+ text-decoration none !important
- *
- pointer-events none
+ *
+ user-select none
+ pointer-events none
- > .container
- max-width 500px
- margin 0 auto
- padding 16px
+ > .container
+ max-width 500px
+ margin 0 auto
+ padding 16px
- > .name
- display block
- margin 0
- padding 0
+ > .name
+ display block
+ margin 0
+ padding 0
+
+ > [data-fa]
+ margin-right 6px
> [data-fa]
- margin-right 6px
+ position absolute
+ top 0
+ bottom 0
+ right 20px
- > [data-fa]
- position absolute
- top 0
- bottom 0
- right 8px
- margin auto 0 auto 0
- width 1em
- height 1em
+ > *
+ height 100%
</style>
<script>
this.browser = this.parent;
this.folder = this.opts.folder;
- this.onclick = () => {
+ this.onclick = ev => {
+ ev.preventDefault();
this.browser.cd(this.folder);
+ return false;
};
</script>
</mk-drive-folder>
diff --git a/src/web/app/mobile/tags/home-timeline.tag b/src/web/app/mobile/tags/home-timeline.tag
index e96823fa10..397d2b3980 100644
--- a/src/web/app/mobile/tags/home-timeline.tag
+++ b/src/web/app/mobile/tags/home-timeline.tag
@@ -47,7 +47,7 @@
this.more = () => {
return this.api('posts/timeline', {
- max_id: this.refs.timeline.tail().id
+ until_id: this.refs.timeline.tail().id
});
};
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/images.tag b/src/web/app/mobile/tags/images.tag
new file mode 100644
index 0000000000..5899364aef
--- /dev/null
+++ b/src/web/app/mobile/tags/images.tag
@@ -0,0 +1,82 @@
+<mk-images>
+ <virtual each={ image in images }>
+ <mk-images-image image={ image }/>
+ </virtual>
+ <style>
+ :scope
+ display grid
+ grid-gap 4px
+ height 256px
+
+ @media (max-width 500px)
+ height 192px
+ </style>
+ <script>
+ this.images = this.opts.images;
+
+ this.on('mount', () => {
+ if (this.images.length == 1) {
+ this.root.style.gridTemplateRows = '1fr';
+
+ this.tags['mk-images-image'].root.style.gridColumn = '1 / 2';
+ this.tags['mk-images-image'].root.style.gridRow = '1 / 2';
+ } else if (this.images.length == 2) {
+ this.root.style.gridTemplateColumns = '1fr 1fr';
+ this.root.style.gridTemplateRows = '1fr';
+
+ this.tags['mk-images-image'][0].root.style.gridColumn = '1 / 2';
+ this.tags['mk-images-image'][0].root.style.gridRow = '1 / 2';
+ this.tags['mk-images-image'][1].root.style.gridColumn = '2 / 3';
+ this.tags['mk-images-image'][1].root.style.gridRow = '1 / 2';
+ } else if (this.images.length == 3) {
+ this.root.style.gridTemplateColumns = '1fr 0.5fr';
+ this.root.style.gridTemplateRows = '1fr 1fr';
+
+ this.tags['mk-images-image'][0].root.style.gridColumn = '1 / 2';
+ this.tags['mk-images-image'][0].root.style.gridRow = '1 / 3';
+ this.tags['mk-images-image'][1].root.style.gridColumn = '2 / 3';
+ this.tags['mk-images-image'][1].root.style.gridRow = '1 / 2';
+ this.tags['mk-images-image'][2].root.style.gridColumn = '2 / 3';
+ this.tags['mk-images-image'][2].root.style.gridRow = '2 / 3';
+ } else if (this.images.length == 4) {
+ this.root.style.gridTemplateColumns = '1fr 1fr';
+ this.root.style.gridTemplateRows = '1fr 1fr';
+
+ this.tags['mk-images-image'][0].root.style.gridColumn = '1 / 2';
+ this.tags['mk-images-image'][0].root.style.gridRow = '1 / 2';
+ this.tags['mk-images-image'][1].root.style.gridColumn = '2 / 3';
+ this.tags['mk-images-image'][1].root.style.gridRow = '1 / 2';
+ this.tags['mk-images-image'][2].root.style.gridColumn = '1 / 2';
+ this.tags['mk-images-image'][2].root.style.gridRow = '2 / 3';
+ this.tags['mk-images-image'][3].root.style.gridColumn = '2 / 3';
+ this.tags['mk-images-image'][3].root.style.gridRow = '2 / 3';
+ }
+ });
+ </script>
+</mk-images>
+
+<mk-images-image>
+ <a ref="view" href={ image.url } target="_blank" style={ styles } title={ image.name }></a>
+ <style>
+ :scope
+ display block
+ overflow hidden
+ border-radius 4px
+
+ > a
+ display block
+ overflow hidden
+ width 100%
+ height 100%
+ background-position center
+ background-size cover
+
+ </style>
+ <script>
+ this.image = this.opts.image;
+ this.styles = {
+ 'background-color': this.image.properties.average_color ? `rgb(${this.image.properties.average_color.join(',')})` : 'transparent',
+ 'background-image': `url(${this.image.url}?thumbnail&size=512)`
+ };
+ </script>
+</mk-images-image>
diff --git a/src/web/app/mobile/tags/index.ts b/src/web/app/mobile/tags/index.ts
index 19952c20cd..20934cdd8d 100644
--- a/src/web/app/mobile/tags/index.ts
+++ b/src/web/app/mobile/tags/index.ts
@@ -14,7 +14,6 @@ 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');
@@ -25,7 +24,7 @@ require('./home-timeline.tag');
require('./timeline.tag');
require('./post-preview.tag');
require('./sub-post-content.tag');
-require('./images-viewer.tag');
+require('./images.tag');
require('./drive.tag');
require('./drive-selector.tag');
require('./drive-folder-selector.tag');
diff --git a/src/web/app/mobile/tags/notifications.tag b/src/web/app/mobile/tags/notifications.tag
index c3500d1b84..742cc45145 100644
--- a/src/web/app/mobile/tags/notifications.tag
+++ b/src/web/app/mobile/tags/notifications.tag
@@ -146,7 +146,7 @@
this.api('i/notifications', {
limit: max + 1,
- max_id: this.notifications[this.notifications.length - 1].id
+ until_id: this.notifications[this.notifications.length - 1].id
}).then(notifications => {
if (notifications.length == max + 1) {
this.moreNotifications = true;
diff --git a/src/web/app/mobile/tags/page/entrance.tag b/src/web/app/mobile/tags/page/entrance.tag
index 380fb780bc..191874caf9 100644
--- a/src/web/app/mobile/tags/page/entrance.tag
+++ b/src/web/app/mobile/tags/page/entrance.tag
@@ -8,7 +8,7 @@
</div>
</main>
<footer>
- <mk-copyright/>
+ <p class="c">{ _COPYRIGHT_ }</p>
</footer>
<style>
:scope
@@ -34,7 +34,7 @@
margin 16px auto 0 auto
> footer
- > mk-copyright
+ > .c
margin 0
text-align center
line-height 64px
diff --git a/src/web/app/mobile/tags/page/settings.tag b/src/web/app/mobile/tags/page/settings.tag
index 9789782144..9a73b0af3c 100644
--- a/src/web/app/mobile/tags/page/settings.tag
+++ b/src/web/app/mobile/tags/page/settings.tag
@@ -24,7 +24,6 @@
<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>
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/post-detail.tag b/src/web/app/mobile/tags/post-detail.tag
index 9f212a2496..1816d1bf93 100644
--- a/src/web/app/mobile/tags/post-detail.tag
+++ b/src/web/app/mobile/tags/post-detail.tag
@@ -34,7 +34,7 @@
<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>
+ <mk-images images={ p.media }/>
</div>
<mk-poll if={ p.poll } post={ p }/>
</div>
diff --git a/src/web/app/mobile/tags/post-form.tag b/src/web/app/mobile/tags/post-form.tag
index 3ac7296f73..05466a6ec2 100644
--- a/src/web/app/mobile/tags/post-form.tag
+++ b/src/web/app/mobile/tags/post-form.tag
@@ -9,12 +9,11 @@
<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 }>
+ <div class="attaches" show={ 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 class="file" each={ files } data-id={ id }>
+ <div class="img" style="background-image: url({ url + '?thumbnail&size=128' })" onclick={ removeFile }></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 }/>
@@ -93,12 +92,9 @@
> .file
display block
float left
- margin 4px
+ margin 0
padding 0
- cursor move
-
- &:hover > .remove
- display block
+ border solid 4px transparent
> .img
width 64px
@@ -106,38 +102,6 @@
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
@@ -181,6 +145,7 @@
</style>
<script>
+ import Sortable from 'sortablejs';
import getKao from '../../common/scripts/get-kao';
this.mixin('api');
@@ -200,6 +165,10 @@
});
this.refs.text.focus();
+
+ new Sortable(this.refs.attaches, {
+ animation: 150
+ });
});
this.onkeydown = e => {
@@ -247,6 +216,13 @@
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;
};
@@ -258,15 +234,23 @@
};
this.post = () => {
- this.wait = true;
+ this.update({
+ wait: true
+ });
- const files = this.files && this.files.length > 0
- ? this.files.map(f => f.id)
- : undefined;
+ const files = [];
+
+ if (this.files.length > 0) {
+ Array.from(this.refs.attaches.children).forEach(el => {
+ const id = el.getAttribute('data-id');
+ const file = this.files.find(f => f.id == id);
+ files.push(file);
+ });
+ }
this.api('posts/create', {
text: this.refs.text.value == '' ? undefined : this.refs.text.value,
- media_ids: files,
+ media_ids: this.files.length > 0 ? files.map(f => f.id) : undefined,
reply_id: opts.reply ? opts.reply.id : undefined,
poll: this.poll ? this.refs.poll.get() : undefined
}).then(data => {
diff --git a/src/web/app/mobile/tags/search-posts.tag b/src/web/app/mobile/tags/search-posts.tag
index 967764bc2c..3e3c034f21 100644
--- a/src/web/app/mobile/tags/search-posts.tag
+++ b/src/web/app/mobile/tags/search-posts.tag
@@ -15,30 +15,28 @@
width calc(100% - 32px)
</style>
<script>
+ import parse from '../../common/scripts/parse-search-query';
+
this.mixin('api');
- this.max = 30;
+ this.limit = 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 => {
+ this.api('posts/search', parse(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,
+ this.offset += this.limit;
+ return this.api('posts/search', Object.assign({}, parse(this.query), {
+ limit: this.limit,
offset: this.offset
- });
+ }));
};
</script>
</mk-search-posts>
diff --git a/src/web/app/mobile/tags/sub-post-content.tag b/src/web/app/mobile/tags/sub-post-content.tag
index 9436b6c1d7..adeb84dea0 100644
--- a/src/web/app/mobile/tags/sub-post-content.tag
+++ b/src/web/app/mobile/tags/sub-post-content.tag
@@ -2,7 +2,7 @@
<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 }/>
+ <mk-images images={ post.media }/>
</details>
<details if={ post.poll }>
<summary>%i18n:mobile.tags.mk-sub-post-content.poll%</summary>
diff --git a/src/web/app/mobile/tags/timeline.tag b/src/web/app/mobile/tags/timeline.tag
index 19f90a1c11..9e85f97da3 100644
--- a/src/web/app/mobile/tags/timeline.tag
+++ b/src/web/app/mobile/tags/timeline.tag
@@ -172,7 +172,7 @@
<a class="quote" if={ p.repost != null }>RP:</a>
</div>
<div class="media" if={ p.media }>
- <mk-images-viewer images={ p.media }/>
+ <mk-images 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>
diff --git a/src/web/app/mobile/tags/ui.tag b/src/web/app/mobile/tags/ui.tag
index 62e128489a..77ad14530d 100644
--- a/src/web/app/mobile/tags/ui.tag
+++ b/src/web/app/mobile/tags/ui.tag
@@ -248,7 +248,7 @@
<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>
+ <a href={ aboutUrl }><p class="about">%i18n:mobile.tags.mk-ui-nav.about%</p></a>
</div>
<style>
:scope
@@ -359,6 +359,8 @@
this.connection = this.stream.getConnection();
this.connectionId = this.stream.use();
+ this.aboutUrl = `${_DOCS_URL_}/${_LANG_}/about`;
+
this.on('mount', () => {
this.connection.on('read_all_notifications', this.onReadAllNotifications);
this.connection.on('read_all_messaging_messages', this.onReadAllMessagingMessages);
@@ -411,7 +413,7 @@
this.search = () => {
const query = window.prompt('%i18n:mobile.tags.mk-ui-nav.search%');
if (query == null || query == '') return;
- this.page('/search:' + query);
+ this.page('/search?q=' + encodeURIComponent(query));
};
</script>
</mk-ui-nav>
diff --git a/src/web/app/mobile/tags/user-timeline.tag b/src/web/app/mobile/tags/user-timeline.tag
index 4dbe719f5a..86ead5971f 100644
--- a/src/web/app/mobile/tags/user-timeline.tag
+++ b/src/web/app/mobile/tags/user-timeline.tag
@@ -26,7 +26,7 @@
return this.api('users/posts', {
user_id: this.user.id,
with_media: this.withMedia,
- max_id: this.refs.timeline.tail().id
+ until_id: this.refs.timeline.tail().id
});
};
</script>