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