commit
82a28f4c05
178 changed files with 5281 additions and 4794 deletions
|
@ -96,7 +96,6 @@
|
||||||
"gulp-babel": "6.1.2",
|
"gulp-babel": "6.1.2",
|
||||||
"gulp-cssnano": "2.1.2",
|
"gulp-cssnano": "2.1.2",
|
||||||
"gulp-imagemin": "3.1.1",
|
"gulp-imagemin": "3.1.1",
|
||||||
"gulp-livescript": "3.0.1",
|
|
||||||
"gulp-pug": "3.2.0",
|
"gulp-pug": "3.2.0",
|
||||||
"gulp-rename": "1.2.2",
|
"gulp-rename": "1.2.2",
|
||||||
"gulp-replace": "0.5.4",
|
"gulp-replace": "0.5.4",
|
||||||
|
@ -108,7 +107,6 @@
|
||||||
"is-root": "1.0.0",
|
"is-root": "1.0.0",
|
||||||
"is-url": "1.2.2",
|
"is-url": "1.2.2",
|
||||||
"js-yaml": "3.8.1",
|
"js-yaml": "3.8.1",
|
||||||
"livescript": "1.5.0",
|
|
||||||
"mime-types": "2.1.14",
|
"mime-types": "2.1.14",
|
||||||
"mocha": "3.2.0",
|
"mocha": "3.2.0",
|
||||||
"mongodb": "2.2.24",
|
"mongodb": "2.2.24",
|
||||||
|
|
|
@ -106,21 +106,25 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
|
|
||||||
@session = @opts.session
|
this.session = this.opts.session;
|
||||||
@app = @session.app
|
this.app = this.session.app;
|
||||||
|
|
||||||
@cancel = ~>
|
this.cancel = () => {
|
||||||
@api \auth/deny do
|
this.api('auth/deny', {
|
||||||
token: @session.token
|
token: this.session.token
|
||||||
.then ~>
|
}).then(() => {
|
||||||
@trigger \denied
|
this.trigger('denied');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@accept = ~>
|
this.accept = () => {
|
||||||
@api \auth/accept do
|
this.api('auth/accept', {
|
||||||
token: @session.token
|
token: this.session.token
|
||||||
.then ~>
|
}).then(() => {
|
||||||
@trigger \accepted
|
this.trigger('accepted');
|
||||||
|
});
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-form>
|
</mk-form>
|
||||||
|
|
|
@ -88,50 +88,60 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \i
|
this.mixin('i');
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
|
|
||||||
@state = null
|
this.state = null;
|
||||||
@fetching = true
|
this.fetching = true;
|
||||||
|
|
||||||
@token = window.location.href.split \/ .pop!
|
this.token = window.location.href.split('/').pop();
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
if not @SIGNIN then return
|
if (!this.SIGNIN) return;
|
||||||
|
|
||||||
# Fetch session
|
// Fetch session
|
||||||
@api \auth/session/show do
|
this.api('auth/session/show', {
|
||||||
token: @token
|
token: this.token
|
||||||
.then (session) ~>
|
}).then(session => {
|
||||||
@session = session
|
this.session = session;
|
||||||
@fetching = false
|
this.fetching = false;
|
||||||
|
|
||||||
# 既に連携していた場合
|
// 既に連携していた場合
|
||||||
if @session.app.is_authorized
|
if (this.session.app.is_authorized) {
|
||||||
@api \auth/accept do
|
this.api('auth/accept', {
|
||||||
token: @session.token
|
token: this.session.token
|
||||||
.then ~>
|
}).then(() => {
|
||||||
@accepted!
|
this.accepted();
|
||||||
else
|
});
|
||||||
@state = \waiting
|
} else {
|
||||||
@update!
|
this.update({
|
||||||
|
state: 'waiting'
|
||||||
|
});
|
||||||
|
|
||||||
@refs.form.on \denied ~>
|
this.refs.form.on('denied', () => {
|
||||||
@state = \denied
|
this.update({
|
||||||
@update!
|
state: 'denied'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@refs.form.on \accepted @accepted
|
this.refs.form.on('accepted', this.accepted);
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
this.update({
|
||||||
|
fetching: false,
|
||||||
|
state: 'fetch-session-error'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
.catch (error) ~>
|
this.accepted = () => {
|
||||||
@fetching = false
|
this.update({
|
||||||
@state = \fetch-session-error
|
state: 'accepted'
|
||||||
@update!
|
});
|
||||||
|
|
||||||
@accepted = ~>
|
if (this.session.app.callback_url) {
|
||||||
@state = \accepted
|
location.href = this.session.app.callback_url + '?token=' + this.session.token;
|
||||||
@update!
|
}
|
||||||
|
};
|
||||||
if @session.app.callback_url
|
|
||||||
location.href = @session.app.callback_url + '?token=' + @session.token
|
|
||||||
</script>
|
</script>
|
||||||
</mk-index>
|
</mk-index>
|
||||||
|
|
|
@ -27,13 +27,16 @@ riot.mixin({
|
||||||
// ↓ iOS待ちPolyfill (SEE: http://caniuse.com/#feat=fetch)
|
// ↓ iOS待ちPolyfill (SEE: http://caniuse.com/#feat=fetch)
|
||||||
require('whatwg-fetch');
|
require('whatwg-fetch');
|
||||||
|
|
||||||
// ↓ NodeList、HTMLCollectionで forEach を使えるようにする
|
// ↓ NodeList、HTMLCollection、FileListで forEach を使えるようにする
|
||||||
if (NodeList.prototype.forEach === undefined) {
|
if (NodeList.prototype.forEach === undefined) {
|
||||||
NodeList.prototype.forEach = Array.prototype.forEach;
|
NodeList.prototype.forEach = Array.prototype.forEach;
|
||||||
}
|
}
|
||||||
if (HTMLCollection.prototype.forEach === undefined) {
|
if (HTMLCollection.prototype.forEach === undefined) {
|
||||||
HTMLCollection.prototype.forEach = Array.prototype.forEach;
|
HTMLCollection.prototype.forEach = Array.prototype.forEach;
|
||||||
}
|
}
|
||||||
|
if (FileList.prototype.forEach === undefined) {
|
||||||
|
FileList.prototype.forEach = Array.prototype.forEach;
|
||||||
|
}
|
||||||
|
|
||||||
// ↓ iOSでプライベートモードだとlocalStorageが使えないので既存のメソッドを上書きする
|
// ↓ iOSでプライベートモードだとlocalStorageが使えないので既存のメソッドを上書きする
|
||||||
try {
|
try {
|
||||||
|
|
8
src/web/app/common/scripts/contains.js
Normal file
8
src/web/app/common/scripts/contains.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
module.exports = function(parent, child) {
|
||||||
|
let node = child.parentNode;
|
||||||
|
while (node) {
|
||||||
|
if (node == parent) return true;
|
||||||
|
node = node.parentNode;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -21,6 +21,6 @@
|
||||||
text-decoration underline
|
text-decoration underline
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \i
|
this.mixin('i');
|
||||||
</script>
|
</script>
|
||||||
</mk-api-info>
|
</mk-api-info>
|
||||||
|
|
|
@ -17,18 +17,17 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
|
|
||||||
@apps = []
|
this.apps = [];
|
||||||
@fetching = true
|
this.fetching = true;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@api \i/authorized_apps
|
this.api('i/authorized_apps').then(apps => {
|
||||||
.then (apps) ~>
|
this.apps = apps;
|
||||||
@apps = apps
|
this.fetching = false;
|
||||||
@fetching = false
|
this.update();
|
||||||
@update!
|
});
|
||||||
.catch (err) ~>
|
});
|
||||||
console.error err
|
|
||||||
</script>
|
</script>
|
||||||
</mk-authorized-apps>
|
</mk-authorized-apps>
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
<mk-copyright><span>(c) syuilo 2014-2017</span>
|
<mk-copyright>
|
||||||
|
<span>(c) syuilo 2014-2017</span>
|
||||||
<style>
|
<style>
|
||||||
:scope
|
:scope
|
||||||
display block
|
display block
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
</mk-copyright>
|
</mk-copyright>
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
<mk-core-error>
|
<mk-core-error>
|
||||||
<!--i: i.fa.fa-times-circle--><img src="/_/resources/error.jpg" alt=""/>
|
<!--i: i.fa.fa-times-circle-->
|
||||||
<h1>
|
<img src="/_/resources/error.jpg" alt=""/>
|
||||||
<mk-ripple-string>サーバーに接続できません</mk-ripple-string>
|
<h1>サーバーに接続できません</h1>
|
||||||
</h1>
|
|
||||||
<p class="text">インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから<a onclick={ retry }>再度お試し</a>ください。</p>
|
<p class="text">インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから<a onclick={ retry }>再度お試し</a>ください。</p>
|
||||||
<p class="thanks">いつもMisskeyをご利用いただきありがとうございます。</p>
|
<p class="thanks">いつもMisskeyをご利用いただきありがとうございます。</p>
|
||||||
<style>
|
<style>
|
||||||
|
@ -57,8 +56,9 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@retry = ~>
|
this.retry = () => {
|
||||||
@unmount!
|
this.unmount();
|
||||||
@opts.retry!
|
this.opts.retry();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</mk-core-error>
|
</mk-core-error>
|
||||||
|
|
|
@ -20,10 +20,5 @@
|
||||||
opacity 1
|
opacity 1
|
||||||
40%
|
40%
|
||||||
opacity 0
|
opacity 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
</mk-ellipsis>
|
</mk-ellipsis>
|
||||||
|
|
|
@ -5,6 +5,6 @@
|
||||||
display inline
|
display inline
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@kind = @opts.type.split \/ .0
|
this.kind = this.opts.type.split('/')[0];
|
||||||
</script>
|
</script>
|
||||||
</mk-file-type-icon>
|
</mk-file-type-icon>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
require('./core-error.tag');
|
require('./core-error.tag');
|
||||||
require('./url.tag');
|
require('./url.tag');
|
||||||
require('./url-preview.tag');
|
require('./url-preview.tag');
|
||||||
require('./ripple-string.tag');
|
|
||||||
require('./time.tag');
|
require('./time.tag');
|
||||||
require('./file-type-icon.tag');
|
require('./file-type-icon.tag');
|
||||||
require('./uploader.tag');
|
require('./uploader.tag');
|
||||||
|
@ -24,3 +23,4 @@ require('./messaging/room.tag');
|
||||||
require('./messaging/message.tag');
|
require('./messaging/message.tag');
|
||||||
require('./messaging/index.tag');
|
require('./messaging/index.tag');
|
||||||
require('./messaging/form.tag');
|
require('./messaging/form.tag');
|
||||||
|
require('./stream-indicator.tag');
|
||||||
|
|
|
@ -21,9 +21,5 @@
|
||||||
margin 0
|
margin 0
|
||||||
text-align center
|
text-align center
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
</mk-introduction>
|
</mk-introduction>
|
||||||
|
|
|
@ -2,9 +2,15 @@
|
||||||
<textarea ref="text" onkeypress={ onkeypress } onpaste={ onpaste } placeholder="ここにメッセージを入力"></textarea>
|
<textarea ref="text" onkeypress={ onkeypress } onpaste={ onpaste } placeholder="ここにメッセージを入力"></textarea>
|
||||||
<div class="files"></div>
|
<div class="files"></div>
|
||||||
<mk-uploader ref="uploader"></mk-uploader>
|
<mk-uploader ref="uploader"></mk-uploader>
|
||||||
<button class="send" onclick={ send } disabled={ sending } title="メッセージを送信"><i class="fa fa-paper-plane" if={ !sending }></i><i class="fa fa-spinner fa-spin" if={ sending }></i></button>
|
<button class="send" onclick={ send } disabled={ sending } title="メッセージを送信">
|
||||||
<button class="attach-from-local" type="button" title="PCから画像を添付する"><i class="fa fa-upload"></i></button>
|
<i class="fa fa-paper-plane" if={ !sending }></i><i class="fa fa-spinner fa-spin" if={ sending }></i>
|
||||||
<button class="attach-from-drive" type="button" title="アルバムから画像を添付する"><i class="fa fa-folder-open"></i></button>
|
</button>
|
||||||
|
<button class="attach-from-local" type="button" title="PCから画像を添付する">
|
||||||
|
<i class="fa fa-upload"></i>
|
||||||
|
</button>
|
||||||
|
<button class="attach-from-drive" type="button" title="アルバムから画像を添付する">
|
||||||
|
<i class="fa fa-folder-open"></i>
|
||||||
|
</button>
|
||||||
<input name="file" type="file" accept="image/*"/>
|
<input name="file" type="file" accept="image/*"/>
|
||||||
<style>
|
<style>
|
||||||
:scope
|
:scope
|
||||||
|
@ -111,49 +117,60 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
|
|
||||||
@onpaste = (e) ~>
|
this.onpaste = (e) => {
|
||||||
data = e.clipboard-data
|
const data = e.clipboardData;
|
||||||
items = data.items
|
const items = data.items;
|
||||||
for i from 0 to items.length - 1
|
for (let i = 0; i < items.length; i++) {
|
||||||
item = items[i]
|
const item = items[i];
|
||||||
switch (item.kind)
|
if (item.kind == 'file') {
|
||||||
| \file =>
|
this.upload(item.getAsFile());
|
||||||
@upload item.get-as-file!
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@onkeypress = (e) ~>
|
this.onkeypress = (e) => {
|
||||||
if (e.which == 10 || e.which == 13) && e.ctrl-key
|
if ((e.which == 10 || e.which == 13) && e.ctrlKey) {
|
||||||
@send!
|
this.send();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@select-file = ~>
|
this.selectFile = () => {
|
||||||
@refs.file.click!
|
this.refs.file.click();
|
||||||
|
};
|
||||||
|
|
||||||
@select-file-from-drive = ~>
|
this.selectFileFromDrive = () => {
|
||||||
browser = document.body.append-child document.create-element \mk-select-file-from-drive-window
|
const browser = document.body.appendChild(document.createElement('mk-select-file-from-drive-window'));
|
||||||
event = riot.observable!
|
const event = riot.observable();
|
||||||
riot.mount browser, do
|
riot.mount(browser, {
|
||||||
multiple: true
|
multiple: true,
|
||||||
event: event
|
event: event
|
||||||
event.one \selected (files) ~>
|
});
|
||||||
files.for-each @add-file
|
event.one('selected', files => {
|
||||||
|
files.forEach(this.addFile);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@send = ~>
|
this.send = () => {
|
||||||
@sending = true
|
this.sending = true;
|
||||||
@api \messaging/messages/create do
|
this.api('messaging/messages/create', {
|
||||||
user_id: @opts.user.id
|
user_id: this.opts.user.id,
|
||||||
text: @refs.text.value
|
text: this.refs.text.value
|
||||||
.then (message) ~>
|
}).then(message => {
|
||||||
@clear!
|
this.clear();
|
||||||
.catch (err) ~>
|
}).catch(err => {
|
||||||
console.error err
|
console.error(err);
|
||||||
.then ~>
|
}).then(() => {
|
||||||
@sending = false
|
this.sending = false;
|
||||||
@update!
|
this.update();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@clear = ~>
|
this.clear = () => {
|
||||||
@refs.text.value = ''
|
this.refs.text.value = '';
|
||||||
@files = []
|
this.files = [];
|
||||||
@update!
|
this.update();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-messaging-form>
|
</mk-messaging-form>
|
||||||
|
|
|
@ -286,72 +286,88 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \i
|
this.mixin('i');
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
|
|
||||||
@search-result = []
|
this.searchResult = [];
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@api \messaging/history
|
this.api('messaging/history').then(history => {
|
||||||
.then (history) ~>
|
this.isLoading = false;
|
||||||
@is-loading = false
|
history.forEach(message => {
|
||||||
history.for-each (message) ~>
|
message.is_me = message.user_id == this.I.id
|
||||||
message.is_me = message.user_id == @I.id
|
message._click = () => {
|
||||||
message._click = ~>
|
this.trigger('navigate-user', message.is_me ? message.recipient : message.user);
|
||||||
if message.is_me
|
};
|
||||||
@trigger \navigate-user message.recipient
|
});
|
||||||
else
|
this.history = history;
|
||||||
@trigger \navigate-user message.user
|
this.update();
|
||||||
@history = history
|
});
|
||||||
@update!
|
});
|
||||||
.catch (err) ~>
|
|
||||||
console.error err
|
|
||||||
|
|
||||||
@search = ~>
|
this.search = () => {
|
||||||
q = @refs.search.value
|
const q = this.refs.search.value;
|
||||||
if q == ''
|
if (q == '') {
|
||||||
@search-result = []
|
this.searchResult = [];
|
||||||
else
|
return;
|
||||||
@api \users/search do
|
}
|
||||||
query: q
|
this.api('users/search', {
|
||||||
max: 5
|
query: q,
|
||||||
.then (users) ~>
|
max: 5
|
||||||
users.for-each (user) ~>
|
}).then(users => {
|
||||||
user._click = ~>
|
users.forEach(user => {
|
||||||
@trigger \navigate-user user
|
user._click = () => {
|
||||||
@search-result = []
|
this.trigger('navigate-user', user);
|
||||||
@search-result = users
|
this.searchResult = [];
|
||||||
@update!
|
};
|
||||||
.catch (err) ~>
|
});
|
||||||
console.error err
|
this.update({
|
||||||
|
searchResult: users
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@on-search-keydown = (e) ~>
|
this.onSearchKeydown = e => {
|
||||||
key = e.which
|
switch (e.which) {
|
||||||
switch (key)
|
case 9: // [TAB]
|
||||||
| 9, 40 => # Key[TAB] or Key[↓]
|
case 40: // [↓]
|
||||||
e.prevent-default!
|
e.preventDefault();
|
||||||
e.stop-propagation!
|
e.stopPropagation();
|
||||||
@refs.search-result.child-nodes[0].focus!
|
this.refs.searchResult.childNodes[0].focus();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@on-search-result-keydown = (i, e) ~>
|
this.onSearchResultKeydown = (i, e) => {
|
||||||
key = e.which
|
const cancel = () => {
|
||||||
switch (key)
|
e.preventDefault();
|
||||||
| 10, 13 => # Key[ENTER]
|
e.stopPropagation();
|
||||||
e.prevent-default!
|
};
|
||||||
e.stop-propagation!
|
switch (true) {
|
||||||
@search-result[i]._click!
|
case e.which == 10: // [ENTER]
|
||||||
| 27 => # Key[ESC]
|
case e.which == 13: // [ENTER]
|
||||||
e.prevent-default!
|
cancel();
|
||||||
e.stop-propagation!
|
this.searchResult[i]._click();
|
||||||
@refs.search.focus!
|
break;
|
||||||
| 38 => # Key[↑]
|
|
||||||
e.prevent-default!
|
case e.which == 27: // [ESC]
|
||||||
e.stop-propagation!
|
cancel();
|
||||||
(@refs.search-result.child-nodes[i].previous-element-sibling || @refs.search-result.child-nodes[@search-result.length - 1]).focus!
|
this.refs.search.focus();
|
||||||
| 9, 40 => # Key[TAB] or Key[↓]
|
break;
|
||||||
e.prevent-default!
|
|
||||||
e.stop-propagation!
|
case e.which == 9 && e.shiftKey: // [TAB] + [Shift]
|
||||||
(@refs.search-result.child-nodes[i].next-element-sibling || @refs.search-result.child-nodes[0]).focus!
|
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>
|
</script>
|
||||||
</mk-messaging>
|
</mk-messaging>
|
||||||
|
|
|
@ -203,28 +203,32 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \i
|
this.mixin('i');
|
||||||
@mixin \text
|
this.mixin('text');
|
||||||
|
|
||||||
@message = @opts.message
|
this.message = this.opts.message;
|
||||||
@message.is_me = @message.user.id == @I.id
|
this.message.is_me = this.message.user.id == this.I.id;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
if @message.text?
|
if (this.message.text) {
|
||||||
tokens = @analyze @message.text
|
const tokens = this.analyze(this.message.text);
|
||||||
|
|
||||||
@refs.text.innerHTML = @compile tokens
|
this.refs.text.innerHTML = this.compile(tokens);
|
||||||
|
|
||||||
@refs.text.children.for-each (e) ~>
|
this.refs.text.children.forEach(e => {
|
||||||
if e.tag-name == \MK-URL
|
if (e.tagName == 'MK-URL') riot.mount(e);
|
||||||
riot.mount e
|
});
|
||||||
|
|
||||||
# URLをプレビュー
|
// URLをプレビュー
|
||||||
tokens
|
tokens
|
||||||
.filter (t) -> t.type == \link
|
.filter(t => t.type == 'link')
|
||||||
.map (t) ~>
|
.map(t => {
|
||||||
@preview = @refs.text.append-child document.create-element \mk-url-preview
|
const el = this.refs.text.appendChild(document.createElement('mk-url-preview'));
|
||||||
riot.mount @preview, do
|
riot.mount(el, {
|
||||||
url: t.content
|
url: t.content
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</mk-messaging-message>
|
</mk-messaging-message>
|
||||||
|
|
|
@ -124,101 +124,117 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \i
|
this.mixin('i');
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
@mixin \messaging-stream
|
this.mixin('messaging-stream');
|
||||||
|
|
||||||
@user = @opts.user
|
this.user = this.opts.user;
|
||||||
@init = true
|
this.init = true;
|
||||||
@sending = false
|
this.sending = false;
|
||||||
@messages = []
|
this.messages = [];
|
||||||
|
|
||||||
@connection = new @MessagingStreamConnection @I, @user.id
|
this.connection = new this.MessagingStreamConnection(this.I, this.user.id);
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@connection.event.on \message @on-message
|
this.connection.event.on('message', this.onMessage);
|
||||||
@connection.event.on \read @on-read
|
this.connection.event.on('read', this.onRead);
|
||||||
|
|
||||||
document.add-event-listener \visibilitychange @on-visibilitychange
|
document.addEventListener('visibilitychange', this.onVisibilitychange);
|
||||||
|
|
||||||
@api \messaging/messages do
|
this.api('messaging/messages', {
|
||||||
user_id: @user.id
|
user_id: this.user.id
|
||||||
.then (messages) ~>
|
}).then(messages => {
|
||||||
@init = false
|
this.init = false;
|
||||||
@messages = messages.reverse!
|
this.messages = messages.reverse();
|
||||||
@update!
|
this.update();
|
||||||
@scroll-to-bottom!
|
this.scrollToBottom();
|
||||||
.catch (err) ~>
|
});
|
||||||
console.error err
|
});
|
||||||
|
|
||||||
@on \unmount ~>
|
this.on('unmount', () => {
|
||||||
@connection.event.off \message @on-message
|
this.connection.event.off('message', this.onMessage);
|
||||||
@connection.event.off \read @on-read
|
this.connection.event.off('read', this.onRead);
|
||||||
@connection.close!
|
this.connection.close();
|
||||||
|
|
||||||
document.remove-event-listener \visibilitychange @on-visibilitychange
|
document.removeEventListener('visibilitychange', this.onVisibilitychange);
|
||||||
|
});
|
||||||
|
|
||||||
@on \update ~>
|
this.on('update', () => {
|
||||||
@messages.for-each (message) ~>
|
this.messages.forEach(message => {
|
||||||
date = (new Date message.created_at).get-date!
|
const date = (new Date(message.created_at)).getDate();
|
||||||
month = (new Date message.created_at).get-month! + 1
|
const month = (new Date(message.created_at)).getMonth() + 1;
|
||||||
message._date = date
|
message._date = date;
|
||||||
message._datetext = month + '月 ' + date + '日'
|
message._datetext = month + '月 ' + date + '日';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@on-message = (message) ~>
|
this.onMessage = (message) => {
|
||||||
is-bottom = @is-bottom!
|
const isbottom = this.isBottom();
|
||||||
|
|
||||||
@messages.push message
|
this.messages.push(message);
|
||||||
if message.user_id != @I.id and not document.hidden
|
if (message.user_id != this.I.id && !document.hidden) {
|
||||||
@connection.socket.send JSON.stringify do
|
this.connection.socket.send(JSON.stringify({
|
||||||
type: \read
|
type: 'read',
|
||||||
id: message.id
|
id: message.id
|
||||||
@update!
|
}));
|
||||||
|
}
|
||||||
|
this.update();
|
||||||
|
|
||||||
if is-bottom
|
if (isBottom) {
|
||||||
# Scroll to bottom
|
// Scroll to bottom
|
||||||
@scroll-to-bottom!
|
this.scrollToBottom();
|
||||||
else if message.user_id != @I.id
|
} else if (message.user_id != this.I.id) {
|
||||||
# Notify
|
// Notify
|
||||||
@notify '新しいメッセージがあります'
|
this.notify('新しいメッセージがあります');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@on-read = (ids) ~>
|
this.onRead = ids => {
|
||||||
if not Array.isArray ids then ids = [ids]
|
if (!Array.isArray(ids)) ids = [ids];
|
||||||
ids.for-each (id) ~>
|
ids.forEach(id => {
|
||||||
if (@messages.some (x) ~> x.id == id)
|
if (this.messages.some(x => x.id == id)) {
|
||||||
exist = (@messages.map (x) -> x.id).index-of id
|
const exist = this.messages.map(x => x.id).indexOf(id);
|
||||||
@messages[exist].is_read = true
|
this.messages[exist].is_read = true;
|
||||||
@update!
|
this.update();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@is-bottom = ~>
|
this.isBottom = () => {
|
||||||
current = @root.scroll-top + @root.offset-height
|
const current = this.root.scrollTop + this.root.offsetHeight;
|
||||||
max = @root.scroll-height
|
const max = this.root.scrollHeight;
|
||||||
current > (max - 32)
|
return current > (max - 32);
|
||||||
|
};
|
||||||
|
|
||||||
@scroll-to-bottom = ~>
|
this.scrollToBottom = () => {
|
||||||
@root.scroll-top = @root.scroll-height
|
this.root.scrollTop = this.root.scrollHeight;
|
||||||
|
};
|
||||||
|
|
||||||
@notify = (message) ~>
|
this.notify = message => {
|
||||||
n = document.create-element \p
|
const n = document.createElement('p');
|
||||||
n.inner-HTML = '<i class="fa fa-arrow-circle-down"></i>' + message
|
n.innerHTML = '<i class="fa fa-arrow-circle-down"></i>' + message;
|
||||||
n.onclick = ~>
|
n.onclick = () => {
|
||||||
@scroll-to-bottom!
|
this.scrollToBottom();
|
||||||
n.parent-node.remove-child n
|
n.parentNode.removeChild(n);
|
||||||
@refs.notifications.append-child n
|
};
|
||||||
|
this.refs.notifications.appendChild(n);
|
||||||
|
|
||||||
set-timeout ~>
|
setTimeout(() => {
|
||||||
n.style.opacity = 0
|
n.style.opacity = 0;
|
||||||
set-timeout ~>
|
setTimeout(() => n.parentNode.removeChild(n), 1000);
|
||||||
n.parent-node.remove-child n
|
}, 4000);
|
||||||
, 1000ms
|
};
|
||||||
, 4000ms
|
|
||||||
|
|
||||||
@on-visibilitychange = ~>
|
this.onVisibilitychange = () => {
|
||||||
if document.hidden then return
|
if (document.hidden) return;
|
||||||
@messages.for-each (message) ~>
|
this.messages.forEach(message => {
|
||||||
if message.user_id != @I.id and not message.is_read
|
if (message.user_id !== this.I.id && !message.is_read) {
|
||||||
@connection.socket.send JSON.stringify do
|
this.connection.socket.send(JSON.stringify({
|
||||||
type: \read
|
type: 'read',
|
||||||
id: message.id
|
id: message.id
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-messaging-room>
|
</mk-messaging-room>
|
||||||
|
|
|
@ -2,17 +2,17 @@
|
||||||
<style>
|
<style>
|
||||||
:scope
|
:scope
|
||||||
display inline
|
display inline
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
# バグ? https://github.com/riot/riot/issues/2103
|
// https://github.com/riot/riot/issues/2103
|
||||||
#value = @opts.value
|
//value = this.opts.value
|
||||||
value = @opts.riot-value
|
let value = this.opts.riotValue;
|
||||||
max = @opts.max
|
const max = this.opts.max;
|
||||||
|
|
||||||
if max? then if value > max then value = max
|
if (max != null && value > max) value = max;
|
||||||
|
|
||||||
@root.innerHTML = value.to-locale-string!
|
this.root.innerHTML = value.toLocaleString();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</mk-number>
|
</mk-number>
|
||||||
|
|
|
@ -86,26 +86,31 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@choices = ['', '']
|
this.choices = ['', ''];
|
||||||
|
|
||||||
@oninput = (i, e) ~>
|
this.oninput = (i, e) => {
|
||||||
@choices[i] = e.target.value
|
this.choices[i] = e.target.value;
|
||||||
|
}
|
||||||
|
|
||||||
@add = ~>
|
this.add = () => {
|
||||||
@choices.push ''
|
this.choices.push('');
|
||||||
@update!
|
this.update();
|
||||||
@refs.choices.child-nodes[@choices.length - 1].child-nodes[0].focus!
|
this.refs.choices.childNodes[this.choices.length - 1].childNodes[0].focus();
|
||||||
|
}
|
||||||
|
|
||||||
@remove = (i) ~>
|
this.remove = (i) => {
|
||||||
@choices = @choices.filter((_, _i) -> _i != i)
|
this.choices = this.choices.filter((_, _i) => _i != i);
|
||||||
@update!
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
@destroy = ~>
|
this.destroy = () => {
|
||||||
@opts.ondestroy!
|
this.opts.ondestroy();
|
||||||
|
}
|
||||||
|
|
||||||
@get = ~>
|
this.get = () => {
|
||||||
return {
|
return {
|
||||||
choices: @choices.filter (choice) -> choice != ''
|
choices: this.choices.filter(choice => choice != '')
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</mk-poll-editor>
|
</mk-poll-editor>
|
||||||
|
|
|
@ -68,32 +68,37 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
|
|
||||||
@post = @opts.post
|
this.post = this.opts.post;
|
||||||
@poll = @post.poll
|
this.poll = this.post.poll;
|
||||||
@total = @poll.choices.reduce ((a, b) -> a + b.votes), 0
|
this.total = this.poll.choices.reduce((a, b) => a + b.votes, 0);
|
||||||
@is-voted = @poll.choices.some (c) -> c.is_voted
|
this.isVoted = this.poll.choices.some(c => c.is_voted);
|
||||||
@result = @is-voted
|
this.result = this.isVoted;
|
||||||
|
|
||||||
@toggle-result = ~>
|
this.toggleResult = () => {
|
||||||
@result = !@result
|
this.result = !this.result;
|
||||||
|
}
|
||||||
|
|
||||||
@vote = (id) ~>
|
this.vote = (id) => {
|
||||||
if (@poll.choices.some (c) -> c.is_voted) then return
|
if (this.poll.choices.some(c => c.is_voted)) return;
|
||||||
@api \posts/polls/vote do
|
this.api('posts/polls/vote', {
|
||||||
post_id: @post.id
|
post_id: this.post.id,
|
||||||
choice: id
|
choice: id
|
||||||
.then ~>
|
}).then(() => {
|
||||||
@poll.choices.for-each (c) ->
|
this.poll.choices.forEach(c => {
|
||||||
if c.id == id
|
if (c.id == id) {
|
||||||
c.votes++
|
c.votes++;
|
||||||
c.is_voted = true
|
c.is_voted = true;
|
||||||
@update do
|
}
|
||||||
poll: @poll
|
});
|
||||||
is-voted: true
|
this.update({
|
||||||
result: true
|
poll: this.poll,
|
||||||
total: @total + 1
|
isVoted: true,
|
||||||
|
result: true,
|
||||||
|
total: this.total + 1
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</mk-poll>
|
</mk-poll>
|
||||||
|
|
|
@ -4,5 +4,5 @@
|
||||||
display inline
|
display inline
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>@root.innerHTML = @opts.content</script>
|
<script>this.root.innerHTML = this.opts.content</script>
|
||||||
</mk-raw>
|
</mk-raw>
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
<mk-ripple-string><yield/>
|
|
||||||
<style>
|
|
||||||
:scope
|
|
||||||
display inline
|
|
||||||
|
|
||||||
> span
|
|
||||||
animation ripple-string 5s infinite ease-in-out both
|
|
||||||
|
|
||||||
@keyframes ripple-string
|
|
||||||
0%, 50%, 100%
|
|
||||||
opacity 1
|
|
||||||
25%
|
|
||||||
opacity 0.5
|
|
||||||
|
|
||||||
</style>
|
|
||||||
<script>
|
|
||||||
@on \mount ~>
|
|
||||||
text = @root.innerHTML
|
|
||||||
@root.innerHTML = ''
|
|
||||||
(text.split '').for-each (c, i) ~>
|
|
||||||
ce = document.create-element \span
|
|
||||||
ce.innerHTML = c
|
|
||||||
ce.style.animation-delay = (i / 10) + 's'
|
|
||||||
@root.append-child ce
|
|
||||||
</script>
|
|
||||||
</mk-ripple-string>
|
|
|
@ -48,28 +48,30 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
@mixin \stream
|
this.mixin('stream');
|
||||||
|
|
||||||
@history = []
|
this.history = [];
|
||||||
@fetching = true
|
this.fetching = true;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@api \i/signin_history
|
this.api('i/signin_history').then(history => {
|
||||||
.then (history) ~>
|
this.update({
|
||||||
@history = history
|
fetching: false,
|
||||||
@fetching = false
|
history: history
|
||||||
@update!
|
});
|
||||||
.catch (err) ~>
|
});
|
||||||
console.error err
|
|
||||||
|
|
||||||
@stream.on \signin @on-signin
|
this.stream.on('signin', this.onSignin);
|
||||||
|
});
|
||||||
|
|
||||||
@on \unmount ~>
|
this.on('unmount', () => {
|
||||||
@stream.off \signin @on-signin
|
this.stream.off('signin', this.onSignin);
|
||||||
|
});
|
||||||
|
|
||||||
@on-signin = (signin) ~>
|
this.onSignin = signin => {
|
||||||
@history.unshift signin
|
this.history.unshift(signin);
|
||||||
@update!
|
this.update();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-signin-history>
|
</mk-signin-history>
|
||||||
|
|
|
@ -97,42 +97,50 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
|
|
||||||
@user = null
|
this.user = null;
|
||||||
@signing = false
|
this.signing = false;
|
||||||
|
|
||||||
@oninput = ~>
|
this.oninput = () => {
|
||||||
@api \users/show do
|
this.api('users/show', {
|
||||||
username: @refs.username.value
|
username: this.refs.username.value
|
||||||
.then (user) ~>
|
}).then(user => {
|
||||||
@user = user
|
this.user = user;
|
||||||
@trigger \user user
|
this.trigger('user', user);
|
||||||
@update!
|
this.update();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@onsubmit = (e) ~>
|
this.onsubmit = e => {
|
||||||
e.prevent-default!
|
e.preventDefault();
|
||||||
|
|
||||||
if @refs.username.value == ''
|
if (this.refs.username.value == '') {
|
||||||
@refs.username.focus!
|
this.refs.username.focus();
|
||||||
return false
|
return false;
|
||||||
if @refs.password.value == ''
|
}
|
||||||
@refs.password.focus!
|
if (this.refs.password.value == '') {
|
||||||
return false
|
this.refs.password.focus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@signing = true
|
this.update({
|
||||||
@update!
|
signing: true
|
||||||
|
});
|
||||||
|
|
||||||
@api \signin do
|
this.api('signin', {
|
||||||
username: @refs.username.value
|
username: this.refs.username.value,
|
||||||
password: @refs.password.value
|
password: this.refs.password.value
|
||||||
.then ~>
|
}).then(() => {
|
||||||
location.reload!
|
location.reload();
|
||||||
.catch ~>
|
}).catch(() => {
|
||||||
alert 'something happened'
|
alert('something happened');
|
||||||
@signing = false
|
this.update({
|
||||||
@update!
|
signing: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
false
|
return false;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-signin>
|
</mk-signin>
|
||||||
|
|
|
@ -174,120 +174,126 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
@mixin \get-password-strength
|
this.mixin('get-password-strength');
|
||||||
|
|
||||||
@username-state = null
|
this.usernameState = null;
|
||||||
@password-strength = ''
|
this.passwordStrength = '';
|
||||||
@password-retype-state = null
|
this.passwordRetypeState = null;
|
||||||
@recaptchaed = false
|
this.recaptchaed = false;
|
||||||
|
|
||||||
window.on-recaptchaed = ~>
|
window.onEecaptchaed = () => {
|
||||||
@recaptchaed = true
|
this.recaptchaed = true;
|
||||||
@update!
|
this.update();
|
||||||
|
};
|
||||||
|
|
||||||
window.on-recaptcha-expired = ~>
|
window.onRecaptchaExpired = () => {
|
||||||
@recaptchaed = false
|
this.recaptchaed = false;
|
||||||
@update!
|
this.update();
|
||||||
|
};
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
head = (document.get-elements-by-tag-name \head).0
|
const head = document.getElementsByTagName('head')[0];
|
||||||
script = document.create-element \script
|
const script = document.createElement('script');
|
||||||
..set-attribute \src \https://www.google.com/recaptcha/api.js
|
script.setAttribute('src', 'https://www.google.com/recaptcha/api.js');
|
||||||
head.append-child script
|
head.appendChild(script);
|
||||||
|
});
|
||||||
|
|
||||||
@on-change-username = ~>
|
this.onChangeUsername = () => {
|
||||||
username = @refs.username.value
|
const username = this.refs.username.value;
|
||||||
|
|
||||||
if username == ''
|
if (username == '') {
|
||||||
@username-state = null
|
this.update({
|
||||||
@update!
|
usernameState: null
|
||||||
return
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
err = switch
|
const err =
|
||||||
| not username.match /^[a-zA-Z0-9\-]+$/ => \invalid-format
|
!username.match(/^[a-zA-Z0-9\-]+$/) ? 'invalid-format' :
|
||||||
| username.length < 3chars => \min-range
|
username.length < 3 ? 'min-range' :
|
||||||
| username.length > 20chars => \max-range
|
username.length > 20 ? 'max-range' :
|
||||||
| _ => null
|
null;
|
||||||
|
|
||||||
if err?
|
if (err) {
|
||||||
@username-state = err
|
this.update({
|
||||||
@update!
|
usernameState: err
|
||||||
else
|
});
|
||||||
@username-state = \wait
|
return;
|
||||||
@update!
|
}
|
||||||
|
|
||||||
@api \username/available do
|
this.update({
|
||||||
username: username
|
usernameState: 'wait'
|
||||||
.then (result) ~>
|
});
|
||||||
if result.available
|
|
||||||
@username-state = \ok
|
|
||||||
else
|
|
||||||
@username-state = \unavailable
|
|
||||||
@update!
|
|
||||||
.catch (err) ~>
|
|
||||||
@username-state = \error
|
|
||||||
@update!
|
|
||||||
|
|
||||||
@on-change-password = ~>
|
this.api('username/available', {
|
||||||
password = @refs.password.value
|
|
||||||
|
|
||||||
if password == ''
|
|
||||||
@password-strength = ''
|
|
||||||
return
|
|
||||||
|
|
||||||
strength = @get-password-strength password
|
|
||||||
|
|
||||||
if strength > 0.3
|
|
||||||
@password-strength = \medium
|
|
||||||
if strength > 0.7
|
|
||||||
@password-strength = \high
|
|
||||||
else
|
|
||||||
@password-strength = \low
|
|
||||||
|
|
||||||
@update!
|
|
||||||
|
|
||||||
@refs.password-metar.style.width = (strength * 100) + \%
|
|
||||||
|
|
||||||
@on-change-password-retype = ~>
|
|
||||||
password = @refs.password.value
|
|
||||||
retyped-password = @refs.password-retype.value
|
|
||||||
|
|
||||||
if retyped-password == ''
|
|
||||||
@password-retype-state = null
|
|
||||||
return
|
|
||||||
|
|
||||||
if password == retyped-password
|
|
||||||
@password-retype-state = \match
|
|
||||||
else
|
|
||||||
@password-retype-state = \not-match
|
|
||||||
|
|
||||||
@onsubmit = (e) ~>
|
|
||||||
e.prevent-default!
|
|
||||||
|
|
||||||
username = @refs.username.value
|
|
||||||
password = @refs.password.value
|
|
||||||
|
|
||||||
locker = document.body.append-child document.create-element \mk-locker
|
|
||||||
|
|
||||||
@api \signup do
|
|
||||||
username: username
|
username: username
|
||||||
password: password
|
}).then(result => {
|
||||||
'g-recaptcha-response': grecaptcha.get-response!
|
this.update({
|
||||||
.then ~>
|
usernameState: result.available ? 'ok' : 'unavailable'
|
||||||
@api \signin do
|
});
|
||||||
username: username
|
}).catch(err => {
|
||||||
|
this.update({
|
||||||
|
usernameState: 'error'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.onChangePassword = () => {
|
||||||
|
const password = this.refs.password.value;
|
||||||
|
|
||||||
|
if (password == '') {
|
||||||
|
this.passwordStrength = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const strength = this.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
|
password: password
|
||||||
.then ~>
|
}).then(() => {
|
||||||
location.href = CONFIG.url
|
location.href = CONFIG.url
|
||||||
.catch ~>
|
});
|
||||||
alert '何らかの原因によりアカウントの作成に失敗しました。再度お試しください。'
|
}).catch(() => {
|
||||||
|
alert('何らかの原因によりアカウントの作成に失敗しました。再度お試しください。');
|
||||||
|
|
||||||
grecaptcha.reset!
|
grecaptcha.reset();
|
||||||
@recaptchaed = false
|
this.recaptchaed = false;
|
||||||
|
|
||||||
locker.parent-node.remove-child locker
|
locker.parentNode.removeChild(locker);
|
||||||
|
});
|
||||||
|
|
||||||
false
|
return false;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-signup>
|
</mk-signup>
|
||||||
|
|
|
@ -20,8 +20,8 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
now = new Date!
|
const now = new Date();
|
||||||
@d = now.get-date!
|
this.d = now.getDate();
|
||||||
@m = now.get-month! + 1
|
this.m = now.getMonth() + 1;
|
||||||
</script>
|
</script>
|
||||||
</mk-special-message>
|
</mk-special-message>
|
||||||
|
|
65
src/web/app/common/tags/stream-indicator.tag
Normal file
65
src/web/app/common/tags/stream-indicator.tag
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
<mk-stream-indicator>
|
||||||
|
<p if={ state == 'initializing' }>
|
||||||
|
<i class="fa fa-spinner fa-spin"></i>
|
||||||
|
<span>接続中<mk-ellipsis></mk-ellipsis></span>
|
||||||
|
</p>
|
||||||
|
<p if={ state == 'reconnecting' }>
|
||||||
|
<i class="fa fa-spinner fa-spin"></i>
|
||||||
|
<span>切断されました 接続中<mk-ellipsis></mk-ellipsis></span>
|
||||||
|
</p>
|
||||||
|
<p if={ state == 'connected' }>
|
||||||
|
<i class="fa fa-check"></i>
|
||||||
|
<span>接続完了</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)
|
||||||
|
|
||||||
|
> p
|
||||||
|
display block
|
||||||
|
margin 0
|
||||||
|
|
||||||
|
> i
|
||||||
|
margin-right 0.25em
|
||||||
|
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
this.mixin('stream');
|
||||||
|
|
||||||
|
this.on('before-mount', () => {
|
||||||
|
this.state = this.getStreamState();
|
||||||
|
|
||||||
|
if (this.state == 'connected') {
|
||||||
|
this.root.style.opacity = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.streamStateEv.on('connected', () => {
|
||||||
|
this.state = this.getStreamState();
|
||||||
|
this.update();
|
||||||
|
setTimeout(() => {
|
||||||
|
Velocity(this.root, {
|
||||||
|
opacity: 0
|
||||||
|
}, 200, 'linear');
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.streamStateEv.on('closed', () => {
|
||||||
|
this.state = this.getStreamState();
|
||||||
|
this.update();
|
||||||
|
Velocity(this.root, {
|
||||||
|
opacity: 1
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</mk-stream-indicator>
|
|
@ -1,41 +1,50 @@
|
||||||
<mk-time>
|
<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>
|
<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>
|
<script>
|
||||||
@time = new Date @opts.time
|
this.time = new Date(this.opts.time);
|
||||||
@mode = @opts.mode || \relative
|
this.mode = this.opts.mode || 'relative';
|
||||||
@tickid = null
|
this.tickid = null;
|
||||||
|
|
||||||
@absolute =
|
this.absolute =
|
||||||
@time.get-full-year! + \年 +
|
this.time.getFullYear() + '年' +
|
||||||
@time.get-month! + 1 + \月 +
|
this.time.getMonth() + 1 + '月' +
|
||||||
@time.get-date! + \日 +
|
this.time.getDate() + '日' +
|
||||||
' ' +
|
' ' +
|
||||||
@time.get-hours! + \時 +
|
this.time.getHours() + '時' +
|
||||||
@time.get-minutes! + \分
|
this.time.getMinutes() + '分';
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
if @mode == \relative or @mode == \detail
|
if (this.mode == 'relative' || this.mode == 'detail') {
|
||||||
@tick!
|
this.tick();
|
||||||
@tickid = set-interval @tick, 1000ms
|
this.tickid = setInterval(this.tick, 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
@on \unmount ~>
|
this.on('unmount', () => {
|
||||||
if @mode == \relative or @mode == \detail
|
if (this.mode === 'relative' || this.mode === 'detail') {
|
||||||
clear-interval @tickid
|
clearInterval(this.tickid);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
@tick = ~>
|
this.tick = () => {
|
||||||
now = new Date!
|
const now = new Date();
|
||||||
ago = (now - @time) / 1000ms
|
const ago = (now - this.time) / 1000/*ms*/;
|
||||||
@relative = switch
|
this.relative =
|
||||||
| ago >= 31536000s => ~~(ago / 31536000s) + '年前'
|
ago >= 31536000 ? ~~(ago / 31536000) + '年前' :
|
||||||
| ago >= 2592000s => ~~(ago / 2592000s) + 'ヶ月前'
|
ago >= 2592000 ? ~~(ago / 2592000) + 'ヶ月前' :
|
||||||
| ago >= 604800s => ~~(ago / 604800s) + '週間前'
|
ago >= 604800 ? ~~(ago / 604800) + '週間前' :
|
||||||
| ago >= 86400s => ~~(ago / 86400s) + '日前'
|
ago >= 86400 ? ~~(ago / 86400) + '日前' :
|
||||||
| ago >= 3600s => ~~(ago / 3600s) + '時間前'
|
ago >= 3600 ? ~~(ago / 3600) + '時間前' :
|
||||||
| ago >= 60s => ~~(ago / 60s) + '分前'
|
ago >= 60 ? ~~(ago / 60) + '分前' :
|
||||||
| ago >= 10s => ~~(ago % 60s) + '秒前'
|
ago >= 10 ? ~~(ago % 60) + '秒前' :
|
||||||
| ago >= 0s => 'たった今'
|
ago >= 0 ? 'たった今' :
|
||||||
| ago < 0s => '未来'
|
ago < 0 ? '未来' :
|
||||||
| _ => 'なぞのじかん'
|
'なぞのじかん';
|
||||||
@update!
|
this.update();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-time>
|
</mk-time>
|
||||||
|
|
|
@ -24,6 +24,6 @@
|
||||||
color #8899a6
|
color #8899a6
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \i
|
this.mixin('i');
|
||||||
</script>
|
</script>
|
||||||
</mk-twitter-setting>
|
</mk-twitter-setting>
|
||||||
|
|
|
@ -140,56 +140,59 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \i
|
this.mixin('i');
|
||||||
|
|
||||||
@uploads = []
|
this.uploads = [];
|
||||||
|
|
||||||
|
this.upload = (file, folder) => {
|
||||||
@upload = (file, folder) ~>
|
const id = Math.random();
|
||||||
id = Math.random!
|
|
||||||
|
|
||||||
ctx =
|
const ctx = {
|
||||||
id: id
|
id: id,
|
||||||
name: file.name || \untitled
|
name: file.name || 'untitled',
|
||||||
progress: undefined
|
progress: undefined
|
||||||
|
};
|
||||||
|
|
||||||
@uploads.push ctx
|
this.uploads.push(ctx);
|
||||||
@trigger \change-uploads @uploads
|
this.trigger('change-uploads', this.uploads);
|
||||||
@update!
|
this.update();
|
||||||
|
|
||||||
reader = new FileReader!
|
const reader = new FileReader();
|
||||||
reader.onload = (e) ~>
|
reader.onload = e => {
|
||||||
ctx.img = e.target.result
|
ctx.img = e.target.result;
|
||||||
@update!
|
this.update();
|
||||||
reader.read-as-data-URL file
|
};
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
|
||||||
data = new FormData!
|
const data = new FormData();
|
||||||
data.append \i @I.token
|
data.append('i', this.I.token);
|
||||||
data.append \file file
|
data.append('file', file);
|
||||||
|
|
||||||
if folder?
|
if (folder) data.append('folder_id', folder);
|
||||||
data.append \folder_id folder
|
|
||||||
|
|
||||||
xhr = new XMLHttpRequest!
|
const xhr = new XMLHttpRequest();
|
||||||
xhr.open \POST CONFIG.apiUrl + '/drive/files/create' true
|
xhr.open('POST', CONFIG.apiUrl + '/drive/files/create', true);
|
||||||
xhr.onload = (e) ~>
|
xhr.onload = e => {
|
||||||
drive-file = JSON.parse e.target.response
|
const driveFile = JSON.parse(e.target.response);
|
||||||
|
|
||||||
@trigger \uploaded drive-file
|
this.trigger('uploaded', driveFile);
|
||||||
|
|
||||||
@uploads = @uploads.filter (x) -> x.id != id
|
this.uploads = this.uploads.filter(x => x.id != id);
|
||||||
@trigger \change-uploads @uploads
|
this.trigger('change-uploads', this.uploads);
|
||||||
|
|
||||||
@update!
|
this.update();
|
||||||
|
};
|
||||||
|
|
||||||
xhr.upload.onprogress = (e) ~>
|
xhr.upload.onprogress = e => {
|
||||||
if e.length-computable
|
if (e.lengthComputable) {
|
||||||
if ctx.progress == undefined
|
if (ctx.progress == undefined) ctx.progress = {};
|
||||||
ctx.progress = {}
|
ctx.progress.max = e.total;
|
||||||
ctx.progress.max = e.total
|
ctx.progress.value = e.loaded;
|
||||||
ctx.progress.value = e.loaded
|
this.update();
|
||||||
@update!
|
}
|
||||||
|
};
|
||||||
|
|
||||||
xhr.send data
|
xhr.send(data);
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-uploader>
|
</mk-uploader>
|
||||||
|
|
|
@ -91,22 +91,24 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
|
|
||||||
@url = @opts.url
|
this.url = this.opts.url;
|
||||||
@loading = true
|
this.loading = true;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
fetch CONFIG.url + '/api:url?url=' + @url
|
fetch(CONFIG.url + '/api:url?url=' + this.url).then(res => {
|
||||||
.then (res) ~>
|
res.json().then(info => {
|
||||||
info <~ res.json!.then
|
this.title = info.title;
|
||||||
@title = info.title
|
this.description = info.description;
|
||||||
@description = info.description
|
this.thumbnail = info.thumbnail;
|
||||||
@thumbnail = info.thumbnail
|
this.icon = info.icon;
|
||||||
@icon = info.icon
|
this.sitename = info.sitename;
|
||||||
@sitename = info.sitename
|
|
||||||
|
|
||||||
@loading = false
|
this.loading = false;
|
||||||
@update!
|
this.update();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</mk-url-preview>
|
</mk-url-preview>
|
||||||
|
|
|
@ -30,19 +30,20 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@url = @opts.href
|
this.url = this.opts.href;
|
||||||
|
|
||||||
@on \before-mount ~>
|
this.on('before-mount', () => {
|
||||||
parser = document.create-element \a
|
parser = document.createElement('a');
|
||||||
parser.href = @url
|
parser.href = this.url;
|
||||||
|
|
||||||
@schema = parser.protocol
|
this.schema = parser.protocol;
|
||||||
@hostname = parser.hostname
|
this.hostname = parser.hostname;
|
||||||
@port = parser.port
|
this.port = parser.port;
|
||||||
@pathname = parser.pathname
|
this.pathname = parser.pathname;
|
||||||
@query = parser.search
|
this.query = parser.search;
|
||||||
@hash = parser.hash
|
this.hash = parser.hash;
|
||||||
|
|
||||||
@update!
|
this.update();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</mk-url>
|
</mk-url>
|
||||||
|
|
|
@ -6,100 +6,90 @@
|
||||||
display block
|
display block
|
||||||
width 256px
|
width 256px
|
||||||
height 256px
|
height 256px
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@on \mount ~>
|
const Vec2 = function(x, y) {
|
||||||
@draw!
|
this.x = x;
|
||||||
@clock = set-interval @draw, 1000ms
|
this.y = y;
|
||||||
|
};
|
||||||
|
|
||||||
@on \unmount ~>
|
this.on('mount', () => {
|
||||||
clear-interval @clock
|
this.draw()
|
||||||
|
this.clock = setInterval(this.draw, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
@draw = ~>
|
this.on('unmount', () => {
|
||||||
now = new Date!
|
clearInterval(this.clock);
|
||||||
s = now.get-seconds!
|
});
|
||||||
m = now.get-minutes!
|
|
||||||
h = now.get-hours!
|
|
||||||
|
|
||||||
vec2 = (x, y) ->
|
this.draw = () => {
|
||||||
@x = x
|
const now = new Date();
|
||||||
@y = y
|
const s = now.getSeconds();
|
||||||
|
const m = now.getMinutes();
|
||||||
|
const h = now.getHours();
|
||||||
|
|
||||||
ctx = @refs.canvas.get-context \2d
|
const ctx = this.refs.canvas.getContext('2d');
|
||||||
canv-w = @refs.canvas.width
|
const canvW = this.refs.canvas.width;
|
||||||
canv-h = @refs.canvas.height
|
const canvH = this.refs.canvas.height;
|
||||||
ctx.clear-rect 0, 0, canv-w, canv-h
|
ctx.clearRect(0, 0, canvW, canvH);
|
||||||
|
|
||||||
# 背景
|
{ // 背景
|
||||||
center = (Math.min (canv-w / 2), (canv-h / 2))
|
const center = Math.min((canvW / 2), (canvH / 2));
|
||||||
line-start = center * 0.90
|
const lineStart = center * 0.90;
|
||||||
line-end-short = center * 0.87
|
const shortLineEnd = center * 0.87;
|
||||||
line-end-long = center * 0.84
|
const longLineEnd = center * 0.84;
|
||||||
for i from 0 to 59 by 1
|
for (let i = 0; i < 60; i++) {
|
||||||
angle = Math.PI * i / 30
|
const angle = Math.PI * i / 30;
|
||||||
uv = new vec2 (Math.sin angle), (-Math.cos angle)
|
const uv = new Vec2(Math.sin(angle), -Math.cos(angle));
|
||||||
ctx.begin-path!
|
ctx.beginPath();
|
||||||
ctx.line-width = 1
|
ctx.lineWidth = 1;
|
||||||
ctx.move-to do
|
ctx.moveTo((canvW / 2) + uv.x * lineStart, (canvH / 2) + uv.y * lineStart);
|
||||||
(canv-w / 2) + uv.x * line-start
|
if (i % 5 == 0) {
|
||||||
(canv-h / 2) + uv.y * line-start
|
ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
|
||||||
if i % 5 == 0
|
ctx.lineTo((canvW / 2) + uv.x * longLineEnd, (canvH / 2) + uv.y * longLineEnd);
|
||||||
ctx.stroke-style = 'rgba(255, 255, 255, 0.2)'
|
} else {
|
||||||
ctx.line-to do
|
ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)';
|
||||||
(canv-w / 2) + uv.x * line-end-long
|
ctx.lineTo((canvW / 2) + uv.x * shortLineEnd, (canvH / 2) + uv.y * shortLineEnd);
|
||||||
(canv-h / 2) + uv.y * line-end-long
|
}
|
||||||
else
|
ctx.stroke();
|
||||||
ctx.stroke-style = 'rgba(255, 255, 255, 0.1)'
|
}
|
||||||
ctx.line-to do
|
}
|
||||||
(canv-w / 2) + uv.x * line-end-short
|
|
||||||
(canv-h / 2) + uv.y * line-end-short
|
|
||||||
ctx.stroke!
|
|
||||||
|
|
||||||
# 分
|
{ // 分
|
||||||
angle = Math.PI * (m + s / 60) / 30
|
const angle = Math.PI * (m + s / 60) / 30;
|
||||||
length = (Math.min canv-w, canv-h) / 2.6
|
const length = Math.min(canvW, canvH) / 2.6;
|
||||||
uv = new vec2 (Math.sin angle), (-Math.cos angle)
|
const uv = new Vec2(Math.sin(angle), -Math.cos(angle));
|
||||||
ctx.begin-path!
|
ctx.beginPath();
|
||||||
ctx.stroke-style = \#ffffff
|
ctx.strokeStyle = '#ffffff';
|
||||||
ctx.line-width = 2
|
ctx.lineWidth = 2;
|
||||||
ctx.move-to do
|
ctx.moveTo(canvW / 2 - uv.x * length / 5, canvH / 2 - uv.y * length / 5);
|
||||||
(canv-w / 2) - uv.x * length / 5
|
ctx.lineTo(canvW / 2 + uv.x * length, canvH / 2 + uv.y * length);
|
||||||
(canv-h / 2) - uv.y * length / 5
|
ctx.stroke();
|
||||||
ctx.line-to do
|
}
|
||||||
(canv-w / 2) + uv.x * length
|
|
||||||
(canv-h / 2) + uv.y * length
|
|
||||||
ctx.stroke!
|
|
||||||
|
|
||||||
# 時
|
{ // 時
|
||||||
angle = Math.PI * (h % 12 + m / 60) / 6
|
const angle = Math.PI * (h % 12 + m / 60) / 6;
|
||||||
length = (Math.min canv-w, canv-h) / 4
|
const length = Math.min(canvW, canvH) / 4;
|
||||||
uv = new vec2 (Math.sin angle), (-Math.cos angle)
|
const uv = new Vec2(Math.sin(angle), -Math.cos(angle));
|
||||||
ctx.begin-path!
|
ctx.beginPath();
|
||||||
#ctx.stroke-style = \#ffffff
|
ctx.strokeStyle = CONFIG.themeColor;
|
||||||
ctx.stroke-style = CONFIG.theme-color
|
ctx.lineWidth = 2;
|
||||||
ctx.line-width = 2
|
ctx.moveTo(canvW / 2 - uv.x * length / 5, canvH / 2 - uv.y * length / 5);
|
||||||
ctx.move-to do
|
ctx.lineTo(canvW / 2 + uv.x * length, canvH / 2 + uv.y * length);
|
||||||
(canv-w / 2) - uv.x * length / 5
|
ctx.stroke();
|
||||||
(canv-h / 2) - uv.y * length / 5
|
}
|
||||||
ctx.line-to do
|
|
||||||
(canv-w / 2) + uv.x * length
|
|
||||||
(canv-h / 2) + uv.y * length
|
|
||||||
ctx.stroke!
|
|
||||||
|
|
||||||
# 秒
|
{ // 秒
|
||||||
angle = Math.PI * s / 30
|
const angle = Math.PI * s / 30;
|
||||||
length = (Math.min canv-w, canv-h) / 2.6
|
const length = Math.min(canvW, canvH) / 2.6;
|
||||||
uv = new vec2 (Math.sin angle), (-Math.cos angle)
|
const uv = new Vec2(Math.sin(angle), -Math.cos(angle));
|
||||||
ctx.begin-path!
|
ctx.beginPath();
|
||||||
ctx.stroke-style = 'rgba(255, 255, 255, 0.5)'
|
ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
|
||||||
ctx.line-width = 1
|
ctx.lineWidth = 1;
|
||||||
ctx.move-to do
|
ctx.moveTo(canvW / 2 - uv.x * length / 5, canvH / 2 - uv.y * length / 5);
|
||||||
(canv-w / 2) - uv.x * length / 5
|
ctx.lineTo(canvW / 2 + uv.x * length, canvH / 2 + uv.y * length);
|
||||||
(canv-h / 2) - uv.y * length / 5
|
ctx.stroke();
|
||||||
ctx.line-to do
|
}
|
||||||
(canv-w / 2) + uv.x * length
|
};
|
||||||
(canv-h / 2) + uv.y * length
|
|
||||||
ctx.stroke!
|
|
||||||
</script>
|
</script>
|
||||||
</mk-analog-clock>
|
</mk-analog-clock>
|
||||||
|
|
|
@ -80,108 +80,118 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
const contains = require('../../common/scripts/contains');
|
||||||
|
|
||||||
@q = @opts.q
|
this.mixin('api');
|
||||||
@textarea = @opts.textarea
|
|
||||||
@loading = true
|
|
||||||
@users = []
|
|
||||||
@select = -1
|
|
||||||
|
|
||||||
@on \mount ~>
|
this.q = this.opts.q;
|
||||||
@textarea.add-event-listener \keydown @on-keydown
|
this.textarea = this.opts.textarea;
|
||||||
|
this.fetching = true;
|
||||||
|
this.users = [];
|
||||||
|
this.select = -1;
|
||||||
|
|
||||||
all = document.query-selector-all 'body *'
|
this.on('mount', () => {
|
||||||
Array.prototype.for-each.call all, (el) ~>
|
this.textarea.addEventListener('keydown', this.onKeydown);
|
||||||
el.add-event-listener \mousedown @mousedown
|
|
||||||
|
|
||||||
@api \users/search_by_username do
|
document.querySelectorAll('body *').forEach(el => {
|
||||||
query: @q
|
el.addEventListener('mousedown', this.mousedown);
|
||||||
limit: 30users
|
});
|
||||||
.then (users) ~>
|
|
||||||
@users = users
|
|
||||||
@loading = false
|
|
||||||
@update!
|
|
||||||
.catch (err) ~>
|
|
||||||
console.error err
|
|
||||||
|
|
||||||
@on \unmount ~>
|
this.api('users/search_by_username', {
|
||||||
@textarea.remove-event-listener \keydown @on-keydown
|
query: this.q,
|
||||||
|
limit: 30
|
||||||
|
}).then(users => {
|
||||||
|
this.update({
|
||||||
|
fetching: false,
|
||||||
|
users: users
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
all = document.query-selector-all 'body *'
|
this.on('unmount', () => {
|
||||||
Array.prototype.for-each.call all, (el) ~>
|
this.textarea.removeEventListener('keydown', this.onKeydown);
|
||||||
el.remove-event-listener \mousedown @mousedown
|
|
||||||
|
|
||||||
@mousedown = (e) ~>
|
document.querySelectorAll('body *').forEach(el => {
|
||||||
if (!contains @root, e.target) and (@root != e.target)
|
el.removeEventListener('mousedown', this.mousedown);
|
||||||
@close!
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@on-click = (e) ~>
|
this.mousedown = e => {
|
||||||
@complete e.item
|
if (!contains(this.root, e.target) && (this.root != e.target)) this.close();
|
||||||
|
};
|
||||||
|
|
||||||
@on-keydown = (e) ~>
|
this.onClick = e => {
|
||||||
key = e.which
|
this.complete(e.item);
|
||||||
switch (key)
|
};
|
||||||
| 10, 13 => # Key[ENTER]
|
|
||||||
if @select != -1
|
|
||||||
e.prevent-default!
|
|
||||||
e.stop-propagation!
|
|
||||||
@complete @users[@select]
|
|
||||||
else
|
|
||||||
@close!
|
|
||||||
| 27 => # Key[ESC]
|
|
||||||
e.prevent-default!
|
|
||||||
e.stop-propagation!
|
|
||||||
@close!
|
|
||||||
| 38 => # Key[↑]
|
|
||||||
if @select != -1
|
|
||||||
e.prevent-default!
|
|
||||||
e.stop-propagation!
|
|
||||||
@select-prev!
|
|
||||||
else
|
|
||||||
@close!
|
|
||||||
| 9, 40 => # Key[TAB] or Key[↓]
|
|
||||||
e.prevent-default!
|
|
||||||
e.stop-propagation!
|
|
||||||
@select-next!
|
|
||||||
| _ =>
|
|
||||||
@close!
|
|
||||||
|
|
||||||
@select-next = ~>
|
this.onKeydown = e => {
|
||||||
@select++
|
const cancel = () => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
if @select >= @users.length
|
switch (e.which) {
|
||||||
@select = 0
|
case 10: // [ENTER]
|
||||||
|
case 13: // [ENTER]
|
||||||
|
if (this.select !== -1) {
|
||||||
|
cancel();
|
||||||
|
this.complete(this.users[this.select]);
|
||||||
|
} else {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
@apply-select!
|
case 27: // [ESC]
|
||||||
|
cancel();
|
||||||
|
this.close();
|
||||||
|
break;
|
||||||
|
|
||||||
@select-prev = ~>
|
case 38: // [↑]
|
||||||
@select--
|
if (this.select !== -1) {
|
||||||
|
cancel();
|
||||||
|
this.selectPrev();
|
||||||
|
} else {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
if @select < 0
|
case 9: // [TAB]
|
||||||
@select = @users.length - 1
|
case 40: // [↓]
|
||||||
|
cancel();
|
||||||
|
this.selectNext();
|
||||||
|
break;
|
||||||
|
|
||||||
@apply-select!
|
default:
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@apply-select = ~>
|
this.selectNext = () => {
|
||||||
@refs.users.children.for-each (el) ~>
|
if (++this.select >= this.users.length) this.select = 0;
|
||||||
el.remove-attribute \data-selected
|
this.applySelect();
|
||||||
|
};
|
||||||
|
|
||||||
@refs.users.children[@select].set-attribute \data-selected \true
|
this.selectPrev = () => {
|
||||||
@refs.users.children[@select].focus!
|
if (--this.select < 0) this.select = this.users.length - 1;
|
||||||
|
this.applySelect();
|
||||||
|
};
|
||||||
|
|
||||||
@complete = (user) ~>
|
this.applySelect = () => {
|
||||||
@opts.complete user
|
this.refs.users.children.forEach(el => {
|
||||||
|
el.removeAttribute('data-selected');
|
||||||
|
});
|
||||||
|
|
||||||
@close = ~>
|
this.refs.users.children[this.select].setAttribute('data-selected', 'true');
|
||||||
@opts.close!
|
this.refs.users.children[this.select].focus();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.complete = user => {
|
||||||
|
this.opts.complete(user);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.close = () => {
|
||||||
|
this.opts.close();
|
||||||
|
};
|
||||||
|
|
||||||
function contains(parent, child)
|
|
||||||
node = child.parent-node
|
|
||||||
while node?
|
|
||||||
if node == parent
|
|
||||||
return true
|
|
||||||
node = node.parent-node
|
|
||||||
return false
|
|
||||||
</script>
|
</script>
|
||||||
</mk-autocomplete-suggestion>
|
</mk-autocomplete-suggestion>
|
||||||
|
|
|
@ -70,58 +70,74 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
@mixin \is-promise
|
this.mixin('is-promise');
|
||||||
@mixin \stream
|
this.mixin('stream');
|
||||||
|
|
||||||
@user = null
|
this.user = null;
|
||||||
@user-promise = if @is-promise @opts.user then @opts.user else Promise.resolve @opts.user
|
this.userPromise = this.isPromise(this.opts.user)
|
||||||
@init = true
|
? this.opts.user
|
||||||
@wait = false
|
: Promise.resolve(this.opts.user);
|
||||||
|
this.init = true;
|
||||||
|
this.wait = false;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@user-promise.then (user) ~>
|
this.userPromise.then(user => {
|
||||||
@user = user
|
this.update({
|
||||||
@init = false
|
init: false,
|
||||||
@update!
|
user: user
|
||||||
@stream.on \follow @on-stream-follow
|
});
|
||||||
@stream.on \unfollow @on-stream-unfollow
|
this.stream.on('follow', this.onStreamFollow);
|
||||||
|
this.stream.on('unfollow', this.onStreamUnfollow);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@on \unmount ~>
|
this.on('unmount', () => {
|
||||||
@stream.off \follow @on-stream-follow
|
this.stream.off('follow', this.onStreamFollow);
|
||||||
@stream.off \unfollow @on-stream-unfollow
|
this.stream.off('unfollow', this.onStreamUnfollow);
|
||||||
|
});
|
||||||
|
|
||||||
@on-stream-follow = (user) ~>
|
this.onStreamFollow = user => {
|
||||||
if user.id == @user.id
|
if (user.id == this.user.id) {
|
||||||
@user = user
|
this.update({
|
||||||
@update!
|
user: user
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@on-stream-unfollow = (user) ~>
|
this.onStreamUnfollow = user => {
|
||||||
if user.id == @user.id
|
if (user.id == this.user.id) {
|
||||||
@user = user
|
this.update({
|
||||||
@update!
|
user: user
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@onclick = ~>
|
this.onclick = () => {
|
||||||
@wait = true
|
this.wait = true;
|
||||||
if @user.is_following
|
if (this.user.is_following) {
|
||||||
@api \following/delete do
|
this.api('following/delete', {
|
||||||
user_id: @user.id
|
user_id: this.user.id
|
||||||
.then ~>
|
}).then(() => {
|
||||||
@user.is_following = false
|
this.user.is_following = false;
|
||||||
.catch (err) ->
|
}).catch(err => {
|
||||||
console.error err
|
console.error(err);
|
||||||
.then ~>
|
}).then(() => {
|
||||||
@wait = false
|
this.wait = false;
|
||||||
@update!
|
this.update();
|
||||||
else
|
});
|
||||||
@api \following/create do
|
} else {
|
||||||
user_id: @user.id
|
this.api('following/create', {
|
||||||
.then ~>
|
user_id: this.user.id
|
||||||
@user.is_following = true
|
}).then(() => {
|
||||||
.catch (err) ->
|
this.user.is_following = true;
|
||||||
console.error err
|
}).catch(err => {
|
||||||
.then ~>
|
console.error(err);
|
||||||
@wait = false
|
}).then(() => {
|
||||||
@update!
|
this.wait = false;
|
||||||
|
this.update();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-big-follow-button>
|
</mk-big-follow-button>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<mk-contextmenu><yield />
|
<mk-contextmenu>
|
||||||
|
<yield />
|
||||||
<style>
|
<style>
|
||||||
:scope
|
:scope
|
||||||
$width = 240px
|
$width = 240px
|
||||||
|
@ -94,46 +95,45 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@root.add-event-listener \contextmenu (e) ~>
|
const contains = require('../../common/scripts/contains');
|
||||||
e.prevent-default!
|
|
||||||
|
|
||||||
@mousedown = (e) ~>
|
this.root.addEventListener('contextmenu', e => {
|
||||||
e.prevent-default!
|
e.preventDefault();
|
||||||
if (!contains @root, e.target) and (@root != e.target)
|
});
|
||||||
@close!
|
|
||||||
return false
|
|
||||||
|
|
||||||
@open = (pos) ~>
|
this.mousedown = e => {
|
||||||
all = document.query-selector-all 'body *'
|
e.preventDefault();
|
||||||
Array.prototype.for-each.call all, (el) ~>
|
if (!contains(this.root, e.target) && (this.root != e.target)) this.close();
|
||||||
el.add-event-listener \mousedown @mousedown
|
return false;
|
||||||
@root.style.display = \block
|
};
|
||||||
@root.style.left = pos.x + \px
|
|
||||||
@root.style.top = pos.y + \px
|
|
||||||
|
|
||||||
Velocity @root, \finish true
|
this.open = pos => {
|
||||||
Velocity @root, { opacity: 0 } 0ms
|
document.querySelectorAll('body *').forEach(el => {
|
||||||
Velocity @root, {
|
el.addEventListener('mousedown', this.mousedown);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.root.style.display = 'block';
|
||||||
|
this.root.style.left = pos.x + 'px';
|
||||||
|
this.root.style.top = pos.y + 'px';
|
||||||
|
|
||||||
|
Velocity(this.root, 'finish', true);
|
||||||
|
Velocity(this.root, { opacity: 0 }, 0);
|
||||||
|
Velocity(this.root, {
|
||||||
opacity: 1
|
opacity: 1
|
||||||
} {
|
}, {
|
||||||
queue: false
|
queue: false,
|
||||||
duration: 100ms
|
duration: 100,
|
||||||
easing: \linear
|
easing: 'linear'
|
||||||
}
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@close = ~>
|
this.close = () => {
|
||||||
all = document.query-selector-all 'body *'
|
document.querySelectorAll('body *').forEach(el => {
|
||||||
Array.prototype.for-each.call all, (el) ~>
|
el.removeEventListener('mousedown', this.mousedown);
|
||||||
el.remove-event-listener \mousedown @mousedown
|
});
|
||||||
@trigger \closed
|
|
||||||
@unmount!
|
|
||||||
|
|
||||||
function contains(parent, child)
|
this.trigger('closed');
|
||||||
node = child.parent-node
|
this.unmount();
|
||||||
while (node != null)
|
};
|
||||||
if (node == parent)
|
|
||||||
return true
|
|
||||||
node = node.parent-node
|
|
||||||
return false
|
|
||||||
</script>
|
</script>
|
||||||
</mk-contextmenu>
|
</mk-contextmenu>
|
||||||
|
|
|
@ -158,31 +158,37 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \cropper
|
this.mixin('cropper');
|
||||||
|
|
||||||
@image = @opts.file
|
this.image = this.opts.file;
|
||||||
@title = @opts.title
|
this.title = this.opts.title;
|
||||||
@aspect-ratio = @opts.aspect-ratio
|
this.aspectRatio = this.opts.aspectRatio;
|
||||||
@cropper = null
|
this.cropper = null;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@img = @refs.window.refs.img
|
this.img = this.refs.window.refs.img;
|
||||||
@cropper = new @Cropper @img, do
|
this.cropper = new this.Cropper(this.img, {
|
||||||
aspect-ratio: @aspect-ratio
|
aspectRatio: this.aspectRatio,
|
||||||
highlight: no
|
highlight: false,
|
||||||
view-mode: 1
|
viewMode: 1
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@ok = ~>
|
this.ok = () => {
|
||||||
@cropper.get-cropped-canvas!.to-blob (blob) ~>
|
this.cropper.getCroppedCanvas().toBlob(blob => {
|
||||||
@trigger \cropped blob
|
this.trigger('cropped', blob);
|
||||||
@refs.window.close!
|
this.refs.window.close();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@skip = ~>
|
this.skip = () => {
|
||||||
@trigger \skiped
|
this.trigger('skiped');
|
||||||
@refs.window.close!
|
this.refs.window.close();
|
||||||
|
};
|
||||||
|
|
||||||
@cancel = ~>
|
this.cancel = () => {
|
||||||
@trigger \canceled
|
this.trigger('canceled');
|
||||||
@refs.window.close!
|
this.refs.window.close();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-crop-window>
|
</mk-crop-window>
|
||||||
|
|
|
@ -79,69 +79,72 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@can-through = if opts.can-through? then opts.can-through else true
|
this.canThrough = opts.canThrough != null ? opts.canThrough : true;
|
||||||
@opts.buttons.for-each (button) ~>
|
this.opts.buttons.forEach(button => {
|
||||||
button._onclick = ~>
|
button._onclick = () => {
|
||||||
if button.onclick?
|
if (button.onclick) button.onclick();
|
||||||
button.onclick!
|
this.close();
|
||||||
@close!
|
};
|
||||||
|
});
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@refs.header.innerHTML = @opts.title
|
this.refs.header.innerHTML = this.opts.title;
|
||||||
@refs.body.innerHTML = @opts.text
|
this.refs.body.innerHTML = this.opts.text;
|
||||||
|
|
||||||
@refs.bg.style.pointer-events = \auto
|
this.refs.bg.style.pointerEvents = 'auto';
|
||||||
Velocity @refs.bg, \finish true
|
Velocity(this.refs.bg, 'finish', true);
|
||||||
Velocity @refs.bg, {
|
Velocity(this.refs.bg, {
|
||||||
opacity: 1
|
opacity: 1
|
||||||
} {
|
}, {
|
||||||
queue: false
|
queue: false,
|
||||||
duration: 100ms
|
duration: 100,
|
||||||
easing: \linear
|
easing: 'linear'
|
||||||
}
|
});
|
||||||
|
|
||||||
Velocity @refs.main, {
|
Velocity(this.refs.main, {
|
||||||
opacity: 0
|
opacity: 0,
|
||||||
scale: 1.2
|
scale: 1.2
|
||||||
} {
|
}, {
|
||||||
duration: 0
|
duration: 0
|
||||||
}
|
});
|
||||||
Velocity @refs.main, {
|
Velocity(this.refs.main, {
|
||||||
opacity: 1
|
opacity: 1,
|
||||||
scale: 1
|
scale: 1
|
||||||
} {
|
}, {
|
||||||
duration: 300ms
|
duration: 300,
|
||||||
easing: [ 0, 0.5, 0.5, 1 ]
|
easing: [ 0, 0.5, 0.5, 1 ]
|
||||||
}
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@close = ~>
|
this.close = () => {
|
||||||
@refs.bg.style.pointer-events = \none
|
this.refs.bg.style.pointerEvents = 'none';
|
||||||
Velocity @refs.bg, \finish true
|
Velocity(this.refs.bg, 'finish', true);
|
||||||
Velocity @refs.bg, {
|
Velocity(this.refs.bg, {
|
||||||
opacity: 0
|
opacity: 0
|
||||||
} {
|
}, {
|
||||||
queue: false
|
queue: false,
|
||||||
duration: 300ms
|
duration: 300,
|
||||||
easing: \linear
|
easing: 'linear'
|
||||||
}
|
});
|
||||||
|
|
||||||
@refs.main.style.pointer-events = \none
|
this.refs.main.style.pointerEvents = 'none';
|
||||||
Velocity @refs.main, \finish true
|
Velocity(this.refs.main, 'finish', true);
|
||||||
Velocity @refs.main, {
|
Velocity(this.refs.main, {
|
||||||
opacity: 0
|
opacity: 0,
|
||||||
scale: 0.8
|
scale: 0.8
|
||||||
} {
|
}, {
|
||||||
queue: false
|
queue: false,
|
||||||
duration: 300ms
|
duration: 300,
|
||||||
easing: [ 0.5, -0.5, 1, 0.5 ]
|
easing: [ 0.5, -0.5, 1, 0.5 ],
|
||||||
complete: ~>
|
complete: () => this.unmount()
|
||||||
@unmount!
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
@bg-click = ~>
|
this.bgClick = () => {
|
||||||
if @can-through
|
if (this.canThrough) {
|
||||||
if @opts.on-through?
|
if (this.opts.onThrough) this.opts.onThrough();
|
||||||
@opts.on-through!
|
this.close();
|
||||||
@close!
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-dialog>
|
</mk-dialog>
|
||||||
|
|
|
@ -47,21 +47,22 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
@mixin \i
|
this.mixin('i');
|
||||||
|
|
||||||
@close = (e) ~>
|
this.close = e => {
|
||||||
e.prevent-default!
|
e.preventDefault();
|
||||||
e.stop-propagation!
|
e.stopPropagation();
|
||||||
|
|
||||||
@I.data.no_donation = true
|
this.I.data.no_donation = true;
|
||||||
@I.update!
|
this.I.update();
|
||||||
@api \i/appdata/set do
|
this.api('i/appdata/set', {
|
||||||
data: JSON.stringify do
|
data: JSON.stringify({
|
||||||
no_donation: @I.data.no_donation
|
no_donation: this.I.data.no_donation
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
@unmount!
|
this.unmount();
|
||||||
|
};
|
||||||
@parent.parent.set-root-layout!
|
|
||||||
</script>
|
</script>
|
||||||
</mk-donation>
|
</mk-donation>
|
||||||
|
|
|
@ -13,26 +13,32 @@
|
||||||
</ul>
|
</ul>
|
||||||
</mk-contextmenu>
|
</mk-contextmenu>
|
||||||
<script>
|
<script>
|
||||||
@browser = @opts.browser
|
this.browser = this.opts.browser;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@refs.ctx.on \closed ~>
|
this.refs.ctx.on('closed', () => {
|
||||||
@trigger \closed
|
this.trigger('closed');
|
||||||
@unmount!
|
this.unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@open = (pos) ~>
|
this.open = pos => {
|
||||||
@refs.ctx.open pos
|
this.refs.ctx.open(pos);
|
||||||
|
};
|
||||||
|
|
||||||
@create-folder = ~>
|
this.createFolder = () => {
|
||||||
@browser.create-folder!
|
this.browser.createFolder();
|
||||||
@refs.ctx.close!
|
this.refs.ctx.close();
|
||||||
|
};
|
||||||
|
|
||||||
@upload = ~>
|
this.upload = () => {
|
||||||
@browser.select-local-file!
|
this.browser.selectLocalFile();
|
||||||
@refs.ctx.close!
|
this.refs.ctx.close();
|
||||||
|
};
|
||||||
|
|
||||||
@url-upload = ~>
|
this.urlUpload = () => {
|
||||||
@browser.url-upload!
|
this.browser.urlUpload();
|
||||||
@refs.ctx.close!
|
this.refs.ctx.close();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-drive-browser-base-contextmenu>
|
</mk-drive-browser-base-contextmenu>
|
||||||
|
|
|
@ -28,19 +28,24 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
|
|
||||||
@folder = if @opts.folder? then @opts.folder else null
|
this.folder = this.opts.folder ? this.opts.folder : null;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@refs.window.on \closed ~>
|
this.refs.window.on('closed', () => {
|
||||||
@unmount!
|
this.unmount();
|
||||||
|
});
|
||||||
|
|
||||||
@api \drive .then (info) ~>
|
this.api('drive').then(info => {
|
||||||
@update do
|
this.update({
|
||||||
usage: info.usage / info.capacity * 100
|
usage: info.usage / info.capacity * 100
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@close = ~>
|
this.close = () => {
|
||||||
@refs.window.close!
|
this.refs.window.close();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-drive-browser-window>
|
</mk-drive-browser-window>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
</div>
|
</div>
|
||||||
<input class="search" type="search" placeholder=" 検索"/>
|
<input class="search" type="search" placeholder=" 検索"/>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="main { uploading: uploads.length > 0, loading: loading }" ref="main" onmousedown={ onmousedown } ondragover={ ondragover } ondragenter={ ondragenter } ondragleave={ ondragleave } ondrop={ ondrop } oncontextmenu={ oncontextmenu }>
|
<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="selection" ref="selection"></div>
|
||||||
<div class="contents" ref="contents">
|
<div class="contents" ref="contents">
|
||||||
<div class="folders" ref="foldersContainer" if={ folders.length > 0 }>
|
<div class="folders" ref="foldersContainer" if={ folders.length > 0 }>
|
||||||
|
@ -23,13 +23,13 @@
|
||||||
</virtual>
|
</virtual>
|
||||||
<button if={ moreFiles }>もっと読み込む</button>
|
<button if={ moreFiles }>もっと読み込む</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="empty" if={ files.length == 0 && folders.length == 0 && !loading }>
|
<div class="empty" if={ files.length == 0 && folders.length == 0 && !fetching }>
|
||||||
<p if={ draghover }>ドロップですか?いいですよ、ボクはカワイイですからね</p>
|
<p if={ draghover }>ドロップですか?いいですよ、ボクはカワイイですからね</p>
|
||||||
<p if={ !draghover && folder == null }><strong>ドライブには何もありません。</strong><br/>右クリックして「ファイルをアップロード」を選んだり、ファイルをドラッグ&ドロップすることでもアップロードできます。</p>
|
<p if={ !draghover && folder == null }><strong>ドライブには何もありません。</strong><br/>右クリックして「ファイルをアップロード」を選んだり、ファイルをドラッグ&ドロップすることでもアップロードできます。</p>
|
||||||
<p if={ !draghover && folder != null }>このフォルダーは空です</p>
|
<p if={ !draghover && folder != null }>このフォルダーは空です</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="loading" if={ loading }>
|
<div class="loading" if={ fetching }>
|
||||||
<div class="spinner">
|
<div class="spinner">
|
||||||
<div class="dot1"></div>
|
<div class="dot1"></div>
|
||||||
<div class="dot2"></div>
|
<div class="dot2"></div>
|
||||||
|
@ -133,7 +133,7 @@
|
||||||
&, *
|
&, *
|
||||||
user-select none
|
user-select none
|
||||||
|
|
||||||
&.loading
|
&.fetching
|
||||||
cursor wait !important
|
cursor wait !important
|
||||||
|
|
||||||
*
|
*
|
||||||
|
@ -184,7 +184,7 @@
|
||||||
> p
|
> p
|
||||||
margin 0
|
margin 0
|
||||||
|
|
||||||
> .loading
|
> .fetching
|
||||||
.spinner
|
.spinner
|
||||||
margin 100px auto
|
margin 100px auto
|
||||||
width 40px
|
width 40px
|
||||||
|
@ -238,418 +238,439 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
const contains = require('../../../common/scripts/contains');
|
||||||
@mixin \dialog
|
|
||||||
@mixin \input-dialog
|
this.mixin('api');
|
||||||
@mixin \stream
|
this.mixin('dialog');
|
||||||
|
this.mixin('input-dialog');
|
||||||
@files = []
|
this.mixin('stream');
|
||||||
@folders = []
|
|
||||||
@hierarchy-folders = []
|
this.files = [];
|
||||||
|
this.folders = [];
|
||||||
@uploads = []
|
this.hierarchyFolders = [];
|
||||||
|
|
||||||
# 現在の階層(フォルダ)
|
this.uploads = [];
|
||||||
# * null でルートを表す
|
|
||||||
@folder = null
|
// 現在の階層(フォルダ)
|
||||||
|
// * null でルートを表す
|
||||||
@multiple = if @opts.multiple? then @opts.multiple else false
|
this.folder = null;
|
||||||
|
|
||||||
# ドロップされようとしているか
|
this.multiple = this.opts.multiple != null ? this.opts.multiple : false;
|
||||||
@draghover = false
|
|
||||||
|
// ドロップされようとしているか
|
||||||
# 自信の所有するアイテムがドラッグをスタートさせたか
|
this.draghover = false;
|
||||||
# (自分自身の階層にドロップできないようにするためのフラグ)
|
|
||||||
@is-drag-source = false
|
// 自信の所有するアイテムがドラッグをスタートさせたか
|
||||||
|
// (自分自身の階層にドロップできないようにするためのフラグ)
|
||||||
@on \mount ~>
|
this.isDragSource = false;
|
||||||
@refs.uploader.on \uploaded (file) ~>
|
|
||||||
@add-file file, true
|
this.on('mount', () => {
|
||||||
|
this.refs.uploader.on('uploaded', file => {
|
||||||
@refs.uploader.on \change-uploads (uploads) ~>
|
this.addFile(file, true);
|
||||||
@uploads = uploads
|
});
|
||||||
@update!
|
|
||||||
|
this.refs.uploader.on('change-uploads', uploads => {
|
||||||
@stream.on \drive_file_created @on-stream-drive-file-created
|
this.update({
|
||||||
@stream.on \drive_file_updated @on-stream-drive-file-updated
|
uploads: uploads
|
||||||
@stream.on \drive_folder_created @on-stream-drive-folder-created
|
});
|
||||||
@stream.on \drive_folder_updated @on-stream-drive-folder-updated
|
});
|
||||||
|
|
||||||
# Riotのバグでnullを渡しても""になる
|
this.stream.on('drive_file_created', this.onStreamDriveFileCreated);
|
||||||
# https://github.com/riot/riot/issues/2080
|
this.stream.on('drive_file_updated', this.onStreamDriveFileUpdated);
|
||||||
#if @opts.folder?
|
this.stream.on('drive_folder_created', this.onStreamDriveFolderCreated);
|
||||||
if @opts.folder? and @opts.folder != ''
|
this.stream.on('drive_folder_updated', this.onStreamDriveFolderUpdated);
|
||||||
@move @opts.folder
|
|
||||||
else
|
// Riotのバグでnullを渡しても""になる
|
||||||
@load!
|
// https://github.com/riot/riot/issues/2080
|
||||||
|
//if (this.opts.folder)
|
||||||
@on \unmount ~>
|
if (this.opts.folder && this.opts.folder != '') {
|
||||||
@stream.off \drive_file_created @on-stream-drive-file-created
|
this.move(this.opts.folder);
|
||||||
@stream.off \drive_file_updated @on-stream-drive-file-updated
|
} else {
|
||||||
@stream.off \drive_folder_created @on-stream-drive-folder-created
|
this.load();
|
||||||
@stream.off \drive_folder_updated @on-stream-drive-folder-updated
|
}
|
||||||
|
});
|
||||||
@on-stream-drive-file-created = (file) ~>
|
|
||||||
@add-file file, true
|
this.on('unmount', () => {
|
||||||
|
this.stream.off('drive_file_created', this.onStreamDriveFileCreated);
|
||||||
@on-stream-drive-file-updated = (file) ~>
|
this.stream.off('drive_file_updated', this.onStreamDriveFileUpdated);
|
||||||
current = if @folder? then @folder.id else null
|
this.stream.off('drive_folder_created', this.onStreamDriveFolderCreated);
|
||||||
if current != file.folder_id
|
this.stream.off('drive_folder_updated', this.onStreamDriveFolderUpdated);
|
||||||
@remove-file file
|
});
|
||||||
else
|
|
||||||
@add-file file, true
|
this.onStreamDriveFileCreated = file => {
|
||||||
|
this.addFile(file, true);
|
||||||
@on-stream-drive-folder-created = (folder) ~>
|
};
|
||||||
@add-folder folder, true
|
|
||||||
|
this.onStreamDriveFileUpdated = file => {
|
||||||
@on-stream-drive-folder-updated = (folder) ~>
|
const current = this.folder ? this.folder.id : null;
|
||||||
current = if @folder? then @folder.id else null
|
if (current != file.folder_id) {
|
||||||
if current != folder.parent_id
|
this.removeFile(file);
|
||||||
@remove-folder folder
|
} else {
|
||||||
else
|
this.addFile(file, true);
|
||||||
@add-folder folder, true
|
}
|
||||||
|
};
|
||||||
@onmousedown = (e) ~>
|
|
||||||
if (contains @refs.folders-container, e.target) or (contains @refs.files-container, e.target)
|
this.onStreamDriveFolderCreated = folder => {
|
||||||
return true
|
this.addFolder(folder, true);
|
||||||
|
};
|
||||||
rect = @refs.main.get-bounding-client-rect!
|
|
||||||
|
this.onStreamDriveFolderUpdated = folder => {
|
||||||
left = e.page-x + @refs.main.scroll-left - rect.left - window.page-x-offset
|
const current = this.folder ? this.folder.id : null;
|
||||||
top = e.page-y + @refs.main.scroll-top - rect.top - window.page-y-offset
|
if (current != folder.parent_id) {
|
||||||
|
this.removeFolder(folder);
|
||||||
move = (e) ~>
|
} else {
|
||||||
@refs.selection.style.display = \block
|
this.addFolder(folder, true);
|
||||||
|
}
|
||||||
cursor-x = e.page-x + @refs.main.scroll-left - rect.left - window.page-x-offset
|
};
|
||||||
cursor-y = e.page-y + @refs.main.scroll-top - rect.top - window.page-y-offset
|
|
||||||
w = cursor-x - left
|
this.onmousedown = e => {
|
||||||
h = cursor-y - top
|
if (contains(this.refs.foldersContainer, e.target) || contains(this.refs.filesContainer, e.target)) return true;
|
||||||
|
|
||||||
if w > 0
|
const rect = this.refs.main.getBoundingClientRect();
|
||||||
@refs.selection.style.width = w + \px
|
|
||||||
@refs.selection.style.left = left + \px
|
const left = e.pageX + this.refs.main.scrollLeft - rect.left - window.pageXOffset
|
||||||
else
|
const top = e.pageY + this.refs.main.scrollTop - rect.top - window.pageYOffset
|
||||||
@refs.selection.style.width = -w + \px
|
|
||||||
@refs.selection.style.left = cursor-x + \px
|
const move = e => {
|
||||||
|
this.refs.selection.style.display = 'block';
|
||||||
if h > 0
|
|
||||||
@refs.selection.style.height = h + \px
|
const cursorX = e.pageX + this.refs.main.scrollLeft - rect.left - window.pageXOffset;
|
||||||
@refs.selection.style.top = top + \px
|
const cursorY = e.pageY + this.refs.main.scrollTop - rect.top - window.pageYOffset;
|
||||||
else
|
const w = cursorX - left;
|
||||||
@refs.selection.style.height = -h + \px
|
const h = cursorY - top;
|
||||||
@refs.selection.style.top = cursor-y + \px
|
|
||||||
|
if (w > 0) {
|
||||||
up = (e) ~>
|
this.refs.selection.style.width = w + 'px';
|
||||||
document.document-element.remove-event-listener \mousemove move
|
this.refs.selection.style.left = left + 'px';
|
||||||
document.document-element.remove-event-listener \mouseup up
|
} else {
|
||||||
|
this.refs.selection.style.width = -w + 'px';
|
||||||
@refs.selection.style.display = \none
|
this.refs.selection.style.left = cursorX + 'px';
|
||||||
|
}
|
||||||
document.document-element.add-event-listener \mousemove move
|
|
||||||
document.document-element.add-event-listener \mouseup up
|
if (h > 0) {
|
||||||
|
this.refs.selection.style.height = h + 'px';
|
||||||
@path-oncontextmenu = (e) ~>
|
this.refs.selection.style.top = top + 'px';
|
||||||
e.prevent-default!
|
} else {
|
||||||
e.stop-immediate-propagation!
|
this.refs.selection.style.height = -h + 'px';
|
||||||
return false
|
this.refs.selection.style.top = cursorY + 'px';
|
||||||
|
}
|
||||||
@ondragover = (e) ~>
|
};
|
||||||
e.prevent-default!
|
|
||||||
e.stop-propagation!
|
up = e => {
|
||||||
|
document.documentElement.removeEventListener('mousemove', move);
|
||||||
# ドラッグ元が自分自身の所有するアイテムかどうか
|
document.documentElement.removeEventListener('mouseup', up);
|
||||||
if !@is-drag-source
|
|
||||||
# ドラッグされてきたものがファイルだったら
|
this.refs.selection.style.display = 'none';
|
||||||
if e.data-transfer.effect-allowed == \all
|
};
|
||||||
e.data-transfer.drop-effect = \copy
|
|
||||||
else
|
document.documentElement.addEventListener('mousemove', move);
|
||||||
e.data-transfer.drop-effect = \move
|
document.documentElement.addEventListener('mouseup', up);
|
||||||
@draghover = true
|
};
|
||||||
else
|
|
||||||
# 自分自身にはドロップさせない
|
this.pathOncontextmenu = e => {
|
||||||
e.data-transfer.drop-effect = \none
|
e.preventDefault();
|
||||||
return false
|
e.stopImmediatePropagation();
|
||||||
|
return false;
|
||||||
@ondragenter = (e) ~>
|
};
|
||||||
e.prevent-default!
|
|
||||||
if !@is-drag-source
|
this.ondragover = e => {
|
||||||
@draghover = true
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
@ondragleave = (e) ~>
|
|
||||||
@draghover = false
|
// ドラッグ元が自分自身の所有するアイテムかどうか
|
||||||
|
if (!this.isDragSource) {
|
||||||
@ondrop = (e) ~>
|
// ドラッグされてきたものがファイルだったら
|
||||||
e.prevent-default!
|
e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move';
|
||||||
e.stop-propagation!
|
this.draghover = true;
|
||||||
|
} else {
|
||||||
@draghover = false
|
// 自分自身にはドロップさせない
|
||||||
|
e.dataTransfer.dropEffect = 'none';
|
||||||
# ドロップされてきたものがファイルだったら
|
return false;
|
||||||
if e.data-transfer.files.length > 0
|
}
|
||||||
Array.prototype.for-each.call e.data-transfer.files, (file) ~>
|
};
|
||||||
@upload file, @folder
|
|
||||||
return false
|
this.ondragenter = e => {
|
||||||
|
e.preventDefault();
|
||||||
# データ取得
|
if (!this.isDragSource) this.draghover = true;
|
||||||
data = e.data-transfer.get-data 'text'
|
};
|
||||||
if !data?
|
|
||||||
return false
|
this.ondragleave = e => {
|
||||||
|
this.draghover = false;
|
||||||
# パース
|
};
|
||||||
obj = JSON.parse data
|
|
||||||
|
this.ondrop = e => {
|
||||||
# (ドライブの)ファイルだったら
|
e.preventDefault();
|
||||||
if obj.type == \file
|
e.stopPropagation();
|
||||||
file = obj.id
|
|
||||||
if (@files.some (f) ~> f.id == file)
|
this.draghover = false;
|
||||||
return false
|
|
||||||
@remove-file file
|
// ドロップされてきたものがファイルだったら
|
||||||
@api \drive/files/update do
|
if (e.dataTransfer.files.length > 0) {
|
||||||
file_id: file
|
e.dataTransfer.files.forEach(file => {
|
||||||
folder_id: if @folder? then @folder.id else null
|
this.upload(file, this.folder);
|
||||||
.then ~>
|
});
|
||||||
# something
|
return false;
|
||||||
.catch (err, text-status) ~>
|
}
|
||||||
console.error err
|
|
||||||
|
// データ取得
|
||||||
# (ドライブの)フォルダーだったら
|
const data = e.dataTransfer.getData('text');
|
||||||
else if obj.type == \folder
|
if (data == null) return false;
|
||||||
folder = obj.id
|
|
||||||
# 移動先が自分自身ならreject
|
// パース
|
||||||
if @folder? and folder == @folder.id
|
// TODO: JSONじゃなかったら中断
|
||||||
return false
|
const obj = JSON.parse(data);
|
||||||
if (@folders.some (f) ~> f.id == folder)
|
|
||||||
return false
|
// (ドライブの)ファイルだったら
|
||||||
@remove-folder folder
|
if (obj.type == 'file') {
|
||||||
@api \drive/folders/update do
|
const file = obj.id;
|
||||||
folder_id: folder
|
if (this.files.some(f => f.id == file)) return false;
|
||||||
parent_id: if @folder? then @folder.id else null
|
this.removeFile(file);
|
||||||
.then ~>
|
this.api('drive/files/update', {
|
||||||
# something
|
file_id: file,
|
||||||
.catch (err) ~>
|
folder_id: this.folder ? this.folder.id : null
|
||||||
if err == 'detected-circular-definition'
|
});
|
||||||
@dialog do
|
// (ドライブの)フォルダーだったら
|
||||||
'<i class="fa fa-exclamation-triangle"></i>操作を完了できません'
|
} else if (obj.type == 'folder') {
|
||||||
'移動先のフォルダーは、移動するフォルダーのサブフォルダーです。'
|
const folder = obj.id;
|
||||||
[
|
// 移動先が自分自身ならreject
|
||||||
text: \OK
|
if (this.folder && folder == this.folder.id) return false;
|
||||||
]
|
if (this.folders.some(f => f.id == folder)) return false;
|
||||||
|
this.removeFolder(folder);
|
||||||
return false
|
this.api('drive/folders/update', {
|
||||||
|
folder_id: folder,
|
||||||
@oncontextmenu = (e) ~>
|
parent_id: this.folder ? this.folder.id : null
|
||||||
e.prevent-default!
|
}).then(() => {
|
||||||
e.stop-immediate-propagation!
|
// something
|
||||||
|
}).catch(err => {
|
||||||
ctx = document.body.append-child document.create-element \mk-drive-browser-base-contextmenu
|
switch (err) {
|
||||||
ctx = riot.mount ctx, do
|
case 'detected-circular-definition':
|
||||||
browser: @
|
this.dialog('<i class="fa fa-exclamation-triangle"></i>操作を完了できません',
|
||||||
ctx = ctx.0
|
'移動先のフォルダーは、移動するフォルダーのサブフォルダーです。', [{
|
||||||
ctx.open do
|
text: 'OK'
|
||||||
x: e.page-x - window.page-x-offset
|
}]);
|
||||||
y: e.page-y - window.page-y-offset
|
break;
|
||||||
|
default:
|
||||||
return false
|
alert('不明なエラー' + err);
|
||||||
|
}
|
||||||
@select-local-file = ~>
|
});
|
||||||
@refs.file-input.click!
|
}
|
||||||
|
|
||||||
@url-upload = ~>
|
return false;
|
||||||
url <~ @input-dialog do
|
};
|
||||||
'URLアップロード'
|
|
||||||
'アップロードしたいファイルのURL'
|
this.oncontextmenu = e => {
|
||||||
null
|
e.preventDefault();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
if url? and url != ''
|
|
||||||
@api \drive/files/upload_from_url do
|
const ctx = riot.mount(document.body.appendChild(document.createElement('mk-drive-browser-base-contextmenu')), {
|
||||||
url: url
|
browser: this
|
||||||
folder_id: if @folder? then @folder.id else undefined
|
})[0];
|
||||||
|
ctx.open({
|
||||||
@dialog do
|
x: e.pageX - window.pageXOffset,
|
||||||
'<i class="fa fa-check"></i>アップロードをリクエストしました'
|
y: e.pageY - window.pageYOffset
|
||||||
'アップロードが完了するまで時間がかかる場合があります。'
|
});
|
||||||
[
|
|
||||||
text: \OK
|
return false;
|
||||||
]
|
};
|
||||||
|
|
||||||
@create-folder = ~>
|
this.selectLocalFile = () => {
|
||||||
name <~ @input-dialog do
|
this.refs.fileInput.click();
|
||||||
'フォルダー作成'
|
};
|
||||||
'フォルダー名'
|
|
||||||
null
|
this.urlUpload = () => {
|
||||||
|
this.inputDialog('URLアップロード', 'アップロードしたいファイルのURL', null, url => {
|
||||||
@api \drive/folders/create do
|
this.api('drive/files/upload_from_url', {
|
||||||
name: name
|
url: url,
|
||||||
folder_id: if @folder? then @folder.id else undefined
|
folder_id: this.folder ? this.folder.id : undefined
|
||||||
.then (folder) ~>
|
});
|
||||||
@add-folder folder, true
|
|
||||||
@update!
|
this.dialog('<i class="fa fa-check"></i>アップロードをリクエストしました',
|
||||||
.catch (err) ~>
|
'アップロードが完了するまで時間がかかる場合があります。', [{
|
||||||
console.error err
|
text: 'OK'
|
||||||
|
}]);
|
||||||
@change-file-input = ~>
|
});
|
||||||
files = @refs.file-input.files
|
};
|
||||||
for i from 0 to files.length - 1
|
|
||||||
file = files.item i
|
this.createFolder = () => {
|
||||||
@upload file, @folder
|
this.inputDialog('フォルダー作成', 'フォルダー名', null, name => {
|
||||||
|
this.api('drive/folders/create', {
|
||||||
@upload = (file, folder) ~>
|
name: name,
|
||||||
if folder? and typeof folder == \object
|
folder_id: this.folder ? this.folder.id : undefined
|
||||||
folder = folder.id
|
}).then(folder => {
|
||||||
@refs.uploader.upload file, folder
|
this.addFolder(folder, true);
|
||||||
|
this.update();
|
||||||
@get-selection = ~>
|
});
|
||||||
@files.filter (file) -> file._selected
|
});
|
||||||
|
};
|
||||||
@new-window = (folder-id) ~>
|
|
||||||
browser = document.body.append-child document.create-element \mk-drive-browser-window
|
this.changeFileInput = () => {
|
||||||
riot.mount browser, do
|
this.refs.fileInput.files.forEach(file => {
|
||||||
folder: folder-id
|
this.upload(file, this.folder);
|
||||||
|
});
|
||||||
@move = (target-folder) ~>
|
};
|
||||||
if target-folder? and typeof target-folder == \object
|
|
||||||
target-folder = target-folder.id
|
this.upload = (file, folder) => {
|
||||||
|
if (folder && typeof folder == 'object') folder = folder.id;
|
||||||
if target-folder == null
|
this.refs.uploader.upload(file, folder);
|
||||||
@go-root!
|
};
|
||||||
return
|
|
||||||
|
this.getSelection = () => {
|
||||||
@loading = true
|
this.files.filter(file => file._selected);
|
||||||
@update!
|
};
|
||||||
|
|
||||||
@api \drive/folders/show do
|
this.newWindow = folderId => {
|
||||||
folder_id: target-folder
|
riot.mount(document.body.appendChild(document.createElement('mk-drive-browser-window')), {
|
||||||
.then (folder) ~>
|
folder: folderId
|
||||||
@folder = folder
|
});
|
||||||
@hierarchy-folders = []
|
};
|
||||||
|
|
||||||
x = (f) ~>
|
this.move = target => {
|
||||||
@hierarchy-folders.unshift f
|
if (target == null) {
|
||||||
if f.parent?
|
this.goRoot();
|
||||||
x f.parent
|
return;
|
||||||
|
} else if (typeof target == 'object') {
|
||||||
if folder.parent?
|
target = target.id;
|
||||||
x folder.parent
|
}
|
||||||
|
|
||||||
@update!
|
this.update({
|
||||||
@load!
|
fetching: true
|
||||||
.catch (err, text-status) ->
|
});
|
||||||
console.error err
|
|
||||||
|
this.api('drive/folders/show', {
|
||||||
@add-folder = (folder, unshift = false) ~>
|
folder_id: target
|
||||||
current = if @folder? then @folder.id else null
|
}).then(folder => {
|
||||||
if current != folder.parent_id
|
this.folder = folder;
|
||||||
return
|
this.hierarchyFolders = [];
|
||||||
|
|
||||||
if (@folders.some (f) ~> f.id == folder.id)
|
const dive = folder => {
|
||||||
exist = (@folders.map (f) -> f.id).index-of folder.id
|
this.hierarchyFolders.unshift(folder);
|
||||||
@folders[exist] = folder
|
if (folder.parent) dive(folder.parent);
|
||||||
@update!
|
};
|
||||||
return
|
|
||||||
|
if (folder.parent) dive(folder.parent);
|
||||||
if unshift
|
|
||||||
@folders.unshift folder
|
this.update();
|
||||||
else
|
this.load();
|
||||||
@folders.push folder
|
});
|
||||||
|
};
|
||||||
@update!
|
|
||||||
|
this.addFolder = (folder, unshift = false) => {
|
||||||
@add-file = (file, unshift = false) ~>
|
const current = this.folder ? this.folder.id : null;
|
||||||
current = if @folder? then @folder.id else null
|
if (current != folder.parent_id) return;
|
||||||
if current != file.folder_id
|
|
||||||
return
|
if (this.folders.some(f => f.id == folder.id)) {
|
||||||
|
const exist = this.folders.map(f => f.id).indexOf(folder.id);
|
||||||
if (@files.some (f) ~> f.id == file.id)
|
this.folders[exist] = folder;
|
||||||
exist = (@files.map (f) -> f.id).index-of file.id
|
this.update();
|
||||||
@files[exist] = file
|
return;
|
||||||
@update!
|
}
|
||||||
return
|
|
||||||
|
if (unshift) {
|
||||||
if unshift
|
this.folders.unshift(folder);
|
||||||
@files.unshift file
|
} else {
|
||||||
else
|
this.folders.push(folder);
|
||||||
@files.push file
|
}
|
||||||
|
|
||||||
@update!
|
this.update();
|
||||||
|
};
|
||||||
@remove-folder = (folder) ~>
|
|
||||||
if typeof folder == \object
|
this.addFile = (file, unshift = false) => {
|
||||||
folder = folder.id
|
const current = this.folder ? this.folder.id : null;
|
||||||
@folders = @folders.filter (f) -> f.id != folder
|
if (current != file.folder_id) return;
|
||||||
@update!
|
|
||||||
|
if (this.files.some(f => f.id == file.id)) {
|
||||||
@remove-file = (file) ~>
|
const exist = this.files.map(f => f.id).indexOf(file.id);
|
||||||
if typeof file == \object
|
this.files[exist] = file;
|
||||||
file = file.id
|
this.update();
|
||||||
@files = @files.filter (f) -> f.id != file
|
return;
|
||||||
@update!
|
}
|
||||||
|
|
||||||
@go-root = ~>
|
if (unshift) {
|
||||||
if @folder != null
|
this.files.unshift(file);
|
||||||
@folder = null
|
} else {
|
||||||
@hierarchy-folders = []
|
this.files.push(file);
|
||||||
@update!
|
}
|
||||||
@load!
|
|
||||||
|
this.update();
|
||||||
@load = ~>
|
};
|
||||||
@folders = []
|
|
||||||
@files = []
|
this.removeFolder = folder => {
|
||||||
@more-folders = false
|
if (typeof folder == 'object') folder = folder.id;
|
||||||
@more-files = false
|
this.folders = this.folders.filter(f => f.id != folder);
|
||||||
@loading = true
|
this.update();
|
||||||
@update!
|
};
|
||||||
|
|
||||||
load-folders = null
|
this.removeFile = file => {
|
||||||
load-files = null
|
if (typeof file == 'object') file = file.id;
|
||||||
|
this.files = this.files.filter(f => f.id != file);
|
||||||
folders-max = 30
|
this.update();
|
||||||
files-max = 30
|
};
|
||||||
|
|
||||||
# フォルダ一覧取得
|
this.goRoot = () => {
|
||||||
@api \drive/folders do
|
// 既にrootにいるなら何もしない
|
||||||
folder_id: if @folder? then @folder.id else null
|
if (this.folder == null) return;
|
||||||
limit: folders-max + 1
|
|
||||||
.then (folders) ~>
|
this.update({
|
||||||
if folders.length == folders-max + 1
|
folder: null,
|
||||||
@more-folders = true
|
hierarchyFolders: []
|
||||||
folders.pop!
|
});
|
||||||
load-folders := folders
|
this.load();
|
||||||
complete!
|
};
|
||||||
.catch (err, text-status) ~>
|
|
||||||
console.error err
|
this.load = () => {
|
||||||
|
this.update({
|
||||||
# ファイル一覧取得
|
folders: [],
|
||||||
@api \drive/files do
|
files: [],
|
||||||
folder_id: if @folder? then @folder.id else null
|
moreFolders: false,
|
||||||
limit: files-max + 1
|
moreFiles: false,
|
||||||
.then (files) ~>
|
fetching: true
|
||||||
if files.length == files-max + 1
|
});
|
||||||
@more-files = true
|
|
||||||
files.pop!
|
let fetchedFolders = null;
|
||||||
load-files := files
|
let fetchedFiles = null;
|
||||||
complete!
|
|
||||||
.catch (err, text-status) ~>
|
const foldersMax = 30;
|
||||||
console.error err
|
const filesMax = 30;
|
||||||
|
|
||||||
flag = false
|
// フォルダ一覧取得
|
||||||
complete = ~>
|
this.api('drive/folders', {
|
||||||
if flag
|
folder_id: this.folder ? this.folder.id : null,
|
||||||
load-folders.for-each (folder) ~>
|
limit: foldersMax + 1
|
||||||
@add-folder folder
|
}).then(folders => {
|
||||||
load-files.for-each (file) ~>
|
if (folders.length == foldersMax + 1) {
|
||||||
@add-file file
|
this.moreFolders = true;
|
||||||
@loading = false
|
folders.pop();
|
||||||
@update!
|
}
|
||||||
else
|
fetchedFolders = folders;
|
||||||
flag := true
|
complete();
|
||||||
|
});
|
||||||
function contains(parent, child)
|
|
||||||
node = child.parent-node
|
// ファイル一覧取得
|
||||||
while node?
|
this.api('drive/files', {
|
||||||
if node == parent
|
folder_id: this.folder ? this.folder.id : null,
|
||||||
return true
|
limit: filesMax + 1
|
||||||
node = node.parent-node
|
}).then(files => {
|
||||||
return false
|
if (files.length == filesMax + 1) {
|
||||||
|
this.moreFiles = true;
|
||||||
|
files.pop();
|
||||||
|
}
|
||||||
|
fetchedFiles = files;
|
||||||
|
complete();
|
||||||
|
});
|
||||||
|
|
||||||
|
let flag = false;
|
||||||
|
const complete = () => {
|
||||||
|
if (flag) {
|
||||||
|
fetchedFolders.forEach(this.addFolder);
|
||||||
|
fetchedFiles.forEach(this.addFile);
|
||||||
|
this.update({
|
||||||
|
fetching: false
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
flag = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</mk-drive-browser>
|
</mk-drive-browser>
|
||||||
|
|
|
@ -22,9 +22,6 @@
|
||||||
<li onclick={ parent.setBanner }>
|
<li onclick={ parent.setBanner }>
|
||||||
<p>バナーに設定</p>
|
<p>バナーに設定</p>
|
||||||
</li>
|
</li>
|
||||||
<li onclick={ parent.setWallpaper }>
|
|
||||||
<p>壁紙に設定</p>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li class="has-child">
|
<li class="has-child">
|
||||||
|
@ -38,60 +35,58 @@
|
||||||
</ul>
|
</ul>
|
||||||
</mk-contextmenu>
|
</mk-contextmenu>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
@mixin \i
|
this.mixin('i');
|
||||||
@mixin \update-avatar
|
this.mixin('update-avatar');
|
||||||
@mixin \update-banner
|
this.mixin('update-banner');
|
||||||
@mixin \update-wallpaper
|
this.mixin('input-dialog');
|
||||||
@mixin \input-dialog
|
this.mixin('NotImplementedException');
|
||||||
@mixin \NotImplementedException
|
|
||||||
|
|
||||||
@browser = @opts.browser
|
this.browser = this.opts.browser;
|
||||||
@file = @opts.file
|
this.file = this.opts.file;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@refs.ctx.on \closed ~>
|
this.refs.ctx.on('closed', () => {
|
||||||
@trigger \closed
|
this.trigger('closed');
|
||||||
@unmount!
|
this.unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@open = (pos) ~>
|
this.open = pos => {
|
||||||
@refs.ctx.open pos
|
this.refs.ctx.open(pos);
|
||||||
|
};
|
||||||
|
|
||||||
@rename = ~>
|
this.rename = () => {
|
||||||
@refs.ctx.close!
|
this.refs.ctx.close();
|
||||||
|
|
||||||
name <~ @input-dialog do
|
this.inputDialog('ファイル名の変更', '新しいファイル名を入力してください', this.file.name, name => {
|
||||||
'ファイル名の変更'
|
this.api('drive/files/update', {
|
||||||
'新しいファイル名を入力してください'
|
file_id: this.file.id,
|
||||||
@file.name
|
name: name
|
||||||
|
})
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@api \drive/files/update do
|
this.copyUrl = () => {
|
||||||
file_id: @file.id
|
this.NotImplementedException();
|
||||||
name: name
|
};
|
||||||
.then ~>
|
|
||||||
# something
|
|
||||||
.catch (err) ~>
|
|
||||||
console.error err
|
|
||||||
|
|
||||||
@copy-url = ~>
|
this.download = () => {
|
||||||
@NotImplementedException!
|
this.refs.ctx.close();
|
||||||
|
};
|
||||||
|
|
||||||
@download = ~>
|
this.setAvatar = () => {
|
||||||
@refs.ctx.close!
|
this.refs.ctx.close();
|
||||||
|
this.updateAvatar(this.I, null, this.file);
|
||||||
|
};
|
||||||
|
|
||||||
@set-avatar = ~>
|
this.setBanner = () => {
|
||||||
@refs.ctx.close!
|
this.refs.ctx.close();
|
||||||
@update-avatar @I, null, @file
|
this.updateBanner(this.I, null, this.file);
|
||||||
|
};
|
||||||
|
|
||||||
@set-banner = ~>
|
this.addApp = () => {
|
||||||
@refs.ctx.close!
|
this.NotImplementedException();
|
||||||
@update-banner @I, null, @file
|
};
|
||||||
|
|
||||||
@set-wallpaper = ~>
|
|
||||||
@refs.ctx.close!
|
|
||||||
@update-wallpaper @I, null, @file
|
|
||||||
|
|
||||||
@add-app = ~>
|
|
||||||
@NotImplementedException!
|
|
||||||
</script>
|
</script>
|
||||||
</mk-drive-browser-file-contextmenu>
|
</mk-drive-browser-file-contextmenu>
|
||||||
|
|
|
@ -144,66 +144,76 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@bytes-to-size = require '../../../common/scripts/bytes-to-size.js'
|
this.bytesToSize = require('../../../common/scripts/bytes-to-size');
|
||||||
|
|
||||||
@mixin \i
|
this.mixin('i');
|
||||||
|
|
||||||
@file = @opts.file
|
this.file = this.opts.file;
|
||||||
@browser = @parent
|
this.browser = this.parent;
|
||||||
|
|
||||||
@title = @file.name + '\n' + @file.type + ' ' + (@bytes-to-size @file.datasize)
|
this.title = `${this.file.name}\n${this.file.type} ${this.bytesToSize(this.file.datasize)}`;
|
||||||
|
|
||||||
@is-contextmenu-showing = false
|
this.isContextmenuShowing = false;
|
||||||
|
|
||||||
@onclick = ~>
|
this.onclick = () => {
|
||||||
if @browser.multiple
|
if (this.browser.multiple) {
|
||||||
if @file._selected?
|
if (this.file._selected != null) {
|
||||||
@file._selected = !@file._selected
|
this.file._selected = !this.file._selected;
|
||||||
else
|
} else {
|
||||||
@file._selected = true
|
this.file._selected = true;
|
||||||
@browser.trigger \change-selection @browser.get-selection!
|
}
|
||||||
else
|
this.browser.trigger('change-selection', this.browser.getSelection());
|
||||||
if @file._selected
|
} else {
|
||||||
@browser.trigger \selected @file
|
if (this.file._selected) {
|
||||||
else
|
this.browser.trigger('selected', this.file);
|
||||||
@browser.files.for-each (file) ~>
|
} else {
|
||||||
file._selected = false
|
this.browser.files.forEach(file => file._selected = false);
|
||||||
@file._selected = true
|
this.file._selected = true;
|
||||||
@browser.trigger \change-selection @file
|
this.browser.trigger('change-selection', this.file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@oncontextmenu = (e) ~>
|
this.oncontextmenu = e => {
|
||||||
e.prevent-default!
|
e.preventDefault();
|
||||||
e.stop-immediate-propagation!
|
e.stopImmediatePropagation();
|
||||||
|
|
||||||
@is-contextmenu-showing = true
|
this.update({
|
||||||
@update!
|
isContextmenuShowing: true
|
||||||
ctx = document.body.append-child document.create-element \mk-drive-browser-file-contextmenu
|
});
|
||||||
ctx = riot.mount ctx, do
|
const ctx = riot.mount(document.body.appendChild(document.createElement('mk-drive-browser-file-contextmenu')), {
|
||||||
browser: @browser
|
browser: this.browser,
|
||||||
file: @file
|
file: this.file
|
||||||
ctx = ctx.0
|
})[0];
|
||||||
ctx.open do
|
ctx.open({
|
||||||
x: e.page-x - window.page-x-offset
|
x: e.pageX - window.pageXOffset,
|
||||||
y: e.page-y - window.page-y-offset
|
y: e.pageY - window.pageYOffset
|
||||||
ctx.on \closed ~>
|
});
|
||||||
@is-contextmenu-showing = false
|
ctx.on('closed', () => {
|
||||||
@update!
|
this.update({
|
||||||
return false
|
isContextmenuShowing: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
@ondragstart = (e) ~>
|
this.ondragstart = e => {
|
||||||
e.data-transfer.effect-allowed = \move
|
e.dataTransfer.effectAllowed = 'move';
|
||||||
e.data-transfer.set-data 'text' JSON.stringify do
|
e.dataTransfer.setData('text', JSON.stringify({
|
||||||
type: \file
|
type: 'file',
|
||||||
id: @file.id
|
id: this.file.id,
|
||||||
file: @file
|
file: this.file
|
||||||
@is-dragging = true
|
}));
|
||||||
|
this.isDragging = true;
|
||||||
|
|
||||||
# 親ブラウザに対して、ドラッグが開始されたフラグを立てる
|
// 親ブラウザに対して、ドラッグが開始されたフラグを立てる
|
||||||
# (=あなたの子供が、ドラッグを開始しましたよ)
|
// (=あなたの子供が、ドラッグを開始しましたよ)
|
||||||
@browser.is-drag-source = true
|
this.browser.isDragSource = true;
|
||||||
|
};
|
||||||
|
|
||||||
@ondragend = (e) ~>
|
this.ondragend = e => {
|
||||||
@is-dragging = false
|
this.isDragging = false;
|
||||||
@browser.is-drag-source = false
|
this.browser.isDragSource = false;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-drive-browser-file>
|
</mk-drive-browser-file>
|
||||||
|
|
|
@ -18,49 +18,45 @@
|
||||||
</ul>
|
</ul>
|
||||||
</mk-contextmenu>
|
</mk-contextmenu>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
@mixin \input-dialog
|
this.mixin('input-dialog');
|
||||||
|
|
||||||
@browser = @opts.browser
|
this.browser = this.opts.browser;
|
||||||
@folder = @opts.folder
|
this.folder = this.opts.folder;
|
||||||
|
|
||||||
@open = (pos) ~>
|
this.open = pos => {
|
||||||
@refs.ctx.open pos
|
this.refs.ctx.open(pos);
|
||||||
|
|
||||||
@refs.ctx.on \closed ~>
|
this.refs.ctx.on('closed', () => {
|
||||||
@trigger \closed
|
this.trigger('closed');
|
||||||
@unmount!
|
this.unmount();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@move = ~>
|
this.move = () => {
|
||||||
@browser.move @folder.id
|
this.browser.move(this.folder.id);
|
||||||
@refs.ctx.close!
|
this.refs.ctx.close();
|
||||||
|
};
|
||||||
|
|
||||||
@new-window = ~>
|
this.newWindow = () => {
|
||||||
@browser.new-window @folder.id
|
this.browser.newWindow(this.folder.id);
|
||||||
@refs.ctx.close!
|
this.refs.ctx.close();
|
||||||
|
};
|
||||||
|
|
||||||
@create-folder = ~>
|
this.createFolder = () => {
|
||||||
@browser.create-folder!
|
this.browser.createFolder();
|
||||||
@refs.ctx.close!
|
this.refs.ctx.close();
|
||||||
|
};
|
||||||
|
|
||||||
@upload = ~>
|
this.rename = () => {
|
||||||
@browser.select-lcoal-file!
|
this.refs.ctx.close();
|
||||||
@refs.ctx.close!
|
|
||||||
|
|
||||||
@rename = ~>
|
this.inputialog('フォルダ名の変更', '新しいフォルダ名を入力してください', this.folder.name, name => {
|
||||||
@refs.ctx.close!
|
this.api('drive/folders/update', {
|
||||||
|
folder_id: this.folder.id,
|
||||||
name <~ @input-dialog do
|
name: name
|
||||||
'フォルダ名の変更'
|
});
|
||||||
'新しいフォルダ名を入力してください'
|
});
|
||||||
@folder.name
|
};
|
||||||
|
|
||||||
@api \drive/folders/update do
|
|
||||||
folder_id: @folder.id
|
|
||||||
name: name
|
|
||||||
.then ~>
|
|
||||||
# something
|
|
||||||
.catch (err) ~>
|
|
||||||
console.error err
|
|
||||||
</script>
|
</script>
|
||||||
</mk-drive-browser-folder-contextmenu>
|
</mk-drive-browser-folder-contextmenu>
|
||||||
|
|
|
@ -50,135 +50,152 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
@mixin \dialog
|
this.mixin('dialog');
|
||||||
|
|
||||||
@folder = @opts.folder
|
this.folder = this.opts.folder;
|
||||||
@browser = @parent
|
this.browser = this.parent;
|
||||||
|
|
||||||
@title = @folder.name
|
this.title = this.folder.name;
|
||||||
@hover = false
|
this.hover = false;
|
||||||
@draghover = false
|
this.draghover = false;
|
||||||
@is-contextmenu-showing = false
|
this.isContextmenuShowing = false;
|
||||||
|
|
||||||
@onclick = ~>
|
this.onclick = () => {
|
||||||
@browser.move @folder
|
this.browser.move(this.folder);
|
||||||
|
};
|
||||||
|
|
||||||
@onmouseover = ~>
|
this.onmouseover = () => {
|
||||||
@hover = true
|
this.hover = true;
|
||||||
|
};
|
||||||
|
|
||||||
@onmouseout = ~>
|
this.onmouseout = () => {
|
||||||
@hover = false
|
this.hover = false
|
||||||
|
};
|
||||||
|
|
||||||
@ondragover = (e) ~>
|
this.ondragover = e => {
|
||||||
e.prevent-default!
|
e.preventDefault();
|
||||||
e.stop-propagation!
|
e.stopPropagation();
|
||||||
|
|
||||||
# 自分自身がドラッグされていない場合
|
// 自分自身がドラッグされていない場合
|
||||||
if !@is-dragging
|
if (!this.isDragging) {
|
||||||
# ドラッグされてきたものがファイルだったら
|
// ドラッグされてきたものがファイルだったら
|
||||||
if e.data-transfer.effect-allowed == \all
|
if (e.dataTransfer.effectAllowed === 'all') {
|
||||||
e.data-transfer.drop-effect = \copy
|
e.dataTransfer.dropEffect = 'copy';
|
||||||
else
|
} else {
|
||||||
e.data-transfer.drop-effect = \move
|
e.dataTransfer.dropEffect = 'move';
|
||||||
else
|
}
|
||||||
# 自分自身にはドロップさせない
|
} else {
|
||||||
e.data-transfer.drop-effect = \none
|
// 自分自身にはドロップさせない
|
||||||
return false
|
e.dataTransfer.dropEffect = 'none';
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
@ondragenter = ~>
|
this.ondragenter = () => {
|
||||||
if !@is-dragging
|
if (!this.isDragging) this.draghover = true;
|
||||||
@draghover = true
|
};
|
||||||
|
|
||||||
@ondragleave = ~>
|
this.ondragleave = () => {
|
||||||
@draghover = false
|
this.draghover = false;
|
||||||
|
};
|
||||||
|
|
||||||
@ondrop = (e) ~>
|
this.ondrop = e => {
|
||||||
e.stop-propagation!
|
e.stopPropagation();
|
||||||
@draghover = false
|
this.draghover = false;
|
||||||
|
|
||||||
# ファイルだったら
|
// ファイルだったら
|
||||||
if e.data-transfer.files.length > 0
|
if (e.dataTransfer.files.length > 0) {
|
||||||
Array.prototype.for-each.call e.data-transfer.files, (file) ~>
|
e.dataTransfer.files.forEach(file => {
|
||||||
@browser.upload file, @folder
|
this.browser.upload(file, this.folder);
|
||||||
return false
|
});
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
# データ取得
|
// データ取得
|
||||||
data = e.data-transfer.get-data 'text'
|
const data = e.dataTransfer.getData('text');
|
||||||
if !data?
|
if (data == null) return false;
|
||||||
return false
|
|
||||||
|
|
||||||
# パース
|
// パース
|
||||||
obj = JSON.parse data
|
// TODO: Validate JSON
|
||||||
|
const obj = JSON.parse(data);
|
||||||
|
|
||||||
# (ドライブの)ファイルだったら
|
// (ドライブの)ファイルだったら
|
||||||
if obj.type == \file
|
if (obj.type == 'file') {
|
||||||
file = obj.id
|
const file = obj.id;
|
||||||
@browser.remove-file file
|
this.browser.removeFile(file);
|
||||||
@api \drive/files/update do
|
this.api('drive/files/update', {
|
||||||
file_id: file
|
file_id: file,
|
||||||
folder_id: @folder.id
|
folder_id: this.folder.id
|
||||||
.then ~>
|
});
|
||||||
# something
|
// (ドライブの)フォルダーだったら
|
||||||
.catch (err, text-status) ~>
|
} else if (obj.type == 'folder') {
|
||||||
console.error err
|
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':
|
||||||
|
this.dialog('<i class="fa fa-exclamation-triangle"></i>操作を完了できません',
|
||||||
|
'移動先のフォルダーは、移動するフォルダーのサブフォルダーです。', [{
|
||||||
|
text: 'OK'
|
||||||
|
}]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
alert('不明なエラー' + err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
# (ドライブの)フォルダーだったら
|
return false;
|
||||||
else if obj.type == \folder
|
};
|
||||||
folder = obj.id
|
|
||||||
# 移動先が自分自身ならreject
|
|
||||||
if folder == @folder.id
|
|
||||||
return false
|
|
||||||
@browser.remove-folder folder
|
|
||||||
@api \drive/folders/update do
|
|
||||||
folder_id: folder
|
|
||||||
parent_id: @folder.id
|
|
||||||
.then ~>
|
|
||||||
# something
|
|
||||||
.catch (err) ~>
|
|
||||||
if err == 'detected-circular-definition'
|
|
||||||
@dialog do
|
|
||||||
'<i class="fa fa-exclamation-triangle"></i>操作を完了できません'
|
|
||||||
'移動先のフォルダーは、移動するフォルダーのサブフォルダーです。'
|
|
||||||
[
|
|
||||||
text: \OK
|
|
||||||
]
|
|
||||||
|
|
||||||
return false
|
this.ondragstart = e => {
|
||||||
|
e.dataTransfer.effectAllowed = 'move';
|
||||||
|
e.dataTransfer.setData('text', JSON.stringify({
|
||||||
|
type: 'folder',
|
||||||
|
id: this.folder.id
|
||||||
|
}));
|
||||||
|
this.isDragging = true;
|
||||||
|
|
||||||
@ondragstart = (e) ~>
|
// 親ブラウザに対して、ドラッグが開始されたフラグを立てる
|
||||||
e.data-transfer.effect-allowed = \move
|
// (=あなたの子供が、ドラッグを開始しましたよ)
|
||||||
e.data-transfer.set-data 'text' JSON.stringify do
|
this.browser.isDragSource = true;
|
||||||
type: \folder
|
};
|
||||||
id: @folder.id
|
|
||||||
@is-dragging = true
|
|
||||||
|
|
||||||
# 親ブラウザに対して、ドラッグが開始されたフラグを立てる
|
this.ondragend = e => {
|
||||||
# (=あなたの子供が、ドラッグを開始しましたよ)
|
this.isDragging = false;
|
||||||
@browser.is-drag-source = true
|
this.browser.isDragSource = false;
|
||||||
|
};
|
||||||
|
|
||||||
@ondragend = (e) ~>
|
this.oncontextmenu = e => {
|
||||||
@is-dragging = false
|
e.preventDefault();
|
||||||
@browser.is-drag-source = false
|
e.stopImmediatePropagation();
|
||||||
|
|
||||||
@oncontextmenu = (e) ~>
|
this.update({
|
||||||
e.prevent-default!
|
isContextmenuShowing: true
|
||||||
e.stop-immediate-propagation!
|
});
|
||||||
|
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
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@is-contextmenu-showing = true
|
return false;
|
||||||
@update!
|
};
|
||||||
ctx = document.body.append-child document.create-element \mk-drive-browser-folder-contextmenu
|
|
||||||
ctx = riot.mount ctx, do
|
|
||||||
browser: @browser
|
|
||||||
folder: @folder
|
|
||||||
ctx = ctx.0
|
|
||||||
ctx.open do
|
|
||||||
x: e.page-x - window.page-x-offset
|
|
||||||
y: e.page-y - window.page-y-offset
|
|
||||||
ctx.on \closed ~>
|
|
||||||
@is-contextmenu-showing = false
|
|
||||||
@update!
|
|
||||||
|
|
||||||
return false
|
|
||||||
</script>
|
</script>
|
||||||
</mk-drive-browser-folder>
|
</mk-drive-browser-folder>
|
||||||
|
|
|
@ -6,92 +6,93 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
|
|
||||||
# Riotのバグでnullを渡しても""になる
|
// Riotのバグでnullを渡しても""になる
|
||||||
# https://github.com/riot/riot/issues/2080
|
// https://github.com/riot/riot/issues/2080
|
||||||
#@folder = @opts.folder
|
//this.folder = this.opts.folder
|
||||||
@folder = if @opts.folder? and @opts.folder != '' then @opts.folder else null
|
this.folder = this.opts.folder && this.opts.folder != '' ? this.opts.folder : null;
|
||||||
@browser = @parent
|
this.browser = this.parent;
|
||||||
|
|
||||||
@hover = false
|
this.hover = false;
|
||||||
|
|
||||||
@onclick = ~>
|
this.onclick = () => {
|
||||||
@browser.move @folder
|
this.browser.move(this.folder);
|
||||||
|
};
|
||||||
|
|
||||||
@onmouseover = ~>
|
this.onmouseover = () => {
|
||||||
@hover = true
|
this.hover = true
|
||||||
|
};
|
||||||
|
|
||||||
@onmouseout = ~>
|
this.onmouseout = () => {
|
||||||
@hover = false
|
this.hover = false
|
||||||
|
};
|
||||||
|
|
||||||
@ondragover = (e) ~>
|
this.ondragover = e => {
|
||||||
e.prevent-default!
|
e.preventDefault();
|
||||||
e.stop-propagation!
|
e.stopPropagation();
|
||||||
|
|
||||||
# このフォルダがルートかつカレントディレクトリならドロップ禁止
|
// このフォルダがルートかつカレントディレクトリならドロップ禁止
|
||||||
if @folder == null and @browser.folder == null
|
if (this.folder == null && this.browser.folder == null) {
|
||||||
e.data-transfer.drop-effect = \none
|
e.dataTransfer.dropEffect = 'none';
|
||||||
# ドラッグされてきたものがファイルだったら
|
// ドラッグされてきたものがファイルだったら
|
||||||
else if e.data-transfer.effect-allowed == \all
|
} else if (e.dataTransfer.effectAllowed == 'all') {
|
||||||
e.data-transfer.drop-effect = \copy
|
e.dataTransfer.dropEffect = 'copy';
|
||||||
else
|
} else {
|
||||||
e.data-transfer.drop-effect = \move
|
e.dataTransfer.dropEffect = 'move';
|
||||||
return false
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
@ondragenter = ~>
|
this.ondragenter = () => {
|
||||||
if @folder != null or @browser.folder != null
|
if (this.folder || this.browser.folder) this.draghover = true;
|
||||||
@draghover = true
|
};
|
||||||
|
|
||||||
@ondragleave = ~>
|
this.ondragleave = () => {
|
||||||
if @folder != null or @browser.folder != null
|
if (this.folder || this.browser.folder) this.draghover = false;
|
||||||
@draghover = false
|
};
|
||||||
|
|
||||||
@ondrop = (e) ~>
|
this.ondrop = e => {
|
||||||
e.stop-propagation!
|
e.stopPropagation();
|
||||||
@draghover = false
|
this.draghover = false;
|
||||||
|
|
||||||
# ファイルだったら
|
// ファイルだったら
|
||||||
if e.data-transfer.files.length > 0
|
if (e.dataTransfer.files.length > 0) {
|
||||||
Array.prototype.for-each.call e.data-transfer.files, (file) ~>
|
e.dataTransfer.files.forEach(file => {
|
||||||
@browser.upload file, @folder
|
this.browser.upload(file, this.folder);
|
||||||
return false
|
});
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
# データ取得
|
// データ取得
|
||||||
data = e.data-transfer.get-data 'text'
|
const data = e.dataTransfer.getData('text');
|
||||||
if !data?
|
if (data == null) return false;
|
||||||
return false
|
|
||||||
|
|
||||||
# パース
|
// パース
|
||||||
obj = JSON.parse data
|
// TODO: Validate JSON
|
||||||
|
const obj = JSON.parse(data);
|
||||||
|
|
||||||
# (ドライブの)ファイルだったら
|
// (ドライブの)ファイルだったら
|
||||||
if obj.type == \file
|
if (obj.type == 'file') {
|
||||||
file = obj.id
|
const file = obj.id;
|
||||||
@browser.remove-file file
|
this.browser.removeFile(file);
|
||||||
@api \drive/files/update do
|
this.api('drive/files/update', {
|
||||||
file_id: file
|
file_id: file,
|
||||||
folder_id: if @folder? then @folder.id else null
|
folder_id: this.folder ? this.folder.id : null
|
||||||
.then ~>
|
});
|
||||||
# something
|
// (ドライブの)フォルダーだったら
|
||||||
.catch (err, text-status) ~>
|
} else if (obj.type == 'folder') {
|
||||||
console.error err
|
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;
|
||||||
else if obj.type == \folder
|
};
|
||||||
folder = obj.id
|
|
||||||
# 移動先が自分自身ならreject
|
|
||||||
if @folder? and folder == @folder.id
|
|
||||||
return false
|
|
||||||
@browser.remove-folder folder
|
|
||||||
@api \drive/folders/update do
|
|
||||||
folder_id: folder
|
|
||||||
parent_id: if @folder? then @folder.id else null
|
|
||||||
.then ~>
|
|
||||||
# something
|
|
||||||
.catch (err, text-status) ~>
|
|
||||||
console.error err
|
|
||||||
|
|
||||||
return false
|
|
||||||
</script>
|
</script>
|
||||||
</mk-drive-browser-nav-folder>
|
</mk-drive-browser-nav-folder>
|
||||||
|
|
|
@ -33,9 +33,5 @@
|
||||||
40%
|
40%
|
||||||
transform scale(1)
|
transform scale(1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
</mk-ellipsis-icon>
|
</mk-ellipsis-icon>
|
||||||
|
|
|
@ -67,58 +67,74 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
@mixin \is-promise
|
this.mixin('is-promise');
|
||||||
@mixin \stream
|
this.mixin('stream');
|
||||||
|
|
||||||
@user = null
|
this.user = null;
|
||||||
@user-promise = if @is-promise @opts.user then @opts.user else Promise.resolve @opts.user
|
this.userPromise = this.isPromise(this.opts.user)
|
||||||
@init = true
|
? this.opts.user
|
||||||
@wait = false
|
: Promise.resolve(this.opts.user);
|
||||||
|
this.init = true;
|
||||||
|
this.wait = false;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@user-promise.then (user) ~>
|
this.userPromise.then(user => {
|
||||||
@user = user
|
this.update({
|
||||||
@init = false
|
init: false,
|
||||||
@update!
|
user: user
|
||||||
@stream.on \follow @on-stream-follow
|
});
|
||||||
@stream.on \unfollow @on-stream-unfollow
|
this.stream.on('follow', this.onStreamFollow);
|
||||||
|
this.stream.on('unfollow', this.onStreamUnfollow);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@on \unmount ~>
|
this.on('unmount', () => {
|
||||||
@stream.off \follow @on-stream-follow
|
this.stream.off('follow', this.onStreamFollow);
|
||||||
@stream.off \unfollow @on-stream-unfollow
|
this.stream.off('unfollow', this.onStreamUnfollow);
|
||||||
|
});
|
||||||
|
|
||||||
@on-stream-follow = (user) ~>
|
this.onStreamFollow = user => {
|
||||||
if user.id == @user.id
|
if (user.id == this.user.id) {
|
||||||
@user = user
|
this.update({
|
||||||
@update!
|
user: user
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@on-stream-unfollow = (user) ~>
|
this.onStreamUnfollow = user => {
|
||||||
if user.id == @user.id
|
if (user.id == this.user.id) {
|
||||||
@user = user
|
this.update({
|
||||||
@update!
|
user: user
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@onclick = ~>
|
this.onclick = () => {
|
||||||
@wait = true
|
this.wait = true;
|
||||||
if @user.is_following
|
if (this.user.is_following) {
|
||||||
@api \following/delete do
|
this.api('following/delete', {
|
||||||
user_id: @user.id
|
user_id: this.user.id
|
||||||
.then ~>
|
}).then(() => {
|
||||||
@user.is_following = false
|
this.user.is_following = false;
|
||||||
.catch (err) ->
|
}).catch(err => {
|
||||||
console.error err
|
console.error(err);
|
||||||
.then ~>
|
}).then(() => {
|
||||||
@wait = false
|
this.wait = false;
|
||||||
@update!
|
this.update();
|
||||||
else
|
});
|
||||||
@api \following/create do
|
} else {
|
||||||
user_id: @user.id
|
this.api('following/create', {
|
||||||
.then ~>
|
user_id: this.user.id
|
||||||
@user.is_following = true
|
}).then(() => {
|
||||||
.catch (err) ->
|
this.user.is_following = true;
|
||||||
console.error err
|
}).catch(err => {
|
||||||
.then ~>
|
console.error(err);
|
||||||
@wait = false
|
}).then(() => {
|
||||||
@update!
|
this.wait = false;
|
||||||
|
this.update();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-follow-button>
|
</mk-follow-button>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<mk-following-setuper>
|
<mk-following-setuper>
|
||||||
<p class="title">気になるユーザーをフォロー:</p>
|
<p class="title">気になるユーザーをフォロー:</p>
|
||||||
<div class="users" if={ !loading && users.length > 0 }>
|
<div class="users" if={ !fetching && users.length > 0 }>
|
||||||
<div class="user" each={ users }><a class="avatar-anchor" href={ CONFIG.url + '/' + username }><img class="avatar" src={ avatar_url + '?thumbnail&size=42' } alt="" data-user-preview={ id }/></a>
|
<div class="user" each={ users }><a class="avatar-anchor" href={ CONFIG.url + '/' + username }><img class="avatar" src={ avatar_url + '?thumbnail&size=42' } alt="" data-user-preview={ id }/></a>
|
||||||
<div class="body"><a class="name" href={ CONFIG.url + '/' + username } target="_blank" data-user-preview={ id }>{ name }</a>
|
<div class="body"><a class="name" href={ CONFIG.url + '/' + username } target="_blank" data-user-preview={ id }>{ name }</a>
|
||||||
<p class="username">@{ username }</p>
|
<p class="username">@{ username }</p>
|
||||||
|
@ -8,8 +8,8 @@
|
||||||
<mk-follow-button user={ this }></mk-follow-button>
|
<mk-follow-button user={ this }></mk-follow-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="empty" if={ !loading && users.length == 0 }>おすすめのユーザーは見つかりませんでした。</p>
|
<p class="empty" if={ !fetching && users.length == 0 }>おすすめのユーザーは見つかりませんでした。</p>
|
||||||
<p class="loading" if={ loading }><i class="fa fa-spinner fa-pulse fa-fw"></i>読み込んでいます
|
<p class="fetching" if={ fetching }><i class="fa fa-spinner fa-pulse fa-fw"></i>読み込んでいます
|
||||||
<mk-ellipsis></mk-ellipsis>
|
<mk-ellipsis></mk-ellipsis>
|
||||||
</p><a class="refresh" onclick={ refresh }>もっと見る</a>
|
</p><a class="refresh" onclick={ refresh }>もっと見る</a>
|
||||||
<button class="close" onclick={ close } title="閉じる"><i class="fa fa-times"></i></button>
|
<button class="close" onclick={ close } title="閉じる"><i class="fa fa-times"></i></button>
|
||||||
|
@ -81,7 +81,7 @@
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color #aaa
|
||||||
|
|
||||||
> .loading
|
> .fetching
|
||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
|
@ -123,41 +123,49 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
@mixin \user-preview
|
this.mixin('user-preview');
|
||||||
|
|
||||||
@users = null
|
this.users = null;
|
||||||
@loading = true
|
this.fetching = true;
|
||||||
|
|
||||||
@limit = 6users
|
this.limit = 6;
|
||||||
@page = 0
|
this.page = 0;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@load!
|
this.fetch();
|
||||||
|
});
|
||||||
|
|
||||||
@load = ~>
|
this.fetch = () => {
|
||||||
@loading = true
|
this.update({
|
||||||
@users = null
|
fetching: true,
|
||||||
@update!
|
users: null
|
||||||
|
});
|
||||||
|
|
||||||
@api \users/recommendation do
|
this.api('users/recommendation', {
|
||||||
limit: @limit
|
limit: this.limit,
|
||||||
offset: @limit * @page
|
offset: this.limit * this.page
|
||||||
.then (users) ~>
|
}).then(users => {
|
||||||
@loading = false
|
this.fetching = false
|
||||||
@users = users
|
this.users = users
|
||||||
@update!
|
this.update({
|
||||||
.catch (err, text-status) ->
|
fetching: false,
|
||||||
console.error err
|
users: users
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@refresh = ~>
|
this.refresh = () => {
|
||||||
if @users.length < @limit
|
if (this.users.length < this.limit) {
|
||||||
@page = 0
|
this.page = 0;
|
||||||
else
|
} else {
|
||||||
@page++
|
this.page++;
|
||||||
@load!
|
}
|
||||||
|
this.fetch();
|
||||||
|
};
|
||||||
|
|
||||||
@close = ~>
|
this.close = () => {
|
||||||
@unmount!
|
this.unmount();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-following-setuper>
|
</mk-following-setuper>
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
<mk-go-top>
|
|
||||||
<button class="hidden" title="一番上へ"><i class="fa fa-angle-up"></i></button>
|
|
||||||
<script>
|
|
||||||
window.add-event-listener \load @on-scroll
|
|
||||||
window.add-event-listener \scroll @on-scroll
|
|
||||||
window.add-event-listener \resize @on-scroll
|
|
||||||
|
|
||||||
@on-scroll = ~>
|
|
||||||
if $ window .scroll-top! > 500px
|
|
||||||
@remove-class \hidden
|
|
||||||
else
|
|
||||||
@add-class \hidden
|
|
||||||
</script>
|
|
||||||
</mk-go-top>
|
|
|
@ -106,43 +106,45 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@draw = ~>
|
this.draw = () => {
|
||||||
now = new Date!
|
const now = new Date();
|
||||||
nd = now.get-date!
|
const nd = now.getDate();
|
||||||
nm = now.get-month!
|
const nm = now.getMonth();
|
||||||
ny = now.get-full-year!
|
const ny = now.getFullYear();
|
||||||
|
|
||||||
@year = ny
|
this.year = ny;
|
||||||
@month = nm + 1
|
this.month = nm + 1;
|
||||||
@day = nd
|
this.day = nd;
|
||||||
@week-day = [\日 \月 \火 \水 \木 \金 \土][now.get-day!]
|
this.weekDay = ['日', '月', '火', '水', '木', '金', '土'][now.getDay()];
|
||||||
|
|
||||||
@day-numer = (now - (new Date ny, nm, nd))
|
this.dayNumer = now - new Date(ny, nm, nd);
|
||||||
@day-denom = 1000ms * 60s * 60m * 24h
|
this.dayDenom = 1000/*ms*/ * 60/*s*/ * 60/*m*/ * 24/*h*/;
|
||||||
@month-numer = (now - (new Date ny, nm, 1))
|
this.monthNumer = now - new Date(ny, nm, 1);
|
||||||
@month-denom = (new Date ny, nm + 1, 1) - (new Date ny, nm, 1)
|
this.monthDenom = new Date(ny, nm + 1, 1) - new Date(ny, nm, 1);
|
||||||
@year-numer = (now - (new Date ny, 0, 1))
|
this.yearNumer = now - new Date(ny, 0, 1);
|
||||||
@year-denom = (new Date ny + 1, 0, 1) - (new Date ny, 0, 1)
|
this.yearDenom = new Date(ny + 1, 0, 1) - new Date(ny, 0, 1);
|
||||||
|
|
||||||
@day-p = @day-numer / @day-denom * 100
|
this.dayP = this.dayNumer / this.dayDenom * 100;
|
||||||
@month-p = @month-numer / @month-denom * 100
|
this.monthP = this.monthNumer / this.monthDenom * 100;
|
||||||
@year-p = @year-numer / @year-denom * 100
|
this.yearP = this.yearNumer / this.yearDenom * 100;
|
||||||
|
|
||||||
@is-holiday =
|
this.isHoliday = now.getDay() == 0 || now.getDay() == 6;
|
||||||
(now.get-day! == 0 or now.get-day! == 6)
|
|
||||||
|
|
||||||
@special =
|
this.special =
|
||||||
| nm == 0 and nd == 1 => \on-new-years-day
|
nm == 0 && nd == 1 ? 'on-new-years-day' :
|
||||||
| _ => false
|
false;
|
||||||
|
|
||||||
@update!
|
this.update();
|
||||||
|
};
|
||||||
|
|
||||||
@draw!
|
this.draw();
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@clock = set-interval @draw, 1000ms
|
this.clock = setInterval(this.draw, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
@on \unmount ~>
|
this.on('unmount', () => {
|
||||||
clear-interval @clock
|
clearInterval(this.clock);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</mk-calendar-home-widget>
|
</mk-calendar-home-widget>
|
||||||
|
|
|
@ -32,5 +32,5 @@
|
||||||
color #999
|
color #999
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>@mixin \user-preview</script>
|
<script>this.mixin('user-preview');</script>
|
||||||
</mk-donation-home-widget>
|
</mk-donation-home-widget>
|
||||||
|
|
|
@ -46,67 +46,73 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \i
|
this.mixin('i');
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
|
|
||||||
@is-loading = true
|
this.isLoading = true;
|
||||||
@is-empty = false
|
this.isEmpty = false;
|
||||||
@more-loading = false
|
this.moreLoading = false;
|
||||||
@mode = \all
|
this.mode = 'all';
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
document.add-event-listener \keydown @on-document-keydown
|
document.addEventListener('keydown', this.onDocumentKeydown);
|
||||||
window.add-event-listener \scroll @on-scroll
|
window.addEventListener('scroll', this.onScroll);
|
||||||
|
|
||||||
@fetch ~>
|
this.fetch(() => this.trigger('loaded'));
|
||||||
@trigger \loaded
|
});
|
||||||
|
|
||||||
@on \unmount ~>
|
this.on('unmount', () => {
|
||||||
document.remove-event-listener \keydown @on-document-keydown
|
document.removeEventListener('keydown', this.onDocumentKeydown);
|
||||||
window.remove-event-listener \scroll @on-scroll
|
window.removeEventListener('scroll', this.onScroll);
|
||||||
|
});
|
||||||
|
|
||||||
@on-document-keydown = (e) ~>
|
this.onDocumentKeydown = e => {
|
||||||
tag = e.target.tag-name.to-lower-case!
|
if (e.target.tagName != 'INPUT' && tag != 'TEXTAREA') {
|
||||||
if tag != \input and tag != \textarea
|
if (e.which == 84) { // t
|
||||||
if e.which == 84 # t
|
this.refs.timeline.focus();
|
||||||
@refs.timeline.focus!
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@fetch = (cb) ~>
|
this.fetch = cb => {
|
||||||
@api \posts/mentions do
|
this.api('posts/mentions', {
|
||||||
following: @mode == \following
|
following: this.mode == 'following'
|
||||||
.then (posts) ~>
|
}).then(posts => {
|
||||||
@is-loading = false
|
this.update({
|
||||||
@is-empty = posts.length == 0
|
isLoading: false,
|
||||||
@update!
|
isEmpty: posts.length == 0
|
||||||
@refs.timeline.set-posts posts
|
});
|
||||||
if cb? then cb!
|
this.refs.timeline.setPosts(posts);
|
||||||
.catch (err) ~>
|
if (cb) cb();
|
||||||
console.error err
|
});
|
||||||
if cb? then cb!
|
};
|
||||||
|
|
||||||
@more = ~>
|
this.more = () => {
|
||||||
if @more-loading or @is-loading or @refs.timeline.posts.length == 0
|
if (this.moreLoading || this.isLoading || this.refs.timeline.posts.length == 0) return;
|
||||||
return
|
this.update({
|
||||||
@more-loading = true
|
moreLoading: true
|
||||||
@update!
|
});
|
||||||
@api \posts/mentions do
|
this.api('posts/mentions', {
|
||||||
following: @mode == \following
|
following: this.mode == 'following',
|
||||||
max_id: @refs.timeline.tail!.id
|
max_id: this.refs.timeline.tail().id
|
||||||
.then (posts) ~>
|
}).then(posts => {
|
||||||
@more-loading = false
|
this.update({
|
||||||
@update!
|
moreLoading: false
|
||||||
@refs.timeline.prepend-posts posts
|
});
|
||||||
.catch (err) ~>
|
this.refs.timeline.prependPosts(posts);
|
||||||
console.error err
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@on-scroll = ~>
|
this.onScroll = () => {
|
||||||
current = window.scroll-y + window.inner-height
|
const current = window.scrollY + window.innerHeight;
|
||||||
if current > document.body.offset-height - 8
|
if (current > document.body.offsetHeight - 8) this.more();
|
||||||
@more!
|
};
|
||||||
|
|
||||||
@set-mode = (mode) ~>
|
this.setMode = mode => {
|
||||||
@update do
|
this.update({
|
||||||
mode: mode
|
mode: mode
|
||||||
@fetch!
|
});
|
||||||
|
this.fetch();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-mentions-home-widget>
|
</mk-mentions-home-widget>
|
||||||
|
|
|
@ -13,9 +13,5 @@
|
||||||
i
|
i
|
||||||
color #ccc
|
color #ccc
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
</mk-nav-home-widget>
|
</mk-nav-home-widget>
|
||||||
|
|
|
@ -43,8 +43,9 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@settings = ~>
|
this.settings = () => {
|
||||||
w = riot.mount document.body.append-child document.create-element \mk-settings-window .0
|
const w = riot.mount(document.body.appendChild(document.createElement('mk-settings-window')))[0];
|
||||||
w.switch \notification
|
w.switch('notification');
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-notifications-home-widget>
|
</mk-notifications-home-widget>
|
||||||
|
|
|
@ -57,31 +57,36 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
@mixin \stream
|
this.mixin('stream');
|
||||||
|
|
||||||
@images = []
|
this.images = [];
|
||||||
@initializing = true
|
this.initializing = true;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@stream.on \drive_file_created @on-stream-drive-file-created
|
this.stream.on('drive_file_created', this.onStreamDriveFileCreated);
|
||||||
|
|
||||||
@api \drive/stream do
|
this.api('drive/stream', {
|
||||||
type: 'image/*'
|
type: 'image/*',
|
||||||
limit: 9images
|
limit: 9
|
||||||
.then (images) ~>
|
}).then(images => {
|
||||||
@initializing = false
|
this.update({
|
||||||
@images = images
|
initializing: false,
|
||||||
@update!
|
images: images
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@on \unmount ~>
|
this.on('unmount', () => {
|
||||||
@stream.off \drive_file_created @on-stream-drive-file-created
|
this.stream.off('drive_file_created', this.onStreamDriveFileCreated);
|
||||||
|
});
|
||||||
|
|
||||||
@on-stream-drive-file-created = (file) ~>
|
this.onStreamDriveFileCreated = file => {
|
||||||
if /^image\/.+$/.test file.type
|
if (/^image\/.+$/.test(file.type)) {
|
||||||
@images.unshift file
|
this.images.unshift(file);
|
||||||
if @images.length > 9
|
if (this.images.length > 9) this.images.pop();
|
||||||
@images.pop!
|
this.update();
|
||||||
@update!
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-photo-stream-home-widget>
|
</mk-photo-stream-home-widget>
|
||||||
|
|
|
@ -41,15 +41,17 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \i
|
this.mixin('i');
|
||||||
@mixin \user-preview
|
this.mixin('user-preview');
|
||||||
@mixin \update-avatar
|
this.mixin('update-avatar');
|
||||||
@mixin \update-banner
|
this.mixin('update-banner');
|
||||||
|
|
||||||
@set-avatar = ~>
|
this.setAvatar = () => {
|
||||||
@update-avatar @I
|
this.updateAvatar(this.I);
|
||||||
|
};
|
||||||
|
|
||||||
@set-banner = ~>
|
this.setBanner = () => {
|
||||||
@update-banner @I
|
this.updateBanner(this.I);
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-profile-home-widget>
|
</mk-profile-home-widget>
|
||||||
|
|
|
@ -64,31 +64,35 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
@mixin \NotImplementedException
|
this.mixin('NotImplementedException');
|
||||||
|
|
||||||
@url = 'http://news.yahoo.co.jp/pickup/rss.xml'
|
this.url = 'http://news.yahoo.co.jp/pickup/rss.xml';
|
||||||
@items = []
|
this.items = [];
|
||||||
@initializing = true
|
this.initializing = true;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@fetch!
|
this.fetch();
|
||||||
@clock = set-interval @fetch, 60000ms
|
this.clock = setInterval(this.fetch, 60000);
|
||||||
|
});
|
||||||
|
|
||||||
@on \unmount ~>
|
this.on('unmount', () => {
|
||||||
clear-interval @clock
|
clearInterval(this.clock);
|
||||||
|
});
|
||||||
|
|
||||||
@fetch = ~>
|
this.fetch = () => {
|
||||||
@api CONFIG.url + '/api:rss' do
|
this.api(CONFIG.url + '/api:rss', {
|
||||||
url: @url
|
url: this.url
|
||||||
.then (feed) ~>
|
}).then(feed => {
|
||||||
@items = feed.rss.channel.item
|
this.update({
|
||||||
@initializing = false
|
initializing: false,
|
||||||
@update!
|
items: feed.rss.channel.item
|
||||||
.catch (err) ->
|
});
|
||||||
console.error err
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@settings = ~>
|
this.settings = () => {
|
||||||
@NotImplementedException!
|
this.NotImplementedException();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-rss-reader-home-widget>
|
</mk-rss-reader-home-widget>
|
||||||
|
|
|
@ -32,80 +32,87 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \i
|
this.mixin('i');
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
@mixin \stream
|
this.mixin('stream');
|
||||||
|
|
||||||
@is-loading = true
|
this.isLoading = true;
|
||||||
@is-empty = false
|
this.isEmpty = false;
|
||||||
@more-loading = false
|
this.moreLoading = false;
|
||||||
@no-following = @I.following_count == 0
|
this.noFollowing = this.I.following_count == 0;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@stream.on \post @on-stream-post
|
this.stream.on('post', this.onStreamPost);
|
||||||
@stream.on \follow @on-stream-follow
|
this.stream.on('follow', this.onStreamFollow);
|
||||||
@stream.on \unfollow @on-stream-unfollow
|
this.stream.on('unfollow', this.onStreamUnfollow);
|
||||||
|
|
||||||
document.add-event-listener \keydown @on-document-keydown
|
document.addEventListener('keydown', this.onDocumentKeydown);
|
||||||
window.add-event-listener \scroll @on-scroll
|
window.addEventListener('scroll', this.onScroll);
|
||||||
|
|
||||||
@load ~>
|
this.load(() => this.trigger('loaded'));
|
||||||
@trigger \loaded
|
});
|
||||||
|
|
||||||
@on \unmount ~>
|
this.on('unmount', () => {
|
||||||
@stream.off \post @on-stream-post
|
this.stream.off('post', this.onStreamPost);
|
||||||
@stream.off \follow @on-stream-follow
|
this.stream.off('follow', this.onStreamFollow);
|
||||||
@stream.off \unfollow @on-stream-unfollow
|
this.stream.off('unfollow', this.onStreamUnfollow);
|
||||||
|
|
||||||
document.remove-event-listener \keydown @on-document-keydown
|
document.removeEventListener('keydown', this.onDocumentKeydown);
|
||||||
window.remove-event-listener \scroll @on-scroll
|
window.removeEventListener('scroll', this.onScroll);
|
||||||
|
});
|
||||||
|
|
||||||
@on-document-keydown = (e) ~>
|
this.onDocumentKeydown = e => {
|
||||||
tag = e.target.tag-name.to-lower-case!
|
if (e.target.tagName != 'INPUT' && e.target.tagName != 'TEXTAREA') {
|
||||||
if tag != \input and tag != \textarea
|
if (e.which == 84) { // t
|
||||||
if e.which == 84 # t
|
this.refs.timeline.focus();
|
||||||
@refs.timeline.focus!
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@load = (cb) ~>
|
this.load = (cb) => {
|
||||||
@api \posts/timeline
|
this.api('posts/timeline').then(posts => {
|
||||||
.then (posts) ~>
|
this.update({
|
||||||
@is-loading = false
|
isLoading: false,
|
||||||
@is-empty = posts.length == 0
|
isEmpty: posts.length == 0
|
||||||
@update!
|
});
|
||||||
@refs.timeline.set-posts posts
|
this.refs.timeline.setPosts(posts);
|
||||||
if cb? then cb!
|
if (cb) cb();
|
||||||
.catch (err) ~>
|
});
|
||||||
console.error err
|
};
|
||||||
if cb? then cb!
|
|
||||||
|
|
||||||
@more = ~>
|
this.more = () => {
|
||||||
if @more-loading or @is-loading or @refs.timeline.posts.length == 0
|
if (this.moreLoading || this.isLoading || this.refs.timeline.posts.length == 0) return;
|
||||||
return
|
this.update({
|
||||||
@more-loading = true
|
moreLoading: true
|
||||||
@update!
|
});
|
||||||
@api \posts/timeline do
|
this.api('posts/timeline', {
|
||||||
max_id: @refs.timeline.tail!.id
|
max_id: this.refs.timeline.tail().id
|
||||||
.then (posts) ~>
|
}).then(posts => {
|
||||||
@more-loading = false
|
this.update({
|
||||||
@update!
|
moreLoading: false
|
||||||
@refs.timeline.prepend-posts posts
|
});
|
||||||
.catch (err) ~>
|
this.refs.timeline.prependPosts(posts);
|
||||||
console.error err
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@on-stream-post = (post) ~>
|
this.onStreamPost = post => {
|
||||||
@is-empty = false
|
this.update({
|
||||||
@update!
|
isEmpty: false
|
||||||
@refs.timeline.add-post post
|
});
|
||||||
|
this.refs.timeline.addPost(post);
|
||||||
|
};
|
||||||
|
|
||||||
@on-stream-follow = ~>
|
this.onStreamFollow = () => {
|
||||||
@load!
|
this.load();
|
||||||
|
};
|
||||||
|
|
||||||
@on-stream-unfollow = ~>
|
this.onStreamUnfollow = () => {
|
||||||
@load!
|
this.load();
|
||||||
|
};
|
||||||
|
|
||||||
@on-scroll = ~>
|
this.onScroll = () => {
|
||||||
current = window.scroll-y + window.inner-height
|
const current = window.scrollY + window.innerHeight;
|
||||||
if current > document.body.offset-height - 8
|
if (current > document.body.offsetHeight - 8) this.more();
|
||||||
@more!
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-timeline-home-widget>
|
</mk-timeline-home-widget>
|
||||||
|
|
|
@ -29,43 +29,46 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@tips = [
|
this.tips = [
|
||||||
'<kbd>t</kbd>でタイムラインにフォーカスできます'
|
'<kbd>t</kbd>でタイムラインにフォーカスできます',
|
||||||
'<kbd>p</kbd>または<kbd>n</kbd>で投稿フォームを開きます'
|
'<kbd>p</kbd>または<kbd>n</kbd>で投稿フォームを開きます',
|
||||||
'投稿フォームにはファイルをドラッグ&ドロップできます'
|
'投稿フォームにはファイルをドラッグ&ドロップできます',
|
||||||
'投稿フォームにクリップボードにある画像データをペーストできます'
|
'投稿フォームにクリップボードにある画像データをペーストできます',
|
||||||
'ドライブにファイルをドラッグ&ドロップしてアップロードできます'
|
'ドライブにファイルをドラッグ&ドロップしてアップロードできます',
|
||||||
'ドライブでファイルをドラッグしてフォルダ移動できます'
|
'ドライブでファイルをドラッグしてフォルダ移動できます',
|
||||||
'ドライブでフォルダをドラッグしてフォルダ移動できます'
|
'ドライブでフォルダをドラッグしてフォルダ移動できます',
|
||||||
'ホームをカスタマイズできます(準備中)'
|
'ホームをカスタマイズできます(準備中)',
|
||||||
'MisskeyはMIT Licenseです'
|
'MisskeyはMIT Licenseです'
|
||||||
]
|
]
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@set!
|
this.set();
|
||||||
@clock = set-interval @change, 20000ms
|
this.clock = setInterval(this.change, 20000);
|
||||||
|
});
|
||||||
|
|
||||||
@on \unmount ~>
|
this.on('unmount', () => {
|
||||||
clear-interval @clock
|
clearInterval(this.clock);
|
||||||
|
});
|
||||||
|
|
||||||
@set = ~>
|
this.set = () => {
|
||||||
@refs.text.innerHTML = @tips[Math.floor Math.random! * @tips.length]
|
this.refs.text.innerHTML = this.tips[Math.floor(Math.random() * this.tips.length)];
|
||||||
@update!
|
};
|
||||||
|
|
||||||
@change = ~>
|
this.change = () => {
|
||||||
Velocity @refs.tip, {
|
Velocity(this.refs.tip, {
|
||||||
opacity: 0
|
opacity: 0
|
||||||
} {
|
}, {
|
||||||
duration: 500ms
|
duration: 500,
|
||||||
easing: \linear
|
easing: 'linear',
|
||||||
complete: @set
|
complete: this.set
|
||||||
}
|
});
|
||||||
|
|
||||||
Velocity @refs.tip, {
|
Velocity(this.refs.tip, {
|
||||||
opacity: 1
|
opacity: 1
|
||||||
} {
|
}, {
|
||||||
duration: 500ms
|
duration: 500,
|
||||||
easing: \linear
|
easing: 'linear'
|
||||||
}
|
});
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-tips-home-widget>
|
</mk-tips-home-widget>
|
||||||
|
|
|
@ -109,44 +109,42 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
@mixin \user-preview
|
this.mixin('user-preview');
|
||||||
|
|
||||||
@users = null
|
this.users = null;
|
||||||
@loading = true
|
this.loading = true;
|
||||||
|
|
||||||
@limit = 3users
|
this.limit = 3;
|
||||||
@page = 0
|
this.page = 0;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@fetch!
|
this.fetch();
|
||||||
@clock = set-interval ~>
|
});
|
||||||
if @users.length < @limit
|
|
||||||
@fetch true
|
|
||||||
, 60000ms
|
|
||||||
|
|
||||||
@on \unmount ~>
|
this.fetch = () => {
|
||||||
clear-interval @clock
|
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
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@fetch = (quiet = false) ~>
|
this.refresh = () => {
|
||||||
@loading = true
|
if (this.users.length < this.limit) {
|
||||||
@users = null
|
this.page = 0;
|
||||||
if not quiet then @update!
|
} else {
|
||||||
@api \users/recommendation do
|
this.page++;
|
||||||
limit: @limit
|
}
|
||||||
offset: @limit * @page
|
this.fetch();
|
||||||
.then (users) ~>
|
};
|
||||||
@loading = false
|
|
||||||
@users = users
|
|
||||||
@update!
|
|
||||||
.catch (err, text-status) ->
|
|
||||||
console.error err
|
|
||||||
|
|
||||||
@refresh = ~>
|
|
||||||
if @users.length < @limit
|
|
||||||
@page = 0
|
|
||||||
else
|
|
||||||
@page++
|
|
||||||
@fetch!
|
|
||||||
</script>
|
</script>
|
||||||
</mk-user-recommendation-home-widget>
|
</mk-user-recommendation-home-widget>
|
||||||
|
|
|
@ -58,33 +58,40 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \i
|
this.mixin('i');
|
||||||
@mode = @opts.mode || \timeline
|
|
||||||
|
|
||||||
# https://github.com/riot/riot/issues/2080
|
this.mode = this.opts.mode || 'timeline';
|
||||||
if @mode == '' then @mode = \timeline
|
// https://github.com/riot/riot/issues/2080
|
||||||
|
if (this.mode == '') this.mode = 'timeline';
|
||||||
|
|
||||||
@home = []
|
this.home = [];
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@refs.tl.on \loaded ~>
|
this.refs.tl.on('loaded', () => {
|
||||||
@trigger \loaded
|
this.trigger('loaded');
|
||||||
|
});
|
||||||
|
|
||||||
@I.data.home.for-each (widget) ~>
|
this.I.data.home.forEach(widget => {
|
||||||
try
|
try {
|
||||||
el = document.create-element \mk- + widget.name + \-home-widget
|
const el = document.createElement(`mk-${widget.name}-home-widget`);
|
||||||
switch widget.place
|
switch (widget.place) {
|
||||||
| \left => @refs.left.append-child el
|
case 'left': this.refs.left.appendChild(el); break;
|
||||||
| \right => @refs.right.append-child el
|
case 'right': this.refs.right.appendChild(el); break;
|
||||||
@home.push (riot.mount el, do
|
}
|
||||||
id: widget.id
|
this.home.push(riot.mount(el, {
|
||||||
|
id: widget.id,
|
||||||
data: widget.data
|
data: widget.data
|
||||||
.0)
|
})[0]);
|
||||||
catch e
|
} catch (e) {
|
||||||
# noop
|
// noop
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@on \unmount ~>
|
this.on('unmount', () => {
|
||||||
@home.for-each (widget) ~>
|
this.home.forEach(widget => {
|
||||||
widget.unmount!
|
widget.unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</mk-home>
|
</mk-home>
|
||||||
|
|
|
@ -35,41 +35,26 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@image = @opts.image
|
this.image = this.opts.image;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
Velocity @root, {
|
Velocity(this.root, {
|
||||||
opacity: 1
|
opacity: 1
|
||||||
} {
|
}, {
|
||||||
duration: 100ms
|
duration: 100,
|
||||||
easing: \linear
|
easing: 'linear'
|
||||||
}
|
});
|
||||||
|
});
|
||||||
|
|
||||||
#Velocity @img, {
|
this.close = () => {
|
||||||
# scale: 1
|
Velocity(this.root, {
|
||||||
# opacity: 1
|
|
||||||
#} {
|
|
||||||
# duration: 200ms
|
|
||||||
# easing: \ease-out
|
|
||||||
#}
|
|
||||||
|
|
||||||
@close = ~>
|
|
||||||
Velocity @root, {
|
|
||||||
opacity: 0
|
opacity: 0
|
||||||
} {
|
}, {
|
||||||
duration: 100ms
|
duration: 100,
|
||||||
easing: \linear
|
easing: 'linear',
|
||||||
complete: ~> @unmount!
|
complete: () => this.unmount()
|
||||||
}
|
});
|
||||||
|
};
|
||||||
|
|
||||||
#Velocity @img, {
|
|
||||||
# scale: 0.9
|
|
||||||
# opacity: 0
|
|
||||||
#} {
|
|
||||||
# duration: 200ms
|
|
||||||
# easing: \ease-in
|
|
||||||
# complete: ~>
|
|
||||||
# @unmount!
|
|
||||||
#}
|
|
||||||
</script>
|
</script>
|
||||||
</mk-image-dialog>
|
</mk-image-dialog>
|
||||||
|
|
|
@ -26,20 +26,22 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@images = @opts.images
|
this.images = this.opts.images;
|
||||||
@image = @images.0
|
this.image = this.images[0];
|
||||||
|
|
||||||
@mousemove = (e) ~>
|
this.mousemove = e => {
|
||||||
rect = @refs.view.get-bounding-client-rect!
|
const rect = this.refs.view.getBoundingClientRect();
|
||||||
mouse-x = e.client-x - rect.left
|
const mouseX = e.clientX - rect.left;
|
||||||
mouse-y = e.client-y - rect.top
|
const mouseY = e.clientY - rect.top;
|
||||||
xp = mouse-x / @refs.view.offset-width * 100
|
const xp = mouseX / this.refs.view.offsetWidth * 100;
|
||||||
yp = mouse-y / @refs.view.offset-height * 100
|
const yp = mouseY / this.refs.view.offsetHeight * 100;
|
||||||
@refs.view.style.background-position = xp + '% ' + yp + '%'
|
this.refs.view.style.backgroundPosition = xp + '% ' + yp + '%';
|
||||||
|
};
|
||||||
|
|
||||||
@click = ~>
|
this.click = () => {
|
||||||
dialog = document.body.append-child document.create-element \mk-image-dialog
|
riot.mount(document.body.appendChild(document.createElement('mk-image-dialog')), {
|
||||||
riot.mount dialog, do
|
image: this.image
|
||||||
image: @image
|
});
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-images-viewer>
|
</mk-images-viewer>
|
||||||
|
|
|
@ -16,7 +16,6 @@ require('./crop-window.tag');
|
||||||
require('./settings.tag');
|
require('./settings.tag');
|
||||||
require('./settings-window.tag');
|
require('./settings-window.tag');
|
||||||
require('./analog-clock.tag');
|
require('./analog-clock.tag');
|
||||||
require('./go-top.tag');
|
|
||||||
require('./ui-header.tag');
|
require('./ui-header.tag');
|
||||||
require('./ui-header-account.tag');
|
require('./ui-header-account.tag');
|
||||||
require('./ui-header-notifications.tag');
|
require('./ui-header-notifications.tag');
|
||||||
|
@ -42,7 +41,6 @@ require('./home-widgets/notifications.tag');
|
||||||
require('./home-widgets/rss-reader.tag');
|
require('./home-widgets/rss-reader.tag');
|
||||||
require('./home-widgets/photo-stream.tag');
|
require('./home-widgets/photo-stream.tag');
|
||||||
require('./home-widgets/broadcast.tag');
|
require('./home-widgets/broadcast.tag');
|
||||||
require('./stream-indicator.tag');
|
|
||||||
require('./timeline.tag');
|
require('./timeline.tag');
|
||||||
require('./messaging/window.tag');
|
require('./messaging/window.tag');
|
||||||
require('./messaging/room-window.tag');
|
require('./messaging/room-window.tag');
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
<mk-input-dialog>
|
<mk-input-dialog>
|
||||||
<mk-window ref="window" is-modal={ true } width={ '500px' }><yield to="header"><i class="fa fa-i-cursor"></i>{ parent.title }</yield>
|
<mk-window ref="window" is-modal={ true } width={ '500px' }>
|
||||||
<yield to="content">
|
<yield to="header">
|
||||||
<div class="body">
|
<i class="fa fa-i-cursor"></i>{ parent.title }
|
||||||
<input ref="text" oninput={ parent.update } onkeydown={ parent.onKeydown } placeholder={ parent.placeholder }/>
|
</yield>
|
||||||
</div>
|
<yield to="content">
|
||||||
<div class="action">
|
<div class="body">
|
||||||
<button class="cancel" onclick={ parent.cancel }>キャンセル</button>
|
<input ref="text" oninput={ parent.update } onkeydown={ parent.onKeydown } placeholder={ parent.placeholder }/>
|
||||||
<button class="ok" disabled={ !parent.allowEmpty && refs.text.value.length == 0 } onclick={ parent.ok }>決定</button>
|
</div>
|
||||||
</div></yield>
|
<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>
|
</mk-window>
|
||||||
<style>
|
<style>
|
||||||
:scope
|
:scope
|
||||||
|
@ -116,42 +120,48 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@done = false
|
this.done = false;
|
||||||
|
|
||||||
@title = @opts.title
|
this.title = this.opts.title;
|
||||||
@placeholder = @opts.placeholder
|
this.placeholder = this.opts.placeholder;
|
||||||
@default = @opts.default
|
this.default = this.opts.default;
|
||||||
@allow-empty = if @opts.allow-empty? then @opts.allow-empty else true
|
this.allowEmpty = this.opts.allowEmpty != null ? this.opts.allowEmpty : true;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@text = @refs.window.refs.text
|
this.text = this.refs.window.refs.text;
|
||||||
if @default?
|
if (this.default) this.text.value = this.default;
|
||||||
@text.value = @default
|
this.text.focus();
|
||||||
@text.focus!
|
|
||||||
|
|
||||||
@refs.window.on \closing ~>
|
this.refs.window.on('closing', () => {
|
||||||
if @done
|
if (this.done) {
|
||||||
@opts.on-ok @text.value
|
this.opts.onOk(this.text.value);
|
||||||
else
|
} else {
|
||||||
if @opts.on-cancel?
|
if (this.opts.onCancel) this.opts.onCancel();
|
||||||
@opts.on-cancel!
|
}
|
||||||
|
});
|
||||||
|
|
||||||
@refs.window.on \closed ~>
|
this.refs.window.on('closed', () => {
|
||||||
@unmount!
|
this.unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@cancel = ~>
|
this.cancel = () => {
|
||||||
@done = false
|
this.done = false;
|
||||||
@refs.window.close!
|
this.refs.window.close();
|
||||||
|
};
|
||||||
|
|
||||||
@ok = ~>
|
this.ok = () => {
|
||||||
if not @allow-empty and @text.value == '' then return
|
if (!this.allowEmpty && this.text.value == '') return;
|
||||||
@done = true
|
this.done = true;
|
||||||
@refs.window.close!
|
this.refs.window.close();
|
||||||
|
};
|
||||||
|
|
||||||
@on-keydown = (e) ~>
|
this.onKeydown = e => {
|
||||||
if e.which == 13 # Enter
|
if (e.which == 13) { // Enter
|
||||||
e.prevent-default!
|
e.preventDefault();
|
||||||
e.stop-propagation!
|
e.stopPropagation();
|
||||||
@ok!
|
this.ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-input-dialog>
|
</mk-input-dialog>
|
||||||
|
|
|
@ -93,5 +93,5 @@
|
||||||
right 16px
|
right 16px
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>@user = @opts.user</script>
|
<script>this.user = this.opts.user</script>
|
||||||
</mk-list-user>
|
</mk-list-user>
|
||||||
|
|
|
@ -19,10 +19,12 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@user = @opts.user
|
this.user = this.opts.user;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@refs.window.on \closed ~>
|
this.refs.window.on('closed', () => {
|
||||||
@unmount!
|
this.unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</mk-messaging-room-window>
|
</mk-messaging-room-window>
|
||||||
|
|
|
@ -19,13 +19,16 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@refs.window.on \closed ~>
|
this.refs.window.on('closed', () => {
|
||||||
@unmount!
|
this.unmount();
|
||||||
|
});
|
||||||
|
|
||||||
@refs.window.refs.index.on \navigate-user (user) ~>
|
this.refs.window.refs.index.on('navigate-user', user => {
|
||||||
w = document.body.append-child document.create-element \mk-messaging-room-window
|
riot.mount(document.body.appendChild(document.createElement('mk-messaging-room-window')), {
|
||||||
riot.mount w, do
|
|
||||||
user: user
|
user: user
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</mk-messaging-window>
|
</mk-messaging-window>
|
||||||
|
|
|
@ -177,37 +177,41 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
@mixin \stream
|
this.mixin('stream');
|
||||||
@mixin \user-preview
|
this.mixin('user-preview');
|
||||||
@mixin \get-post-summary
|
this.mixin('get-post-summary');
|
||||||
|
|
||||||
@notifications = []
|
this.notifications = [];
|
||||||
@loading = true
|
this.loading = true;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@api \i/notifications
|
this.api('i/notifications').then(notifications => {
|
||||||
.then (notifications) ~>
|
this.update({
|
||||||
@notifications = notifications
|
loading: false,
|
||||||
@loading = false
|
notifications: notifications
|
||||||
@update!
|
});
|
||||||
.catch (err, text-status) ->
|
});
|
||||||
console.error err
|
|
||||||
|
|
||||||
@stream.on \notification @on-notification
|
this.stream.on('notification', this.onNotification);
|
||||||
|
});
|
||||||
|
|
||||||
@on \unmount ~>
|
this.on('unmount', () => {
|
||||||
@stream.off \notification @on-notification
|
this.stream.off('notification', this.onNotification);
|
||||||
|
});
|
||||||
|
|
||||||
@on-notification = (notification) ~>
|
this.onNotification = notification => {
|
||||||
@notifications.unshift notification
|
this.notifications.unshift(notification);
|
||||||
@update!
|
this.update();
|
||||||
|
};
|
||||||
|
|
||||||
@on \update ~>
|
this.on('update', () => {
|
||||||
@notifications.for-each (notification) ~>
|
this.notifications.forEach(notification => {
|
||||||
date = (new Date notification.created_at).get-date!
|
const date = new Date(notification.created_at).getDate();
|
||||||
month = (new Date notification.created_at).get-month! + 1
|
const month = new Date(notification.created_at).getMonth() + 1;
|
||||||
notification._date = date
|
notification._date = date;
|
||||||
notification._datetext = month + '月 ' + date + '日'
|
notification._datetext = `${month}月 ${date}日`;
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</mk-notifications>
|
</mk-notifications>
|
||||||
|
|
|
@ -63,18 +63,24 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mode = \signin
|
this.mode = 'signin';
|
||||||
|
|
||||||
@signup = ~>
|
this.signup = () => {
|
||||||
@mode = \signup
|
this.update({
|
||||||
@update!
|
mode: 'signup'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@signin = ~>
|
this.signin = () => {
|
||||||
@mode = \signin
|
this.update({
|
||||||
@update!
|
mode: 'signin'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@introduction = ~>
|
this.introduction = () => {
|
||||||
@mode = \introduction
|
this.update({
|
||||||
@update!
|
mode: 'introduction'
|
||||||
|
});
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-entrance>
|
</mk-entrance>
|
||||||
|
|
|
@ -119,12 +119,16 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@refs.signin.on \user (user) ~>
|
this.refs.signin.on('user', user => {
|
||||||
@update do
|
this.update({
|
||||||
user: user
|
user: user
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@introduction = ~>
|
this.introduction = () => {
|
||||||
@parent.introduction!
|
this.parent.introduction();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-entrance-signin>
|
</mk-entrance-signin>
|
||||||
|
|
|
@ -43,9 +43,5 @@
|
||||||
> i
|
> i
|
||||||
padding 14px
|
padding 14px
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
</mk-entrance-signup>
|
</mk-entrance-signup>
|
||||||
|
|
|
@ -5,43 +5,45 @@
|
||||||
<style>
|
<style>
|
||||||
:scope
|
:scope
|
||||||
display block
|
display block
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \i
|
this.mixin('i');
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
@mixin \ui-progress
|
this.mixin('ui-progress');
|
||||||
@mixin \stream
|
this.mixin('stream');
|
||||||
@mixin \get-post-summary
|
this.mixin('get-post-summary');
|
||||||
|
|
||||||
@unread-count = 0
|
this.unreadCount = 0;
|
||||||
|
|
||||||
@page = switch @opts.mode
|
this.page = this.opts.mode || 'timeline';
|
||||||
| \timelie => \home
|
|
||||||
| \mentions => \mentions
|
|
||||||
| _ => \home
|
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@refs.ui.refs.home.on \loaded ~>
|
this.refs.ui.refs.home.on('loaded', () => {
|
||||||
@Progress.done!
|
this.Progress.done();
|
||||||
|
});
|
||||||
|
document.title = 'Misskey';
|
||||||
|
this.Progress.start();
|
||||||
|
this.stream.on('post', this.onStreamPost);
|
||||||
|
document.addEventListener('visibilitychange', this.windowOnVisibilitychange, false);
|
||||||
|
});
|
||||||
|
|
||||||
document.title = 'Misskey'
|
this.on('unmount', () => {
|
||||||
@Progress.start!
|
this.stream.off('post', this.onStreamPost);
|
||||||
@stream.on \post @on-stream-post
|
document.removeEventListener('visibilitychange', this.windowOnVisibilitychange);
|
||||||
document.add-event-listener \visibilitychange @window-on-visibilitychange, false
|
});
|
||||||
|
|
||||||
@on \unmount ~>
|
this.onStreamPost = post => {
|
||||||
@stream.off \post @on-stream-post
|
if (document.hidden && post.user_id != this.I.id) {
|
||||||
document.remove-event-listener \visibilitychange @window-on-visibilitychange
|
this.unreadCount++;
|
||||||
|
document.title = `(${this.unreadCount}) ${this.getPostSummary(post)}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@on-stream-post = (post) ~>
|
this.windowOnVisibilitychange = () => {
|
||||||
if document.hidden and post.user_id !== @I.id
|
if (!document.hidden) {
|
||||||
@unread-count++
|
this.unreadCount = 0;
|
||||||
document.title = '(' + @unread-count + ') ' + @get-post-summary post
|
document.title = 'Misskey';
|
||||||
|
}
|
||||||
@window-on-visibilitychange = ~>
|
};
|
||||||
if !document.hidden
|
|
||||||
@unread-count = 0
|
|
||||||
document.title = 'Misskey'
|
|
||||||
</script>
|
</script>
|
||||||
</mk-home-page>
|
</mk-home-page>
|
||||||
|
|
|
@ -1,54 +1,11 @@
|
||||||
<mk-not-found>
|
<mk-not-found>
|
||||||
<mk-ui>
|
<mk-ui>
|
||||||
<main>
|
<main>
|
||||||
<h1>Not Found</h1><img src="/_/resources/rogge.jpg" alt=""/>
|
<h1>Not Found</h1>
|
||||||
<div class="mask"></div>
|
|
||||||
</main>
|
</main>
|
||||||
</mk-ui>
|
</mk-ui>
|
||||||
<style>
|
<style>
|
||||||
:scope
|
:scope
|
||||||
display block
|
display block
|
||||||
|
|
||||||
main
|
|
||||||
display block
|
|
||||||
width 600px
|
|
||||||
margin 32px auto
|
|
||||||
|
|
||||||
> img
|
|
||||||
display block
|
|
||||||
width 600px
|
|
||||||
height 459px
|
|
||||||
pointer-events none
|
|
||||||
user-select none
|
|
||||||
border-radius 16px
|
|
||||||
box-shadow 0 0 16px rgba(0, 0, 0, 0.1)
|
|
||||||
|
|
||||||
> h1
|
|
||||||
display block
|
|
||||||
margin 0
|
|
||||||
padding 0
|
|
||||||
position absolute
|
|
||||||
top 260px
|
|
||||||
left 225px
|
|
||||||
transform rotate(-12deg)
|
|
||||||
z-index 2
|
|
||||||
color #444
|
|
||||||
font-size 24px
|
|
||||||
line-height 20px
|
|
||||||
|
|
||||||
> .mask
|
|
||||||
position absolute
|
|
||||||
top 262px
|
|
||||||
left 217px
|
|
||||||
width 126px
|
|
||||||
height 18px
|
|
||||||
transform rotate(-12deg)
|
|
||||||
background #D6D5DA
|
|
||||||
border-radius 2px 6px 7px 6px
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
</mk-not-found>
|
</mk-not-found>
|
||||||
|
|
|
@ -16,17 +16,20 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \ui-progress
|
this.mixin('ui-progress');
|
||||||
|
|
||||||
@post = @opts.post
|
this.post = this.opts.post;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@Progress.start!
|
this.Progress.start();
|
||||||
|
|
||||||
@refs.ui.refs.detail.on \post-fetched ~>
|
this.refs.ui.refs.detail.on('post-fetched', () => {
|
||||||
@Progress.set 0.5
|
this.Progress.set(0.5);
|
||||||
|
});
|
||||||
|
|
||||||
@refs.ui.refs.detail.on \loaded ~>
|
this.refs.ui.refs.detail.on('loaded', () => {
|
||||||
@Progress.done!
|
this.Progress.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</mk-post-page>
|
</mk-post-page>
|
||||||
|
|
|
@ -5,15 +5,16 @@
|
||||||
<style>
|
<style>
|
||||||
:scope
|
:scope
|
||||||
display block
|
display block
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \ui-progress
|
this.mixin('ui-progress');
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@Progress.start!
|
this.Progress.start();
|
||||||
|
|
||||||
@refs.ui.refs.search.on \loaded ~>
|
this.refs.ui.refs.search.on('loaded', () => {
|
||||||
@Progress.done!
|
this.Progress.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</mk-search-page>
|
</mk-search-page>
|
||||||
|
|
|
@ -5,21 +5,23 @@
|
||||||
<style>
|
<style>
|
||||||
:scope
|
:scope
|
||||||
display block
|
display block
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \ui-progress
|
this.mixin('ui-progress');
|
||||||
|
|
||||||
@user = @opts.user
|
this.user = this.opts.user;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@Progress.start!
|
this.Progress.start();
|
||||||
|
|
||||||
@refs.ui.refs.user.on \user-fetched (user) ~>
|
this.refs.ui.refs.user.on('user-fetched', user => {
|
||||||
@Progress.set 0.5
|
this.Progress.set(0.5);
|
||||||
document.title = user.name + ' | Misskey'
|
document.title = user.name + ' | Misskey'
|
||||||
|
});
|
||||||
|
|
||||||
@refs.ui.refs.user.on \loaded ~>
|
this.refs.ui.refs.user.on('loaded', () => {
|
||||||
@Progress.done!
|
this.Progress.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</mk-user-page>
|
</mk-user-page>
|
||||||
|
|
|
@ -103,38 +103,45 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
@mixin \text
|
this.mixin('text');
|
||||||
@mixin \date-stringify
|
this.mixin('date-stringify');
|
||||||
@mixin \user-preview
|
this.mixin('user-preview');
|
||||||
|
|
||||||
@post = @opts.post
|
this.post = this.opts.post;
|
||||||
|
|
||||||
@url = CONFIG.url + '/' + @post.user.username + '/' + @post.id
|
this.url = CONFIG.url + '/' + this.post.user.username + '/' + this.post.id;
|
||||||
|
|
||||||
@title = @date-stringify @post.created_at
|
this.title = this.dateStringify(this.post.created_at);
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
if @post.text?
|
if (this.p.text) {
|
||||||
tokens = @analyze @post.text
|
const tokens = this.analyze(this.p.text);
|
||||||
@refs.text.innerHTML = @compile tokens
|
|
||||||
|
|
||||||
@refs.text.children.for-each (e) ~>
|
this.refs.text.innerHTML = this.refs.text.innerHTML.replace('<p class="dummy"></p>', this.compile(tokens));
|
||||||
if e.tag-name == \MK-URL
|
|
||||||
riot.mount e
|
|
||||||
|
|
||||||
@like = ~>
|
this.refs.text.children.forEach(e => {
|
||||||
if @post.is_liked
|
if (e.tagName == 'MK-URL') riot.mount(e);
|
||||||
@api \posts/likes/delete do
|
});
|
||||||
post_id: @post.id
|
}
|
||||||
.then ~>
|
});
|
||||||
@post.is_liked = false
|
|
||||||
@update!
|
this.like = () => {
|
||||||
else
|
if (this.post.is_liked) {
|
||||||
@api \posts/likes/create do
|
this.api('posts/likes/delete', {
|
||||||
post_id: @post.id
|
post_id: this.post.id
|
||||||
.then ~>
|
}).then(() => {
|
||||||
@post.is_liked = true
|
this.post.is_liked = false;
|
||||||
@update!
|
this.update();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.api('posts/likes/create', {
|
||||||
|
post_id: this.post.id
|
||||||
|
}).then(() => {
|
||||||
|
this.post.is_liked = true;
|
||||||
|
this.update();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-post-detail-sub>
|
</mk-post-detail-sub>
|
||||||
|
|
|
@ -329,108 +329,126 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
@mixin \text
|
this.mixin('text');
|
||||||
@mixin \user-preview
|
this.mixin('user-preview');
|
||||||
@mixin \date-stringify
|
this.mixin('date-stringify');
|
||||||
@mixin \NotImplementedException
|
this.mixin('NotImplementedException');
|
||||||
|
|
||||||
@fetching = true
|
this.fetching = true;
|
||||||
@loading-context = false
|
this.loadingContext = false;
|
||||||
@content = null
|
this.content = null;
|
||||||
@post = null
|
this.post = null;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
|
this.api('posts/show', {
|
||||||
|
post_id: this.opts.post
|
||||||
|
}).then(post => {
|
||||||
|
const isRepost = post.repost != null;
|
||||||
|
const p = isRepost ? post.repost : post;
|
||||||
|
this.update({
|
||||||
|
fetching: false,
|
||||||
|
post: post,
|
||||||
|
isRepost: isRepost,
|
||||||
|
p: p,
|
||||||
|
title: this.dateStringify(p.created_at)
|
||||||
|
});
|
||||||
|
|
||||||
@api \posts/show do
|
this.trigger('loaded');
|
||||||
post_id: @opts.post
|
|
||||||
.then (post) ~>
|
|
||||||
@fetching = false
|
|
||||||
@post = post
|
|
||||||
@trigger \loaded
|
|
||||||
|
|
||||||
@is-repost = @post.repost?
|
if (this.p.text) {
|
||||||
@p = if @is-repost then @post.repost else @post
|
const tokens = this.analyze(this.p.text);
|
||||||
|
|
||||||
@title = @date-stringify @p.created_at
|
this.refs.text.innerHTML = this.compile(tokens);
|
||||||
|
|
||||||
@update!
|
this.refs.text.children.forEach(e => {
|
||||||
|
if (e.tagName == 'MK-URL') riot.mount(e);
|
||||||
|
});
|
||||||
|
|
||||||
if @p.text?
|
// URLをプレビュー
|
||||||
tokens = @analyze @p.text
|
|
||||||
@refs.text.innerHTML = @compile tokens
|
|
||||||
|
|
||||||
@refs.text.children.for-each (e) ~>
|
|
||||||
if e.tag-name == \MK-URL
|
|
||||||
riot.mount e
|
|
||||||
|
|
||||||
# URLをプレビュー
|
|
||||||
tokens
|
tokens
|
||||||
.filter (t) -> t.type == \link
|
.filter(t => t.type == 'link')
|
||||||
.map (t) ~>
|
.map(t => {
|
||||||
@preview = @refs.text.append-child document.create-element \mk-url-preview
|
riot.mount(this.refs.text.appendChild(document.createElement('mk-url-preview')), {
|
||||||
riot.mount @preview, do
|
url: t.content
|
||||||
url: t.content
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
# Get likes
|
// Get likes
|
||||||
@api \posts/likes do
|
this.api('posts/likes', {
|
||||||
post_id: @p.id
|
post_id: this.p.id,
|
||||||
limit: 8
|
limit: 8
|
||||||
.then (likes) ~>
|
}).then(likes => {
|
||||||
@likes = likes
|
this.update({
|
||||||
@update!
|
likes: likes
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
# Get reposts
|
// Get reposts
|
||||||
@api \posts/reposts do
|
this.api('posts/reposts', {
|
||||||
post_id: @p.id
|
post_id: this.p.id,
|
||||||
limit: 8
|
limit: 8
|
||||||
.then (reposts) ~>
|
}).then(reposts => {
|
||||||
@reposts = reposts
|
this.update({
|
||||||
@update!
|
reposts: reposts
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
# Get replies
|
// Get replies
|
||||||
@api \posts/replies do
|
this.api('posts/replies', {
|
||||||
post_id: @p.id
|
post_id: this.p.id,
|
||||||
limit: 8
|
limit: 8
|
||||||
.then (replies) ~>
|
}).then(replies => {
|
||||||
@replies = replies
|
this.update({
|
||||||
@update!
|
replies: replies
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@update!
|
this.reply = () => {
|
||||||
|
riot.mount(document.body.appendChild(document.createElement('mk-post-form-window')), {
|
||||||
|
reply: this.p
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@reply = ~>
|
this.repost = () => {
|
||||||
form = document.body.append-child document.create-element \mk-post-form-window
|
riot.mount(document.body.appendChild(document.createElement('mk-repost-form-window')), {
|
||||||
riot.mount form, do
|
post: this.p
|
||||||
reply: @p
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@repost = ~>
|
this.like = () => {
|
||||||
form = document.body.append-child document.create-element \mk-repost-form-window
|
if (this.p.is_liked) {
|
||||||
riot.mount form, do
|
this.api('posts/likes/delete', {
|
||||||
post: @p
|
post_id: this.p.id
|
||||||
|
}).then(() => {
|
||||||
|
this.p.is_liked = false;
|
||||||
|
this.update();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.api('posts/likes/create', {
|
||||||
|
post_id: this.p.id
|
||||||
|
}).then(() => {
|
||||||
|
this.p.is_liked = true;
|
||||||
|
this.update();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@like = ~>
|
this.loadContext = () => {
|
||||||
if @p.is_liked
|
this.loadingContext = true;
|
||||||
@api \posts/likes/delete do
|
|
||||||
post_id: @p.id
|
|
||||||
.then ~>
|
|
||||||
@p.is_liked = false
|
|
||||||
@update!
|
|
||||||
else
|
|
||||||
@api \posts/likes/create do
|
|
||||||
post_id: @p.id
|
|
||||||
.then ~>
|
|
||||||
@p.is_liked = true
|
|
||||||
@update!
|
|
||||||
|
|
||||||
@load-context = ~>
|
// Fetch context
|
||||||
@loading-context = true
|
this.api('posts/context', {
|
||||||
|
post_id: this.p.reply_to_id
|
||||||
# Get context
|
}).then(context => {
|
||||||
@api \posts/context do
|
this.update({
|
||||||
post_id: @p.reply_to_id
|
loadContext: false,
|
||||||
.then (context) ~>
|
content: context.reverse()
|
||||||
@context = context.reverse!
|
});
|
||||||
@loading-context = false
|
});
|
||||||
@update!
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-post-detail>
|
</mk-post-detail>
|
||||||
|
|
|
@ -32,24 +32,31 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@uploading-files = []
|
this.uploadingFiles = [];
|
||||||
@files = []
|
this.files = [];
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@refs.window.refs.form.focus!
|
this.refs.window.refs.form.focus();
|
||||||
|
|
||||||
@refs.window.on \closed ~>
|
this.refs.window.on('closed', () => {
|
||||||
@unmount!
|
this.unmount();
|
||||||
|
});
|
||||||
|
|
||||||
@refs.window.refs.form.on \post ~>
|
this.refs.window.refs.form.on('post', () => {
|
||||||
@refs.window.close!
|
this.refs.window.close();
|
||||||
|
});
|
||||||
|
|
||||||
@refs.window.refs.form.on \change-uploading-files (files) ~>
|
this.refs.window.refs.form.on('change-uploading-files', files => {
|
||||||
@uploading-files = files
|
this.update({
|
||||||
@update!
|
uploadingFiles: files
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@refs.window.refs.form.on \change-files (files) ~>
|
this.refs.window.refs.form.on('change-files', files => {
|
||||||
@files = files
|
this.update({
|
||||||
@update!
|
files: files
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</mk-post-form-window>
|
</mk-post-form-window>
|
||||||
|
|
|
@ -305,161 +305,160 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
get-cat = require '../../common/scripts/get-cat'
|
const getCat = require('../../common/scripts/get-cat');
|
||||||
|
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
@mixin \notify
|
this.mixin('notify');
|
||||||
@mixin \autocomplete
|
this.mixin('autocomplete');
|
||||||
|
|
||||||
@wait = false
|
this.wait = false;
|
||||||
@uploadings = []
|
this.uploadings = [];
|
||||||
@files = []
|
this.files = [];
|
||||||
@autocomplete = null
|
this.autocomplete = null;
|
||||||
@poll = false
|
this.poll = false;
|
||||||
|
|
||||||
@in-reply-to-post = @opts.reply
|
this.inReplyToPost = this.opts.reply;
|
||||||
|
|
||||||
# https://github.com/riot/riot/issues/2080
|
// https://github.com/riot/riot/issues/2080
|
||||||
if @in-reply-to-post == '' then @in-reply-to-post = null
|
if (this.inReplyToPost == '') this.inReplyToPost = null;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@refs.uploader.on \uploaded (file) ~>
|
this.refs.uploader.on('uploaded', file => {
|
||||||
@add-file file
|
this.addFile(file);
|
||||||
|
});
|
||||||
|
|
||||||
@refs.uploader.on \change-uploads (uploads) ~>
|
this.refs.uploader.on('change-uploads', uploads => {
|
||||||
@trigger \change-uploading-files uploads
|
this.trigger('change-uploading-files', uploads);
|
||||||
|
});
|
||||||
|
|
||||||
@autocomplete = new @Autocomplete @refs.text
|
this.autocomplete = new this.Autocomplete(this.refs.text);
|
||||||
@autocomplete.attach!
|
this.autocomplete.attach();
|
||||||
|
});
|
||||||
|
|
||||||
@on \unmount ~>
|
this.on('unmount', () => {
|
||||||
@autocomplete.detach!
|
this.autocomplete.detach();
|
||||||
|
});
|
||||||
|
|
||||||
@focus = ~>
|
this.focus = () => {
|
||||||
@refs.text.focus!
|
this.refs.text.focus();
|
||||||
|
};
|
||||||
|
|
||||||
@clear = ~>
|
this.clear = () => {
|
||||||
@refs.text.value = ''
|
this.refs.text.value = '';
|
||||||
@files = []
|
this.files = [];
|
||||||
@trigger \change-files
|
this.trigger('change-files');
|
||||||
@update!
|
this.update();
|
||||||
|
};
|
||||||
|
|
||||||
@ondragover = (e) ~>
|
this.ondragover = e => {
|
||||||
e.stop-propagation!
|
e.stopPropagation();
|
||||||
@draghover = true
|
this.draghover = true;
|
||||||
# ドラッグされてきたものがファイルだったら
|
e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move';
|
||||||
if e.data-transfer.effect-allowed == \all
|
return false;
|
||||||
e.data-transfer.drop-effect = \copy
|
};
|
||||||
else
|
|
||||||
e.data-transfer.drop-effect = \move
|
|
||||||
return false
|
|
||||||
|
|
||||||
@ondragenter = (e) ~>
|
this.ondragenter = e => {
|
||||||
@draghover = true
|
this.draghover = true;
|
||||||
|
};
|
||||||
|
|
||||||
@ondragleave = (e) ~>
|
this.ondragleave = e => {
|
||||||
@draghover = false
|
this.draghover = false;
|
||||||
|
};
|
||||||
|
|
||||||
@ondrop = (e) ~>
|
this.ondrop = e => {
|
||||||
e.prevent-default!
|
e.preventDefault();
|
||||||
e.stop-propagation!
|
e.stopPropagation();
|
||||||
@draghover = false
|
this.draghover = false;
|
||||||
|
|
||||||
# ファイルだったら
|
// ファイルだったら
|
||||||
if e.data-transfer.files.length > 0
|
if (e.dataTransfer.files.length > 0) {
|
||||||
Array.prototype.for-each.call e.data-transfer.files, (file) ~>
|
e.dataTransfer.files.forEach(this.upload);
|
||||||
@upload file
|
}
|
||||||
return false
|
|
||||||
|
|
||||||
# データ取得
|
return false;
|
||||||
data = e.data-transfer.get-data 'text'
|
};
|
||||||
if !data?
|
|
||||||
return false
|
|
||||||
|
|
||||||
try
|
this.onkeydown = e => {
|
||||||
# パース
|
if ((e.which == 10 || e.which == 13) && (e.ctrlKey || e.metaKey)) this.post();
|
||||||
obj = JSON.parse data
|
};
|
||||||
|
|
||||||
# (ドライブの)ファイルだったら
|
this.onpaste = e => {
|
||||||
if obj.type == \file
|
e.clipboardData.items.forEach(item => {
|
||||||
@add-file obj.file
|
if (item.kind == 'file') {
|
||||||
catch
|
this.upload(item.getAsFile());
|
||||||
# ignore
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return false
|
this.selectFile = () => {
|
||||||
|
this.refs.file.click();
|
||||||
|
};
|
||||||
|
|
||||||
@onkeydown = (e) ~>
|
this.selectFileFromDrive = () => {
|
||||||
if (e.which == 10 || e.which == 13) && (e.ctrl-key || e.meta-key)
|
const i = riot.mount(document.body.appendChild(document.createElement('mk-select-file-from-drive-window')), {
|
||||||
@post!
|
|
||||||
|
|
||||||
@onpaste = (e) ~>
|
|
||||||
data = e.clipboard-data
|
|
||||||
items = data.items
|
|
||||||
for i from 0 to items.length - 1
|
|
||||||
item = items[i]
|
|
||||||
switch (item.kind)
|
|
||||||
| \file =>
|
|
||||||
@upload item.get-as-file!
|
|
||||||
|
|
||||||
@select-file = ~>
|
|
||||||
@refs.file.click!
|
|
||||||
|
|
||||||
@select-file-from-drive = ~>
|
|
||||||
browser = document.body.append-child document.create-element \mk-select-file-from-drive-window
|
|
||||||
i = riot.mount browser, do
|
|
||||||
multiple: true
|
multiple: true
|
||||||
i[0].one \selected (files) ~>
|
})[0];
|
||||||
files.for-each @add-file
|
i.one('selected', files => {
|
||||||
|
files.forEach(this.addFile);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@change-file = ~>
|
this.changeFile = () => {
|
||||||
files = @refs.file.files
|
this.refs.file.files.forEach(this.upload);
|
||||||
for i from 0 to files.length - 1
|
};
|
||||||
file = files.item i
|
|
||||||
@upload file
|
|
||||||
|
|
||||||
@upload = (file) ~>
|
this.upload = file => {
|
||||||
@refs.uploader.upload file
|
this.refs.uploader.upload(file);
|
||||||
|
};
|
||||||
|
|
||||||
@add-file = (file) ~>
|
this.addFile = file => {
|
||||||
file._remove = ~>
|
file._remove = () => {
|
||||||
@files = @files.filter (x) -> x.id != file.id
|
this.files = this.files.filter(x => x.id != file.id);
|
||||||
@trigger \change-files @files
|
this.trigger('change-files', this.files);
|
||||||
@update!
|
this.update();
|
||||||
|
};
|
||||||
|
|
||||||
@files.push file
|
this.files.push(file);
|
||||||
@trigger \change-files @files
|
this.trigger('change-files', this.files);
|
||||||
@update!
|
this.update();
|
||||||
|
};
|
||||||
|
|
||||||
@add-poll = ~>
|
this.addPoll = () => {
|
||||||
@poll = true
|
this.poll = true;
|
||||||
|
};
|
||||||
|
|
||||||
@on-poll-destroyed = ~>
|
this.onPollDestroyed = () => {
|
||||||
@update do
|
this.update({
|
||||||
poll: false
|
poll: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@post = (e) ~>
|
this.post = e => {
|
||||||
@wait = true
|
this.wait = true;
|
||||||
|
|
||||||
files = if @files? and @files.length > 0
|
const files = this.files && this.files.length > 0
|
||||||
then @files.map (f) -> f.id
|
? this.files.map(f => f.id)
|
||||||
else undefined
|
: undefined;
|
||||||
|
|
||||||
@api \posts/create do
|
this.api('posts/create', {
|
||||||
text: @refs.text.value
|
text: this.refs.text.value,
|
||||||
media_ids: files
|
media_ids: files,
|
||||||
reply_to_id: if @in-reply-to-post? then @in-reply-to-post.id else undefined
|
reply_to_id: this.inReplyToPost ? this.inReplyToPost.id : undefined,
|
||||||
poll: if @poll then @refs.poll.get! else undefined
|
poll: this.poll ? this.refs.poll.get() : undefined
|
||||||
.then (data) ~>
|
}).then(data => {
|
||||||
@trigger \post
|
this.trigger('post');
|
||||||
@notify if @in-reply-to-post? then '返信しました!' else '投稿しました!'
|
this.notify(this.inReplyToPost ? '返信しました!' : '投稿しました!');
|
||||||
.catch (err) ~>
|
}).catch(err => {
|
||||||
console.error err
|
this.notify('投稿できませんでした');
|
||||||
@notify '投稿できませんでした'
|
}).then(() => {
|
||||||
.then ~>
|
this.update({
|
||||||
@wait = false
|
wait: false
|
||||||
@update!
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@cat = ~>
|
this.cat = () => {
|
||||||
@refs.text.value = @refs.text.value + get-cat!
|
this.refs.text.value += getCat();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-post-form>
|
</mk-post-form>
|
||||||
|
|
|
@ -83,11 +83,11 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \date-stringify
|
this.mixin('date-stringify');
|
||||||
@mixin \user-preview
|
this.mixin('user-preview');
|
||||||
|
|
||||||
@post = @opts.post
|
this.post = this.opts.post;
|
||||||
|
|
||||||
@title = @date-stringify @post.created_at
|
this.title = this.dateStringify(this.post.created_at);
|
||||||
</script>
|
</script>
|
||||||
</mk-post-preview>
|
</mk-post-preview>
|
||||||
|
|
|
@ -75,20 +75,25 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@title = @opts.title
|
this.title = this.opts.title;
|
||||||
@value = parse-int @opts.value, 10
|
this.value = parseInt(this.opts.value, 10);
|
||||||
@max = parse-int @opts.max, 10
|
this.max = parseInt(this.opts.max, 10);
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@refs.window.on \closed ~>
|
this.refs.window.on('closed', () => {
|
||||||
@unmount!
|
this.unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@update-progress = (value, max) ~>
|
this.updateProgress = (value, max) => {
|
||||||
@value = parse-int value, 10
|
this.update({
|
||||||
@max = parse-int max, 10
|
value: parseInt(value, 10),
|
||||||
@update!
|
max: parseInt(max, 10)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@close = ~>
|
this.close = () => {
|
||||||
@refs.window.close!
|
this.refs.window.close();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-progress-dialog>
|
</mk-progress-dialog>
|
||||||
|
|
|
@ -12,25 +12,32 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@on-document-keydown = (e) ~>
|
this.onDocumentKeydown = e => {
|
||||||
tag = e.target.tag-name.to-lower-case!
|
if (e.target.tagName != 'INPUT' && e.target.tagName != 'TEXTAREA') {
|
||||||
if tag != \input and tag != \textarea
|
if (e.which == 27) { // Esc
|
||||||
if e.which == 27 # Esc
|
this.refs.window.close();
|
||||||
@refs.window.close!
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@refs.window.refs.form.on \cancel ~>
|
this.refs.window.refs.form.on('cancel', () => {
|
||||||
@refs.window.close!
|
this.refs.window.close();
|
||||||
|
});
|
||||||
|
|
||||||
@refs.window.refs.form.on \posted ~>
|
this.refs.window.refs.form.on('posted', () => {
|
||||||
@refs.window.close!
|
this.refs.window.close();
|
||||||
|
});
|
||||||
|
|
||||||
document.add-event-listener \keydown @on-document-keydown
|
document.addEventListener('keydown', this.onDocumentKeydown);
|
||||||
|
|
||||||
@refs.window.on \closed ~>
|
this.refs.window.on('closed', () => {
|
||||||
@unmount!
|
this.unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@on \unmount ~>
|
this.on('unmount', () => {
|
||||||
document.remove-event-listener \keydown @on-document-keydown
|
document.removeEventListener('keydown', this.onDocumentKeydown);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</mk-repost-form-window>
|
</mk-repost-form-window>
|
||||||
|
|
|
@ -114,31 +114,35 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
@mixin \notify
|
this.mixin('notify');
|
||||||
|
|
||||||
@wait = false
|
this.wait = false;
|
||||||
@quote = false
|
this.quote = false;
|
||||||
|
|
||||||
@cancel = ~>
|
this.cancel = () => {
|
||||||
@trigger \cancel
|
this.trigger('cancel');
|
||||||
|
};
|
||||||
|
|
||||||
@ok = ~>
|
this.ok = () => {
|
||||||
@wait = true
|
this.wait = true;
|
||||||
@api \posts/create do
|
this.api('posts/create', {
|
||||||
repost_id: @opts.post.id
|
repost_id: this.opts.post.id,
|
||||||
text: if @quote then @refs.text.value else undefined
|
text: this.quote ? this.refs.text.value : undefined
|
||||||
.then (data) ~>
|
}).then(data => {
|
||||||
@trigger \posted
|
this.trigger('posted');
|
||||||
@notify 'Repostしました!'
|
this.notify('Repostしました!');
|
||||||
.catch (err) ~>
|
}).catch(err => {
|
||||||
console.error err
|
this.notify('Repostできませんでした');
|
||||||
@notify 'Repostできませんでした'
|
}).then(() => {
|
||||||
.then ~>
|
this.update({
|
||||||
@wait = false
|
wait: false
|
||||||
@update!
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@onquote = ~>
|
this.onquote = () => {
|
||||||
@quote = true
|
this.quote = true;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-repost-form>
|
</mk-repost-form>
|
||||||
|
|
|
@ -28,59 +28,64 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
@mixin \get-post-summary
|
this.mixin('get-post-summary');
|
||||||
|
|
||||||
@query = @opts.query
|
this.query = this.opts.query;
|
||||||
@is-loading = true
|
this.isLoading = true;
|
||||||
@is-empty = false
|
this.isEmpty = false;
|
||||||
@more-loading = false
|
this.moreLoading = false;
|
||||||
@page = 0
|
this.page = 0;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
document.add-event-listener \keydown @on-document-keydown
|
document.addEventListener('keydown', this.onDocumentKeydown);
|
||||||
window.add-event-listener \scroll @on-scroll
|
window.addEventListener('scroll', this.onScroll);
|
||||||
|
|
||||||
@api \posts/search do
|
this.api('posts/search', {
|
||||||
query: @query
|
query: this.query
|
||||||
.then (posts) ~>
|
}).then(posts => {
|
||||||
@is-loading = false
|
this.update({
|
||||||
@is-empty = posts.length == 0
|
isLoading: false,
|
||||||
@update!
|
isEmpty: posts.length == 0
|
||||||
@refs.timeline.set-posts posts
|
});
|
||||||
@trigger \loaded
|
this.refs.timeline.setPosts(posts);
|
||||||
.catch (err) ~>
|
this.trigger('loaded');
|
||||||
console.error err
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@on \unmount ~>
|
this.on('unmount', () => {
|
||||||
document.remove-event-listener \keydown @on-document-keydown
|
document.removeEventListener('keydown', this.onDocumentKeydown);
|
||||||
window.remove-event-listener \scroll @on-scroll
|
window.removeEventListener('scroll', this.onScroll);
|
||||||
|
});
|
||||||
|
|
||||||
@on-document-keydown = (e) ~>
|
this.onDocumentKeydown = e => {
|
||||||
tag = e.target.tag-name.to-lower-case!
|
if (e.target.tagName != 'INPUT' && e.target.tagName != 'TEXTAREA') {
|
||||||
if tag != \input and tag != \textarea
|
if (e.which == 84) { // t
|
||||||
if e.which == 84 # t
|
this.refs.timeline.focus();
|
||||||
@refs.timeline.focus!
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@more = ~>
|
this.more = () => {
|
||||||
if @more-loading or @is-loading or @timeline.posts.length == 0
|
if (this.moreLoading || this.isLoading || this.timeline.posts.length == 0) return;
|
||||||
return
|
this.update({
|
||||||
@more-loading = true
|
moreLoading: true
|
||||||
@update!
|
});
|
||||||
@api \posts/search do
|
this.api('posts/search', {
|
||||||
query: @query
|
query: this.query,
|
||||||
page: @page + 1
|
page: this.page + 1
|
||||||
.then (posts) ~>
|
}).then(posts => {
|
||||||
@more-loading = false
|
this.update({
|
||||||
@page++
|
moreLoading: false,
|
||||||
@update!
|
page: page + 1
|
||||||
@refs.timeline.prepend-posts posts
|
});
|
||||||
.catch (err) ~>
|
this.refs.timeline.prependPosts(posts);
|
||||||
console.error err
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@on-scroll = ~>
|
this.onScroll = () => {
|
||||||
current = window.scroll-y + window.inner-height
|
const current = window.scrollY + window.innerHeight;
|
||||||
if current > document.body.offset-height - 16 # 遊び
|
if (current > document.body.offsetHeight - 16) this.more();
|
||||||
@more!
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-search-posts>
|
</mk-search-posts>
|
||||||
|
|
|
@ -23,10 +23,12 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@query = @opts.query
|
this.query = this.opts.query;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@refs.posts.on \loaded ~>
|
this.refs.posts.on('loaded', () => {
|
||||||
@trigger \loaded
|
this.trigger('loaded');
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</mk-search>
|
</mk-search>
|
||||||
|
|
|
@ -131,31 +131,38 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@file = []
|
this.file = [];
|
||||||
|
|
||||||
@multiple = if @opts.multiple? then @opts.multiple else false
|
this.multiple = this.opts.multiple != null ? this.opts.multiple : false;
|
||||||
@title = @opts.title || '<i class="fa fa-file-o"></i>ファイルを選択'
|
this.title = this.opts.title || '<i class="fa fa-file-o"></i>ファイルを選択';
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@refs.window.refs.browser.on \selected (file) ~>
|
this.refs.window.refs.browser.on('selected', file => {
|
||||||
@file = file
|
this.file = file;
|
||||||
@ok!
|
this.ok();
|
||||||
|
});
|
||||||
|
|
||||||
@refs.window.refs.browser.on \change-selection (files) ~>
|
this.refs.window.refs.browser.on('change-selection', files => {
|
||||||
@file = files
|
this.file = files;
|
||||||
@update!
|
this.update();
|
||||||
|
});
|
||||||
|
|
||||||
@refs.window.on \closed ~>
|
this.refs.window.on('closed', () => {
|
||||||
@unmount!
|
this.unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@close = ~>
|
this.close = () => {
|
||||||
@refs.window.close!
|
this.refs.window.close();
|
||||||
|
};
|
||||||
|
|
||||||
@upload = ~>
|
this.upload = () => {
|
||||||
@refs.window.refs.browser.select-local-file!
|
this.refs.window.refs.browser.selectLocalFile();
|
||||||
|
};
|
||||||
|
|
||||||
@ok = ~>
|
this.ok = () => {
|
||||||
@trigger \selected @file
|
this.trigger('selected', this.file);
|
||||||
@refs.window.close!
|
this.refs.window.close();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-select-file-from-drive-window>
|
</mk-select-file-from-drive-window>
|
||||||
|
|
|
@ -31,15 +31,17 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \i
|
this.mixin('i');
|
||||||
@mixin \update-avatar
|
this.mixin('update-avatar');
|
||||||
|
|
||||||
@set = ~>
|
this.set = () => {
|
||||||
@update-avatar @I
|
this.updateAvatar(this.I);
|
||||||
|
};
|
||||||
|
|
||||||
@close = (e) ~>
|
this.close = e => {
|
||||||
e.prevent-default!
|
e.preventDefault();
|
||||||
e.stop-propagation!
|
e.stopPropagation();
|
||||||
@unmount!
|
this.unmount();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-set-avatar-suggestion>
|
</mk-set-avatar-suggestion>
|
||||||
|
|
|
@ -31,15 +31,17 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \i
|
this.mixin('i');
|
||||||
@mixin \update-banner
|
this.mixin('update-banner');
|
||||||
|
|
||||||
@set = ~>
|
this.set = () => {
|
||||||
@update-banner @I
|
this.updateBanner(this.I);
|
||||||
|
};
|
||||||
|
|
||||||
@close = (e) ~>
|
this.close = e => {
|
||||||
e.prevent-default!
|
e.preventDefault();
|
||||||
e.stop-propagation!
|
e.stopPropagation();
|
||||||
@unmount!
|
this.unmount();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-set-banner-suggestion>
|
</mk-set-banner-suggestion>
|
||||||
|
|
|
@ -15,11 +15,14 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
@refs.window.on \closed ~>
|
this.refs.window.on('closed', () => {
|
||||||
@unmount!
|
this.unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@close = ~>
|
this.close = () => {
|
||||||
@refs.window.close!
|
this.refs.window.close();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-settings-window>
|
</mk-settings-window>
|
||||||
|
|
|
@ -47,11 +47,6 @@
|
||||||
<p>読み込みを高速化する</p>
|
<p>読み込みを高速化する</p>
|
||||||
<p>API通信時に新鮮なユーザー情報をキャッシュすることでフェッチのオーバーヘッドを無くします。(実験的)</p>
|
<p>API通信時に新鮮なユーザー情報をキャッシュすることでフェッチのオーバーヘッドを無くします。(実験的)</p>
|
||||||
</label>
|
</label>
|
||||||
<label class="checkbox">
|
|
||||||
<input type="checkbox" checked={ I.data.debug } onclick={ updateDebug }/>
|
|
||||||
<p>開発者モード</p>
|
|
||||||
<p>デバッグ等の開発者モードを有効にします。</p>
|
|
||||||
</label>
|
|
||||||
<label class="checkbox">
|
<label class="checkbox">
|
||||||
<input type="checkbox" checked={ I.data.nya } onclick={ updateNya }/>
|
<input type="checkbox" checked={ I.data.nya } onclick={ updateNya }/>
|
||||||
<p><i>な</i>を<i>にゃ</i>に変換する</p>
|
<p><i>な</i>を<i>にゃ</i>に変換する</p>
|
||||||
|
@ -198,46 +193,49 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \i
|
this.mixin('i');
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
@mixin \dialog
|
this.mixin('notify');
|
||||||
@mixin \update-avatar
|
this.mixin('dialog');
|
||||||
|
this.mixin('update-avatar');
|
||||||
|
|
||||||
@page = \account
|
this.page = 'account';
|
||||||
|
|
||||||
@set-page = (page) ~>
|
this.setPage = page => {
|
||||||
@page = page
|
this.page = page;
|
||||||
|
};
|
||||||
|
|
||||||
@avatar = ~>
|
this.avatar = () => {
|
||||||
@update-avatar @I
|
this.updateAvatar(this.I);
|
||||||
|
};
|
||||||
|
|
||||||
@update-account = ~>
|
this.updateAccount = () => {
|
||||||
@api \i/update do
|
this.api('i/update', {
|
||||||
name: @refs.account-name.value
|
name: this.refs.accountName.value,
|
||||||
location: @refs.account-location.value
|
location: this.refs.accountLocation.value,
|
||||||
bio: @refs.account-bio.value
|
bio: this.refs.accountBio.value,
|
||||||
birthday: @refs.account-birthday.value
|
birthday: this.refs.accountBirthday.value
|
||||||
.then (i) ~>
|
}).then(() => {
|
||||||
alert \ok
|
this.notify('プロフィールを更新しました');
|
||||||
.catch (err) ~>
|
});
|
||||||
console.error err
|
};
|
||||||
|
|
||||||
@update-cache = ~>
|
this.updateCache = () => {
|
||||||
@I.data.cache = !@I.data.cache
|
this.I.data.cache = !this.I.data.cache;
|
||||||
@api \i/appdata/set do
|
this.api('i/appdata/set', {
|
||||||
data: JSON.stringify do
|
data: JSON.stringify({
|
||||||
cache: @I.data.cache
|
cache: this.I.data.cache
|
||||||
|
})
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@update-debug = ~>
|
this.updateNya = () => {
|
||||||
@I.data.debug = !@I.data.debug
|
this.I.data.nya = !this.I.data.nya;
|
||||||
@api \i/appdata/set do
|
this.api('i/appdata/set', {
|
||||||
data: JSON.stringify do
|
data: JSON.stringify({
|
||||||
debug: @I.data.debug
|
nya: this.I.data.nya
|
||||||
|
})
|
||||||
@update-nya = ~>
|
});
|
||||||
@I.data.nya = !@I.data.nya
|
};
|
||||||
@api \i/appdata/set do
|
|
||||||
data: JSON.stringify do
|
|
||||||
nya: @I.data.nya
|
|
||||||
</script>
|
</script>
|
||||||
</mk-settings>
|
</mk-settings>
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
<mk-stream-indicator>
|
|
||||||
<p if={ state == 'initializing' }><i class="fa fa-spinner fa-spin"></i><span>接続中
|
|
||||||
<mk-ellipsis></mk-ellipsis></span></p>
|
|
||||||
<p if={ state == 'reconnecting' }><i class="fa fa-spinner fa-spin"></i><span>切断されました 接続中
|
|
||||||
<mk-ellipsis></mk-ellipsis></span></p>
|
|
||||||
<p if={ state == 'connected' }><i class="fa fa-check"></i><span>接続完了</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)
|
|
||||||
|
|
||||||
> p
|
|
||||||
display block
|
|
||||||
margin 0
|
|
||||||
|
|
||||||
> i
|
|
||||||
margin-right 0.25em
|
|
||||||
|
|
||||||
</style>
|
|
||||||
<script>
|
|
||||||
@mixin \stream
|
|
||||||
|
|
||||||
@on \before-mount ~>
|
|
||||||
@state = @get-stream-state!
|
|
||||||
|
|
||||||
if @state == \connected
|
|
||||||
@root.style.opacity = 0
|
|
||||||
|
|
||||||
@stream-state-ev.on \connected ~>
|
|
||||||
@state = @get-stream-state!
|
|
||||||
@update!
|
|
||||||
set-timeout ~>
|
|
||||||
Velocity @root, {
|
|
||||||
opacity: 0
|
|
||||||
} 200ms \linear
|
|
||||||
, 1000ms
|
|
||||||
|
|
||||||
@stream-state-ev.on \closed ~>
|
|
||||||
@state = @get-stream-state!
|
|
||||||
@update!
|
|
||||||
Velocity @root, {
|
|
||||||
opacity: 1
|
|
||||||
} 0ms
|
|
||||||
</script>
|
|
||||||
</mk-stream-indicator>
|
|
|
@ -1,5 +1,11 @@
|
||||||
<mk-sub-post-content>
|
<mk-sub-post-content>
|
||||||
<div class="body"><a class="reply" if={ post.reply_to_id }><i class="fa fa-reply"></i></a><span ref="text"></span><a class="quote" if={ post.repost_id } href={ '/post:' + post.repost_id }>RP: ...</a></div>
|
<div class="body">
|
||||||
|
<a class="reply" if={ post.reply_to_id }>
|
||||||
|
<i class="fa fa-reply"></i>
|
||||||
|
</a>
|
||||||
|
<span ref="text"></span>
|
||||||
|
<a class="quote" if={ post.repost_id } href={ '/post:' + post.repost_id }>RP: ...</a>
|
||||||
|
</div>
|
||||||
<details if={ post.media }>
|
<details if={ post.media }>
|
||||||
<summary>({ post.media.length }つのメディア)</summary>
|
<summary>({ post.media.length }つのメディア)</summary>
|
||||||
<mk-images-viewer images={ post.media }></mk-images-viewer>
|
<mk-images-viewer images={ post.media }></mk-images-viewer>
|
||||||
|
@ -28,18 +34,20 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \text
|
this.mixin('text');
|
||||||
@mixin \user-preview
|
this.mixin('user-preview');
|
||||||
|
|
||||||
@post = @opts.post
|
this.post = this.opts.post;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
if @post.text?
|
if (this.post.text) {
|
||||||
tokens = @analyze @post.text
|
const tokens = this.analyze(this.post.text);
|
||||||
@refs.text.innerHTML = @compile tokens, false
|
this.refs.text.innerHTML = this.compile(tokens, false);
|
||||||
|
|
||||||
@refs.text.children.for-each (e) ~>
|
this.refs.text.children.forEach(e => {
|
||||||
if e.tag-name == \MK-URL
|
if (e.tagName == 'MK-URL') riot.mount(e);
|
||||||
riot.mount e
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</mk-sub-post-content>
|
</mk-sub-post-content>
|
||||||
|
|
|
@ -8,15 +8,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
<script>
|
|
||||||
@mixin \date-stringify
|
|
||||||
@mixin \user-preview
|
|
||||||
|
|
||||||
@post = @opts.post
|
|
||||||
|
|
||||||
@title = @date-stringify @post.created_at
|
|
||||||
|
|
||||||
</script>
|
|
||||||
<style>
|
<style>
|
||||||
:scope
|
:scope
|
||||||
display block
|
display block
|
||||||
|
@ -97,4 +88,11 @@
|
||||||
font-size 80%
|
font-size 80%
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
<script>
|
||||||
|
this.mixin('date-stringify');
|
||||||
|
this.mixin('user-preview');
|
||||||
|
|
||||||
|
this.post = this.opts.post;
|
||||||
|
this.title = this.dateStringify(this.post.created_at);
|
||||||
|
</script>
|
||||||
</mk-timeline-post-sub>
|
</mk-timeline-post-sub>
|
||||||
|
|
|
@ -55,8 +55,13 @@
|
||||||
<button class={ liked: p.is_liked } onclick={ like } title="善哉"><i class="fa fa-thumbs-o-up"></i>
|
<button class={ liked: p.is_liked } onclick={ like } title="善哉"><i class="fa fa-thumbs-o-up"></i>
|
||||||
<p class="count" if={ p.likes_count > 0 }>{ p.likes_count }</p>
|
<p class="count" if={ p.likes_count > 0 }>{ p.likes_count }</p>
|
||||||
</button>
|
</button>
|
||||||
<button onclick={ NotImplementedException }><i class="fa fa-ellipsis-h"></i></button>
|
<button onclick={ NotImplementedException }>
|
||||||
<button onclick={ toggleDetail } title="詳細"><i class="fa fa-caret-down" if={ !isDetailOpened }></i><i class="fa fa-caret-up" if={ isDetailOpened }></i></button>
|
<i class="fa fa-ellipsis-h"></i>
|
||||||
|
</button>
|
||||||
|
<button onclick={ toggleDetail } title="詳細">
|
||||||
|
<i class="fa fa-caret-down" if={ !isDetailOpened }></i>
|
||||||
|
<i class="fa fa-caret-up" if={ isDetailOpened }></i>
|
||||||
|
</button>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
@ -312,96 +317,124 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \api
|
this.mixin('api');
|
||||||
@mixin \text
|
this.mixin('text');
|
||||||
@mixin \date-stringify
|
this.mixin('date-stringify');
|
||||||
@mixin \user-preview
|
this.mixin('user-preview');
|
||||||
@mixin \NotImplementedException
|
this.mixin('NotImplementedException');
|
||||||
|
|
||||||
@post = @opts.post
|
this.post = this.opts.post;
|
||||||
@is-repost = @post.repost? and !@post.text?
|
this.isRepost = this.post.repost && this.post.text == null;
|
||||||
@p = if @is-repost then @post.repost else @post
|
this.p = this.isRepost ? this.post.repost : this.post;
|
||||||
|
|
||||||
@title = @date-stringify @p.created_at
|
this.title = this.dateStringify(this.p.created_at);
|
||||||
|
|
||||||
@url = CONFIG.url + '/' + @p.user.username + '/' + @p.id
|
this.url = CONFIG.url + '/' + this.p.user.username + '/' + this.p.id;
|
||||||
@is-detail-opened = false
|
this.isDetailOpened = false;
|
||||||
|
|
||||||
@on \mount ~>
|
this.on('mount', () => {
|
||||||
if @p.text?
|
if (this.p.text) {
|
||||||
tokens = if @p._highlight?
|
const tokens = this.analyze(this.p.text);
|
||||||
then @analyze @p._highlight
|
|
||||||
else @analyze @p.text
|
|
||||||
|
|
||||||
@refs.text.innerHTML = @refs.text.innerHTML.replace '<p class="dummy"></p>' if @p._highlight?
|
this.refs.text.innerHTML = this.refs.text.innerHTML.replace('<p class="dummy"></p>', this.compile(tokens));
|
||||||
then @compile tokens, true, false
|
|
||||||
else @compile tokens
|
|
||||||
|
|
||||||
@refs.text.children.for-each (e) ~>
|
this.refs.text.children.forEach(e => {
|
||||||
if e.tag-name == \MK-URL
|
if (e.tagName == 'MK-URL') riot.mount(e);
|
||||||
riot.mount e
|
});
|
||||||
|
|
||||||
# URLをプレビュー
|
// URLをプレビュー
|
||||||
tokens
|
tokens
|
||||||
.filter (t) -> t.type == \link
|
.filter(t => t.type == 'link')
|
||||||
.map (t) ~>
|
.map(t => {
|
||||||
@preview = @refs.text.append-child document.create-element \mk-url-preview
|
riot.mount(this.refs.text.appendChild(document.createElement('mk-url-preview')), {
|
||||||
riot.mount @preview, do
|
url: t.content
|
||||||
url: t.content
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
@reply = ~>
|
this.reply = () => {
|
||||||
form = document.body.append-child document.create-element \mk-post-form-window
|
riot.mount(document.body.appendChild(document.createElement('mk-post-form-window')), {
|
||||||
riot.mount form, do
|
reply: this.p
|
||||||
reply: @p
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@repost = ~>
|
this.repost = () => {
|
||||||
form = document.body.append-child document.create-element \mk-repost-form-window
|
riot.mount(document.body.appendChild(document.createElement('mk-repost-form-window')), {
|
||||||
riot.mount form, do
|
post: this.p
|
||||||
post: @p
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@like = ~>
|
this.like = () => {
|
||||||
if @p.is_liked
|
if (this.p.is_liked) {
|
||||||
@api \posts/likes/delete do
|
this.api('posts/likes/delete', {
|
||||||
post_id: @p.id
|
post_id: this.p.id
|
||||||
.then ~>
|
}).then(() => {
|
||||||
@p.is_liked = false
|
this.p.is_liked = false;
|
||||||
@update!
|
this.update();
|
||||||
else
|
});
|
||||||
@api \posts/likes/create do
|
} else {
|
||||||
post_id: @p.id
|
this.api('posts/likes/create', {
|
||||||
.then ~>
|
post_id: this.p.id
|
||||||
@p.is_liked = true
|
}).then(() => {
|
||||||
@update!
|
this.p.is_liked = true;
|
||||||
|
this.update();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@toggle-detail = ~>
|
this.toggleDetail = () => {
|
||||||
@is-detail-opened = !@is-detail-opened
|
this.update({
|
||||||
@update!
|
isDetailOpened: !this.isDetailOpened
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@on-key-down = (e) ~>
|
this.onKeyDown = e => {
|
||||||
should-be-cancel = true
|
let shouldBeCancel = true;
|
||||||
switch
|
|
||||||
| e.which == 38 or e.which == 74 or (e.which == 9 and e.shift-key) => # ↑, j or Shift+Tab
|
|
||||||
focus @root, (e) -> e.previous-element-sibling
|
|
||||||
| e.which == 40 or e.which == 75 or e.which == 9 => # ↓, k or Tab
|
|
||||||
focus @root, (e) -> e.next-element-sibling
|
|
||||||
| e.which == 81 or e.which == 69 => # q or e
|
|
||||||
@repost!
|
|
||||||
| e.which == 70 or e.which == 76 => # f or l
|
|
||||||
@like!
|
|
||||||
| e.which == 82 => # r
|
|
||||||
@reply!
|
|
||||||
| _ =>
|
|
||||||
should-be-cancel = false
|
|
||||||
|
|
||||||
if should-be-cancel
|
switch (true) {
|
||||||
e.prevent-default!
|
case e.which == 38: // [↑]
|
||||||
|
case e.which == 74: // [j]
|
||||||
|
case e.which == 9 && e.shiftKey: // [Shift] + [Tab]
|
||||||
|
focus(this.root, e => e.previousElementSibling);
|
||||||
|
break;
|
||||||
|
|
||||||
function focus(el, fn)
|
case e.which == 40: // [↓]
|
||||||
target = fn el
|
case e.which == 75: // [k]
|
||||||
if target?
|
case e.which == 9: // [Tab]
|
||||||
if target.has-attribute \tabindex
|
focus(this.root, e => e.nextElementSibling);
|
||||||
target.focus!
|
break;
|
||||||
else
|
|
||||||
focus target, fn
|
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();
|
||||||
|
};
|
||||||
|
|
||||||
|
function focus(el, fn) {
|
||||||
|
const target = fn(el);
|
||||||
|
if (target) {
|
||||||
|
if (target.hasAttribute('tabindex')) {
|
||||||
|
target.focus();
|
||||||
|
} else {
|
||||||
|
focus(target, fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</mk-timeline-post>
|
</mk-timeline-post>
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
<mk-timeline-post post={ post }></mk-timeline-post>
|
<mk-timeline-post post={ post }></mk-timeline-post>
|
||||||
<p class="date" if={ i != posts.length - 1 && post._date != posts[i + 1]._date }><span><i class="fa fa-angle-up"></i>{ post._datetext }</span><span><i class="fa fa-angle-down"></i>{ posts[i + 1]._datetext }</span></p>
|
<p class="date" if={ i != posts.length - 1 && post._date != posts[i + 1]._date }><span><i class="fa fa-angle-up"></i>{ post._datetext }</span><span><i class="fa fa-angle-down"></i>{ posts[i + 1]._datetext }</span></p>
|
||||||
</virtual>
|
</virtual>
|
||||||
<footer data-yield="footer"><yield from="footer"/></footer>
|
<footer data-yield="footer">
|
||||||
|
<yield from="footer"/>
|
||||||
|
</footer>
|
||||||
<style>
|
<style>
|
||||||
:scope
|
:scope
|
||||||
display block
|
display block
|
||||||
|
@ -44,36 +46,47 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@posts = []
|
this.posts = [];
|
||||||
|
|
||||||
@set-posts = (posts) ~>
|
this.on('update', () => {
|
||||||
@posts = posts
|
this.posts.forEach(post => {
|
||||||
@update!
|
const date = new Date(post.created_at).getDate();
|
||||||
|
const month = new Date(post.created_at).getMonth() + 1;
|
||||||
|
post._date = date;
|
||||||
|
post._datetext = `${month}月 ${date}日`;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@prepend-posts = (posts) ~>
|
this.setPosts = posts => {
|
||||||
posts.for-each (post) ~>
|
this.update({
|
||||||
@posts.push post
|
posts: posts
|
||||||
@update!
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@add-post = (post) ~>
|
this.prependPosts = posts => {
|
||||||
@posts.unshift post
|
posts.forEach(post => {
|
||||||
@update!
|
this.posts.push(post);
|
||||||
|
this.update();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@clear = ~>
|
this.addPost = post => {
|
||||||
@posts = []
|
this.posts.unshift(post);
|
||||||
@update!
|
this.update();
|
||||||
|
};
|
||||||
|
|
||||||
@focus = ~>
|
this.tail = () => {
|
||||||
@root.children.0.focus!
|
return this.posts[this.posts.length - 1];
|
||||||
|
};
|
||||||
|
|
||||||
@on \update ~>
|
this.clear = () => {
|
||||||
@posts.for-each (post) ~>
|
this.posts = [];
|
||||||
date = (new Date post.created_at).get-date!
|
this.update();
|
||||||
month = (new Date post.created_at).get-month! + 1
|
};
|
||||||
post._date = date
|
|
||||||
post._datetext = month + '月 ' + date + '日'
|
this.focus = () => {
|
||||||
|
this.root.children[0].focus();
|
||||||
|
};
|
||||||
|
|
||||||
@tail = ~>
|
|
||||||
@posts[@posts.length - 1]
|
|
||||||
</script>
|
</script>
|
||||||
</mk-timeline>
|
</mk-timeline>
|
||||||
|
|
|
@ -159,54 +159,54 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
@mixin \i
|
const contains = require('../../common/scripts/contains');
|
||||||
@mixin \signout
|
|
||||||
|
|
||||||
@is-open = false
|
this.mixin('i');
|
||||||
|
this.mixin('signout');
|
||||||
|
|
||||||
@on \before-unmount ~>
|
this.isOpen = false;
|
||||||
@close!
|
|
||||||
|
|
||||||
@toggle = ~>
|
this.on('before-unmount', () => {
|
||||||
if @is-open
|
this.close();
|
||||||
@close!
|
});
|
||||||
else
|
|
||||||
@open!
|
|
||||||
|
|
||||||
@open = ~>
|
this.toggle = () => {
|
||||||
@is-open = true
|
this.isOpen ? this.close() : this.open();
|
||||||
@update!
|
};
|
||||||
all = document.query-selector-all 'body *'
|
|
||||||
Array.prototype.for-each.call all, (el) ~>
|
|
||||||
el.add-event-listener \mousedown @mousedown
|
|
||||||
|
|
||||||
@close = ~>
|
this.open = () => {
|
||||||
@is-open = false
|
this.update({
|
||||||
@update!
|
isOpen: true
|
||||||
all = document.query-selector-all 'body *'
|
});
|
||||||
Array.prototype.for-each.call all, (el) ~>
|
document.querySelectorAll('body *').forEach(el => {
|
||||||
el.remove-event-listener \mousedown @mousedown
|
el.addEventListener('mousedown', this.mousedown);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@mousedown = (e) ~>
|
this.close = () => {
|
||||||
e.prevent-default!
|
this.update({
|
||||||
if (!contains @root, e.target) and (@root != e.target)
|
isOpen: false
|
||||||
@close!
|
});
|
||||||
return false
|
document.querySelectorAll('body *').forEach(el => {
|
||||||
|
el.removeEventListener('mousedown', this.mousedown);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@drive = ~>
|
this.mousedown = e => {
|
||||||
@close!
|
e.preventDefault();
|
||||||
riot.mount document.body.append-child document.create-element \mk-drive-browser-window
|
if (!contains(this.root, e.target) && this.root != e.target) this.close();
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
@settings = ~>
|
this.drive = () => {
|
||||||
@close!
|
this.close();
|
||||||
riot.mount document.body.append-child document.create-element \mk-settings-window
|
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')));
|
||||||
|
};
|
||||||
|
|
||||||
function contains(parent, child)
|
|
||||||
node = child.parent-node
|
|
||||||
while node?
|
|
||||||
if node == parent
|
|
||||||
return true
|
|
||||||
node = node.parent-node
|
|
||||||
return false
|
|
||||||
</script>
|
</script>
|
||||||
</mk-ui-header-account>
|
</mk-ui-header-account>
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue