This commit is contained in:
syuilo 2018-02-18 12:35:18 +09:00
parent 61b95e0c26
commit 99b3499364
103 changed files with 878 additions and 790 deletions

View File

@ -62,7 +62,7 @@ export default Vue.extend({
send() { send() {
this.sending = true; this.sending = true;
this.$root.$data.os.api('messaging/messages/create', { (this as any).api('messaging/messages/create', {
user_id: this.user.id, user_id: this.user.id,
text: this.text text: this.text
}).then(message => { }).then(message => {

View File

@ -8,7 +8,7 @@
<p class="read" v-if="message.is_me && message.is_read">%i18n:common.tags.mk-messaging-message.is-read%</p> <p class="read" v-if="message.is_me && message.is_read">%i18n:common.tags.mk-messaging-message.is-read%</p>
<button class="delete-button" v-if="message.is_me" title="%i18n:common.delete%"><img src="/assets/desktop/messaging/delete.png" alt="Delete"/></button> <button class="delete-button" v-if="message.is_me" title="%i18n:common.delete%"><img src="/assets/desktop/messaging/delete.png" alt="Delete"/></button>
<div class="content" v-if="!message.is_deleted"> <div class="content" v-if="!message.is_deleted">
<mk-post-html v-if="message.ast" :ast="message.ast" :i="$root.$data.os.i"/> <mk-post-html v-if="message.ast" :ast="message.ast" :i="os.i"/>
<mk-url-preview v-for="url in urls" :url="url" :key="url"/> <mk-url-preview v-for="url in urls" :url="url" :key="url"/>
<div class="image" v-if="message.file"><img src={ message.file.url } alt="image" title={ message.file.name }/></div> <div class="image" v-if="message.file"><img src={ message.file.url } alt="image" title={ message.file.name }/></div>
</div> </div>
@ -30,7 +30,7 @@ export default Vue.extend({
props: ['message'], props: ['message'],
computed: { computed: {
isMe(): boolean { isMe(): boolean {
return this.message.user_id == this.$root.$data.os.i.id; return this.message.user_id == (this as any).os.i.id;
}, },
urls(): string[] { urls(): string[] {
if (this.message.ast) { if (this.message.ast) {

View File

@ -48,7 +48,7 @@ export default Vue.extend({
}, },
mounted() { mounted() {
this.connection = new MessagingStreamConnection(this.$root.$data.os.i, this.user.id); this.connection = new MessagingStreamConnection((this as any).os.i, this.user.id);
this.connection.on('message', this.onMessage); this.connection.on('message', this.onMessage);
this.connection.on('read', this.onRead); this.connection.on('read', this.onRead);
@ -72,7 +72,7 @@ export default Vue.extend({
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const max = this.existMoreMessages ? 20 : 10; const max = this.existMoreMessages ? 20 : 10;
this.$root.$data.os.api('messaging/messages', { (this as any).api('messaging/messages', {
user_id: this.user.id, user_id: this.user.id,
limit: max + 1, limit: max + 1,
until_id: this.existMoreMessages ? this.messages[0].id : undefined until_id: this.existMoreMessages ? this.messages[0].id : undefined
@ -99,7 +99,7 @@ export default Vue.extend({
const isBottom = this.isBottom(); const isBottom = this.isBottom();
this.messages.push(message); this.messages.push(message);
if (message.user_id != this.$root.$data.os.i.id && !document.hidden) { if (message.user_id != (this as any).os.i.id && !document.hidden) {
this.connection.send({ this.connection.send({
type: 'read', type: 'read',
id: message.id id: message.id
@ -109,7 +109,7 @@ export default Vue.extend({
if (isBottom) { if (isBottom) {
// Scroll to bottom // Scroll to bottom
this.scrollToBottom(); this.scrollToBottom();
} else if (message.user_id != this.$root.$data.os.i.id) { } else if (message.user_id != (this as any).os.i.id) {
// Notify // Notify
this.notify('%i18n:common.tags.mk-messaging-room.new-message%'); this.notify('%i18n:common.tags.mk-messaging-room.new-message%');
} }
@ -157,7 +157,7 @@ export default Vue.extend({
onVisibilitychange() { onVisibilitychange() {
if (document.hidden) return; if (document.hidden) return;
this.messages.forEach(message => { this.messages.forEach(message => {
if (message.user_id !== this.$root.$data.os.i.id && !message.is_read) { if (message.user_id !== (this as any).os.i.id && !message.is_read) {
this.connection.send({ this.connection.send({
type: 'read', type: 'read',
id: message.id id: message.id

View File

@ -71,13 +71,13 @@ export default Vue.extend({
}; };
}, },
mounted() { mounted() {
this.connection = this.$root.$data.os.streams.messagingIndexStream.getConnection(); this.connection = (this as any).os.streams.messagingIndexStream.getConnection();
this.connectionId = this.$root.$data.os.streams.messagingIndexStream.use(); this.connectionId = (this as any).os.streams.messagingIndexStream.use();
this.connection.on('message', this.onMessage); this.connection.on('message', this.onMessage);
this.connection.on('read', this.onRead); this.connection.on('read', this.onRead);
this.$root.$data.os.api('messaging/history').then(messages => { (this as any).api('messaging/history').then(messages => {
this.fetching = false; this.fetching = false;
this.messages = messages; this.messages = messages;
}); });
@ -85,11 +85,11 @@ export default Vue.extend({
beforeDestroy() { beforeDestroy() {
this.connection.off('message', this.onMessage); this.connection.off('message', this.onMessage);
this.connection.off('read', this.onRead); this.connection.off('read', this.onRead);
this.$root.$data.os.stream.dispose(this.connectionId); (this as any).os.stream.dispose(this.connectionId);
}, },
methods: { methods: {
isMe(message) { isMe(message) {
return message.user_id == this.$root.$data.os.i.id; return message.user_id == (this as any).os.i.id;
}, },
onMessage(message) { onMessage(message) {
this.messages = this.messages.filter(m => !( this.messages = this.messages.filter(m => !(
@ -109,7 +109,7 @@ export default Vue.extend({
this.result = []; this.result = [];
return; return;
} }
this.$root.$data.os.api('users/search', { (this as any).api('users/search', {
query: this.q, query: this.q,
max: 5 max: 5
}).then(users => { }).then(users => {

View File

@ -47,7 +47,7 @@
}, },
vote(id) { vote(id) {
if (this.poll.choices.some(c => c.is_voted)) return; if (this.poll.choices.some(c => c.is_voted)) return;
this.$root.$data.os.api('posts/polls/vote', { (this as any).api('posts/polls/vote', {
post_id: this.post.id, post_id: this.post.id,
choice: id choice: id
}).then(() => { }).then(() => {

View File

@ -48,7 +48,7 @@ export default Vue.extend({
}, },
methods: { methods: {
pin() { pin() {
this.$root.$data.os.api('i/pin', { (this as any).api('i/pin', {
post_id: this.post.id post_id: this.post.id
}).then(() => { }).then(() => {
this.$destroy(); this.$destroy();

View File

@ -68,7 +68,7 @@ export default Vue.extend({
}, },
methods: { methods: {
react(reaction) { react(reaction) {
this.$root.$data.os.api('posts/reactions/create', { (this as any).api('posts/reactions/create', {
post_id: this.post.id, post_id: this.post.id,
reaction: reaction reaction: reaction
}).then(() => { }).then(() => {

View File

@ -28,7 +28,7 @@ export default Vue.extend({
}, },
methods: { methods: {
onUsernameChange() { onUsernameChange() {
this.$root.$data.os.api('users/show', { (this as any).api('users/show', {
username: this.username username: this.username
}).then(user => { }).then(user => {
this.user = user; this.user = user;
@ -37,7 +37,7 @@ export default Vue.extend({
onSubmit() { onSubmit() {
this.signing = true; this.signing = true;
this.$root.$data.os.api('signin', { (this as any).api('signin', {
username: this.username, username: this.username,
password: this.password, password: this.password,
token: this.user && this.user.two_factor_enabled ? this.token : undefined token: this.user && this.user.two_factor_enabled ? this.token : undefined

View File

@ -88,7 +88,7 @@ export default Vue.extend({
this.usernameState = 'wait'; this.usernameState = 'wait';
this.$root.$data.os.api('username/available', { (this as any).api('username/available', {
username: this.username username: this.username
}).then(result => { }).then(result => {
this.usernameState = result.available ? 'ok' : 'unavailable'; this.usernameState = result.available ? 'ok' : 'unavailable';
@ -115,12 +115,12 @@ export default Vue.extend({
this.passwordRetypeState = this.password == this.retypedPassword ? 'match' : 'not-match'; this.passwordRetypeState = this.password == this.retypedPassword ? 'match' : 'not-match';
}, },
onSubmit() { onSubmit() {
this.$root.$data.os.api('signup', { (this as any).api('signup', {
username: this.username, username: this.username,
password: this.password, password: this.password,
'g-recaptcha-response': (window as any).grecaptcha.getResponse() 'g-recaptcha-response': (window as any).grecaptcha.getResponse()
}).then(() => { }).then(() => {
this.$root.$data.os.api('signin', { (this as any).api('signin', {
username: this.username, username: this.username,
password: this.password password: this.password
}).then(() => { }).then(() => {

View File

@ -26,10 +26,10 @@ export default Vue.extend({
}; };
}, },
created() { created() {
this.stream = this.$root.$data.os.stream.borrow(); this.stream = (this as any).os.stream.borrow();
this.$root.$data.os.stream.on('connected', this.onConnected); (this as any).os.stream.on('connected', this.onConnected);
this.$root.$data.os.stream.on('disconnected', this.onDisconnected); (this as any).os.stream.on('disconnected', this.onDisconnected);
this.$nextTick(() => { this.$nextTick(() => {
if (this.stream.state == 'connected') { if (this.stream.state == 'connected') {
@ -38,12 +38,12 @@ export default Vue.extend({
}); });
}, },
beforeDestroy() { beforeDestroy() {
this.$root.$data.os.stream.off('connected', this.onConnected); (this as any).os.stream.off('connected', this.onConnected);
this.$root.$data.os.stream.off('disconnected', this.onDisconnected); (this as any).os.stream.off('disconnected', this.onDisconnected);
}, },
methods: { methods: {
onConnected() { onConnected() {
this.stream = this.$root.$data.os.stream.borrow(); this.stream = (this as any).os.stream.borrow();
setTimeout(() => { setTimeout(() => {
anime({ anime({

View File

@ -50,7 +50,7 @@ export default Vue.extend({
reader.readAsDataURL(file); reader.readAsDataURL(file);
const data = new FormData(); const data = new FormData();
data.append('i', this.$root.$data.os.i.token); data.append('i', (this as any).os.i.token);
data.append('file', file); data.append('file', file);
if (folder) data.append('folder_id', folder); if (folder) data.append('folder_id', folder);

View File

@ -26,12 +26,12 @@ export default define({
}; };
}, },
mounted() { mounted() {
this.connection = this.$root.$data.os.stream.getConnection(); this.connection = (this as any).os.stream.getConnection();
this.connectionId = this.$root.$data.os.stream.use(); this.connectionId = (this as any).os.stream.use();
this.connection.on('drive_file_created', this.onDriveFileCreated); this.connection.on('drive_file_created', this.onDriveFileCreated);
this.$root.$data.os.api('drive/stream', { (this as any).api('drive/stream', {
type: 'image/*', type: 'image/*',
limit: 9 limit: 9
}).then(images => { }).then(images => {
@ -41,7 +41,7 @@ export default define({
}, },
beforeDestroy() { beforeDestroy() {
this.connection.off('drive_file_created', this.onDriveFileCreated); this.connection.off('drive_file_created', this.onDriveFileCreated);
this.$root.$data.os.stream.dispose(this.connectionId); (this as any).os.stream.dispose(this.connectionId);
}, },
methods: { methods: {
onDriveFileCreated(file) { onDriveFileCreated(file) {

View File

@ -89,7 +89,7 @@ export default define({
fetch() { fetch() {
this.fetching = true; this.fetching = true;
this.$root.$data.os.api('drive/files', { (this as any).api('drive/files', {
folder_id: this.props.folder, folder_id: this.props.folder,
type: 'image/*', type: 'image/*',
limit: 100 limit: 100
@ -102,7 +102,7 @@ export default define({
}); });
}, },
choose() { choose() {
this.$root.$data.api.chooseDriveFolder().then(folder => { (this as any).apis.chooseDriveFolder().then(folder => {
this.props.folder = folder ? folder.id : null; this.props.folder = folder ? folder.id : null;
this.fetch(); this.fetch();
}); });

View File

@ -1,60 +0,0 @@
<mk-drive-browser-window>
<mk-window ref="window" is-modal={ false } width={ '800px' } height={ '500px' } popout={ popout }>
<yield to="header">
<p class="info" v-if="parent.usage"><b>{ parent.usage.toFixed(1) }%</b> %i18n:desktop.tags.mk-drive-browser-window.used%</p>
%fa:cloud%%i18n:desktop.tags.mk-drive-browser-window.drive%
</yield>
<yield to="content">
<mk-drive-browser multiple={ true } folder={ parent.folder } ref="browser"/>
</yield>
</mk-window>
<style lang="stylus" scoped>
:scope
> mk-window
[data-yield='header']
> .info
position absolute
top 0
left 16px
margin 0
font-size 80%
> [data-fa]
margin-right 4px
[data-yield='content']
> mk-drive-browser
height 100%
</style>
<script lang="typescript">
this.mixin('api');
this.folder = this.opts.folder ? this.opts.folder : null;
this.popout = () => {
const folder = this.$refs.window.refs.browser.folder;
if (folder) {
return `${_URL_}/i/drive/folder/${folder.id}`;
} else {
return `${_URL_}/i/drive`;
}
};
this.on('mount', () => {
this.$refs.window.on('closed', () => {
this.$destroy();
});
this.$root.$data.os.api('drive').then(info => {
this.update({
usage: info.usage / info.capacity * 100
});
});
});
this.close = () => {
this.$refs.window.close();
};
</script>
</mk-drive-browser-window>

View File

@ -1,99 +0,0 @@
<mk-drive-browser-file-contextmenu>
<mk-contextmenu ref="ctx">
<ul>
<li @click="parent.rename">
<p>%fa:i-cursor%%i18n:desktop.tags.mk-drive-browser-file-contextmenu.rename%</p>
</li>
<li @click="parent.copyUrl">
<p>%fa:link%%i18n:desktop.tags.mk-drive-browser-file-contextmenu.copy-url%</p>
</li>
<li><a href={ parent.file.url + '?download' } download={ parent.file.name } @click="parent.download">%fa:download%%i18n:desktop.tags.mk-drive-browser-file-contextmenu.download%</a></li>
<li class="separator"></li>
<li @click="parent.delete">
<p>%fa:R trash-alt%%i18n:common.delete%</p>
</li>
<li class="separator"></li>
<li class="has-child">
<p>%i18n:desktop.tags.mk-drive-browser-file-contextmenu.else-files%%fa:caret-right%</p>
<ul>
<li @click="parent.setAvatar">
<p>%i18n:desktop.tags.mk-drive-browser-file-contextmenu.set-as-avatar%</p>
</li>
<li @click="parent.setBanner">
<p>%i18n:desktop.tags.mk-drive-browser-file-contextmenu.set-as-banner%</p>
</li>
</ul>
</li>
<li class="has-child">
<p>%i18n:desktop.tags.mk-drive-browser-file-contextmenu.open-in-app%...%fa:caret-right%</p>
<ul>
<li @click="parent.addApp">
<p>%i18n:desktop.tags.mk-drive-browser-file-contextmenu.add-app%...</p>
</li>
</ul>
</li>
</ul>
</mk-contextmenu>
<script lang="typescript">
import copyToClipboard from '../../../common/scripts/copy-to-clipboard';
import dialog from '../../scripts/dialog';
import inputDialog from '../../scripts/input-dialog';
import updateAvatar from '../../scripts/update-avatar';
import NotImplementedException from '../../scripts/not-implemented-exception';
this.mixin('i');
this.mixin('api');
this.browser = this.opts.browser;
this.file = this.opts.file;
this.on('mount', () => {
this.$refs.ctx.on('closed', () => {
this.$emit('closed');
this.$destroy();
});
});
this.open = pos => {
this.$refs.ctx.open(pos);
};
this.rename = () => {
this.$refs.ctx.close();
inputDialog('%i18n:desktop.tags.mk-drive-browser-file-contextmenu.rename-file%', '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.input-new-file-name%', this.file.name, name => {
this.$root.$data.os.api('drive/files/update', {
file_id: this.file.id,
name: name
})
});
};
this.copyUrl = () => {
copyToClipboard(this.file.url);
this.$refs.ctx.close();
dialog('%fa:check%%i18n:desktop.tags.mk-drive-browser-file-contextmenu.copied%',
'%i18n:desktop.tags.mk-drive-browser-file-contextmenu.copied-url-to-clipboard%', [{
text: '%i18n:common.ok%'
}]);
};
this.download = () => {
this.$refs.ctx.close();
};
this.setAvatar = () => {
this.$refs.ctx.close();
updateAvatar(this.I, null, this.file);
};
this.setBanner = () => {
this.$refs.ctx.close();
updateBanner(this.I, null, this.file);
};
this.addApp = () => {
NotImplementedException();
};
</script>
</mk-drive-browser-file-contextmenu>

View File

@ -1,63 +0,0 @@
<mk-drive-browser-folder-contextmenu>
<mk-contextmenu ref="ctx">
<ul>
<li @click="parent.move">
<p>%fa:arrow-right%%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.move-to-this-folder%</p>
</li>
<li @click="parent.newWindow">
<p>%fa:R window-restore%%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.show-in-new-window%</p>
</li>
<li class="separator"></li>
<li @click="parent.rename">
<p>%fa:i-cursor%%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.rename%</p>
</li>
<li class="separator"></li>
<li @click="parent.delete">
<p>%fa:R trash-alt%%i18n:common.delete%</p>
</li>
</ul>
</mk-contextmenu>
<script lang="typescript">
import inputDialog from '../../scripts/input-dialog';
this.mixin('api');
this.browser = this.opts.browser;
this.folder = this.opts.folder;
this.open = pos => {
this.$refs.ctx.open(pos);
this.$refs.ctx.on('closed', () => {
this.$emit('closed');
this.$destroy();
});
};
this.move = () => {
this.browser.move(this.folder.id);
this.$refs.ctx.close();
};
this.newWindow = () => {
this.browser.newWindow(this.folder.id);
this.$refs.ctx.close();
};
this.createFolder = () => {
this.browser.createFolder();
this.$refs.ctx.close();
};
this.rename = () => {
this.$refs.ctx.close();
inputDialog('%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.rename-folder%', '%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.input-new-folder-name%', this.folder.name, name => {
this.$root.$data.os.api('drive/folders/update', {
folder_id: this.folder.id,
name: name
});
});
};
</script>
</mk-drive-browser-folder-contextmenu>

View File

@ -0,0 +1,18 @@
import MkChooseFileFromDriveWindow from '../views/components/choose-file-from-drive-window.vue';
export default function(opts) {
return new Promise((res, rej) => {
const o = opts || {};
const w = new MkChooseFileFromDriveWindow({
propsData: {
title: o.title,
multiple: o.multiple,
initFolder: o.currentFolder
}
}).$mount();
w.$once('selected', file => {
res(file);
});
document.body.appendChild(w.$el);
});
}

View File

@ -1,12 +1,12 @@
import MkChooseFolderFromDriveWindow from '../../../common/views/components/choose-folder-from-drive-window.vue'; import MkChooseFolderFromDriveWindow from '../views/components/choose-folder-from-drive-window.vue';
export default function(this: any, opts) { export default function(opts) {
return new Promise((res, rej) => { return new Promise((res, rej) => {
const o = opts || {}; const o = opts || {};
const w = new MkChooseFolderFromDriveWindow({ const w = new MkChooseFolderFromDriveWindow({
parent: this,
propsData: { propsData: {
title: o.title title: o.title,
initFolder: o.currentFolder
} }
}).$mount(); }).$mount();
w.$once('selected', folder => { w.$once('selected', folder => {

View File

@ -0,0 +1,16 @@
import Ctx from '../views/components/context-menu.vue';
export default function(e, menu, opts?) {
const o = opts || {};
const vm = new Ctx({
propsData: {
menu,
x: e.pageX - window.pageXOffset,
y: e.pageY - window.pageYOffset,
}
}).$mount();
vm.$once('closed', () => {
if (o.closed) o.closed();
});
document.body.appendChild(vm.$el);
}

View File

@ -0,0 +1,19 @@
import Dialog from '../views/components/dialog.vue';
export default function(opts) {
return new Promise<string>((res, rej) => {
const o = opts || {};
const d = new Dialog({
propsData: {
title: o.title,
text: o.text,
modal: o.modal,
buttons: o.actions
}
}).$mount();
d.$once('clicked', id => {
res(id);
});
document.body.appendChild(d.$el);
});
}

View File

@ -0,0 +1,19 @@
import InputDialog from '../views/components/input-dialog.vue';
export default function(opts) {
return new Promise<string>((res, rej) => {
const o = opts || {};
const d = new InputDialog({
propsData: {
title: o.title,
placeholder: o.placeholder,
default: o.default,
type: o.type || 'text'
}
}).$mount();
d.$once('done', text => {
res(text);
});
document.body.appendChild(d.$el);
});
}

View File

@ -11,6 +11,9 @@ import HomeStreamManager from '../common/scripts/streaming/home-stream-manager';
import composeNotification from '../common/scripts/compose-notification'; import composeNotification from '../common/scripts/compose-notification';
import chooseDriveFolder from './api/choose-drive-folder'; import chooseDriveFolder from './api/choose-drive-folder';
import chooseDriveFile from './api/choose-drive-file';
import dialog from './api/dialog';
import input from './api/input';
import MkIndex from './views/pages/index.vue'; import MkIndex from './views/pages/index.vue';
@ -30,7 +33,10 @@ init(async (launch) => {
require('./views/components'); require('./views/components');
const app = launch({ const app = launch({
chooseDriveFolder chooseDriveFolder,
chooseDriveFile,
dialog,
input
}); });
/** /**

View File

@ -36,7 +36,7 @@ export default Vue.extend({
methods: { methods: {
register() { register() {
passwordDialog('%i18n:desktop.tags.mk-2fa-setting.enter-password%', password => { passwordDialog('%i18n:desktop.tags.mk-2fa-setting.enter-password%', password => {
this.$root.$data.os.api('i/2fa/register', { (this as any).api('i/2fa/register', {
password: password password: password
}).then(data => { }).then(data => {
this.data = data; this.data = data;
@ -46,21 +46,21 @@ export default Vue.extend({
unregister() { unregister() {
passwordDialog('%i18n:desktop.tags.mk-2fa-setting.enter-password%', password => { passwordDialog('%i18n:desktop.tags.mk-2fa-setting.enter-password%', password => {
this.$root.$data.os.api('i/2fa/unregister', { (this as any).api('i/2fa/unregister', {
password: password password: password
}).then(() => { }).then(() => {
notify('%i18n:desktop.tags.mk-2fa-setting.unregistered%'); notify('%i18n:desktop.tags.mk-2fa-setting.unregistered%');
this.$root.$data.os.i.two_factor_enabled = false; (this as any).os.i.two_factor_enabled = false;
}); });
}); });
}, },
submit() { submit() {
this.$root.$data.os.api('i/2fa/done', { (this as any).api('i/2fa/done', {
token: this.token token: this.token
}).then(() => { }).then(() => {
notify('%i18n:desktop.tags.mk-2fa-setting.success%'); notify('%i18n:desktop.tags.mk-2fa-setting.success%');
this.$root.$data.os.i.two_factor_enabled = true; (this as any).os.i.two_factor_enabled = true;
}).catch(() => { }).catch(() => {
notify('%i18n:desktop.tags.mk-2fa-setting.failed%'); notify('%i18n:desktop.tags.mk-2fa-setting.failed%');
}); });

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="mk-api-setting"> <div class="mk-api-setting">
<p>Token: <code>{{ $root.$data.os.i.token }}</code></p> <p>Token: <code>{{ os.i.token }}</code></p>
<p>%i18n:desktop.tags.mk-api-info.intro%</p> <p>%i18n:desktop.tags.mk-api-info.intro%</p>
<div class="ui info warn"><p>%fa:exclamation-triangle%%i18n:desktop.tags.mk-api-info.caution%</p></div> <div class="ui info warn"><p>%fa:exclamation-triangle%%i18n:desktop.tags.mk-api-info.caution%</p></div>
<p>%i18n:desktop.tags.mk-api-info.regeneration-of-token%</p> <p>%i18n:desktop.tags.mk-api-info.regeneration-of-token%</p>
@ -16,7 +16,7 @@ export default Vue.extend({
methods: { methods: {
regenerateToken() { regenerateToken() {
passwordDialog('%i18n:desktop.tags.mk-api-info.enter-password%', password => { passwordDialog('%i18n:desktop.tags.mk-api-info.enter-password%', password => {
this.$root.$data.os.api('i/regenerate_token', { (this as any).api('i/regenerate_token', {
password: password password: password
}); });
}); });

View File

@ -0,0 +1,113 @@
<template>
<ul class="me-nu">
<li v-for="(item, i) in menu" :key="i" :class="item.type">
<template v-if="item.type == 'item'">
<p @click="click(item)"><span class="icon" v-if="item.icon" v-html="item.icon"></span>{{ item.text }}</p>
</template>
<template v-else-if="item.type == 'nest'">
<p><span class="icon" v-if="item.icon" v-html="item.icon"></span>{{ item.text }}...<span class="caret">%fa:caret-right%</span></p>
<me-nu :menu="item.menu" @x="click"/>
</template>
</li>
</ul>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
name: 'me-nu',
props: ['menu'],
methods: {
click(item) {
this.$emit('x', item);
}
}
});
</script>
<style lang="stylus" scoped>
.me-nu
$width = 240px
$item-height = 38px
$padding = 10px
ul
display block
margin 0
padding $padding 0
list-style none
li
display block
&:empty
margin-top $padding
padding-top $padding
border-top solid 1px #eee
&.nest
> p
cursor default
> .caret
> *
position absolute
top 0
right 8px
line-height $item-height
&:hover > ul
visibility visible
&:active
> p, a
background $theme-color
> p, a
display block
z-index 1
margin 0
padding 0 32px 0 38px
line-height $item-height
color #868C8C
text-decoration none
cursor pointer
&:hover
text-decoration none
*
pointer-events none
> .icon
> *
width 28px
margin-left -28px
text-align center
&:hover
> p, a
text-decoration none
background $theme-color
color $theme-color-foreground
&:active
> p, a
text-decoration none
background darken($theme-color, 10%)
color $theme-color-foreground
li > ul
visibility hidden
position absolute
top 0
left $width
margin-top -($padding)
width $width
background #fff
border-radius 0 4px 4px 4px
box-shadow 2px 2px 8px rgba(0, 0, 0, 0.2)
transition visibility 0s linear 0.2s
</style>

View File

@ -0,0 +1,74 @@
<template>
<div class="context-menu" :style="{ x: `${x}px`, y: `${y}px` }" @contextmenu.prevent="() => {}">
<me-nu :menu="menu" @x="click"/>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import * as anime from 'animejs';
import contains from '../../../common/scripts/contains';
import meNu from './context-menu-menu.vue';
export default Vue.extend({
components: {
'me-nu': meNu
},
props: ['x', 'y', 'menu'],
mounted() {
this.$nextTick(() => {
Array.from(document.querySelectorAll('body *')).forEach(el => {
el.addEventListener('mousedown', this.onMousedown);
});
this.$el.style.display = 'block';
anime({
targets: this.$el,
opacity: [0, 1],
duration: 100,
easing: 'linear'
});
});
},
methods: {
onMousedown(e) {
e.preventDefault();
if (!contains(this.$el, e.target) && (this.$el != e.target)) this.close();
return false;
},
click(item) {
if (item.onClick) item.onClick();
this.close();
},
close() {
Array.from(document.querySelectorAll('body *')).forEach(el => {
el.removeEventListener('mousedown', this.onMousedown);
});
this.$emit('closed');
this.$destroy();
}
}
});
</script>
<style lang="stylus" scoped>
.context-menu
$width = 240px
$item-height = 38px
$padding = 10px
display none
position fixed
top 0
left 0
z-index 4096
width $width
font-size 0.8em
background #fff
border-radius 0 4px 4px 4px
box-shadow 2px 2px 8px rgba(0, 0, 0, 0.2)
opacity 0
</style>

View File

@ -1,142 +0,0 @@
<template>
<div class="mk-contextmenu" @contextmenu.prevent="() => {}">
<slot></slot>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import * as anime from 'animejs';
import contains from '../../../common/scripts/contains';
export default Vue.extend({
props: ['x', 'y'],
mounted() {
document.querySelectorAll('body *').forEach(el => {
el.addEventListener('mousedown', this.onMousedown);
});
this.$el.style.display = 'block';
this.$el.style.left = this.x + 'px';
this.$el.style.top = this.y + 'px';
anime({
targets: this.$el,
opacity: [0, 1],
duration: 100,
easing: 'linear'
});
},
methods: {
onMousedown(e) {
e.preventDefault();
if (!contains(this.$el, e.target) && (this.$el != e.target)) this.close();
return false;
},
close() {
Array.from(document.querySelectorAll('body *')).forEach(el => {
el.removeEventListener('mousedown', this.onMousedown);
});
this.$emit('closed');
this.$destroy();
}
}
});
</script>
<style lang="stylus" scoped>
.mk-contextmenu
$width = 240px
$item-height = 38px
$padding = 10px
display none
position fixed
top 0
left 0
z-index 4096
width $width
font-size 0.8em
background #fff
border-radius 0 4px 4px 4px
box-shadow 2px 2px 8px rgba(0, 0, 0, 0.2)
opacity 0
ul
display block
margin 0
padding $padding 0
list-style none
li
display block
&.separator
margin-top $padding
padding-top $padding
border-top solid 1px #eee
&.has-child
> p
cursor default
> [data-fa]:last-child
position absolute
top 0
right 8px
line-height $item-height
&:hover > ul
visibility visible
&:active
> p, a
background $theme-color
> p, a
display block
z-index 1
margin 0
padding 0 32px 0 38px
line-height $item-height
color #868C8C
text-decoration none
cursor pointer
&:hover
text-decoration none
*
pointer-events none
> i
width 28px
margin-left -28px
text-align center
&:hover
> p, a
text-decoration none
background $theme-color
color $theme-color-foreground
&:active
> p, a
text-decoration none
background darken($theme-color, 10%)
color $theme-color-foreground
li > ul
visibility hidden
position absolute
top 0
left $width
margin-top -($padding)
width $width
background #fff
border-radius 0 4px 4px 4px
box-shadow 2px 2px 8px rgba(0, 0, 0, 0.2)
transition visibility 0s linear 0.2s
</style>

View File

@ -5,7 +5,7 @@
<header v-html="title"></header> <header v-html="title"></header>
<div class="body" v-html="text"></div> <div class="body" v-html="text"></div>
<div class="buttons"> <div class="buttons">
<button v-for="(button, i) in buttons" @click="click(button)" :key="i">{{ button.text }}</button> <button v-for="button in buttons" @click="click(button)" :key="button.id">{{ button.text }}</button>
</div> </div>
</div> </div>
</div> </div>
@ -26,13 +26,9 @@ export default Vue.extend({
buttons: { buttons: {
type: Array type: Array
}, },
canThrough: { modal: {
type: Boolean, type: Boolean,
default: true default: false
},
onThrough: {
type: Function,
required: false
} }
}, },
mounted() { mounted() {
@ -54,7 +50,7 @@ export default Vue.extend({
}, },
methods: { methods: {
click(button) { click(button) {
if (button.onClick) button.onClick(); this.$emit('clicked', button.id);
this.close(); this.close();
}, },
close() { close() {
@ -77,8 +73,7 @@ export default Vue.extend({
}); });
}, },
onBgClick() { onBgClick() {
if (this.canThrough) { if (!this.modal) {
if (this.onThrough) this.onThrough();
this.close(); this.close();
} }
} }

View File

@ -1,46 +0,0 @@
<template>
<mk-contextmenu ref="menu" @closed="onClosed">
<ul>
<li @click="createFolder">
<p>%fa:R folder%%i18n:desktop.tags.mk-drive-browser-base-contextmenu.create-folder%</p>
</li>
<li @click="upload">
<p>%fa:upload%%i18n:desktop.tags.mk-drive-browser-base-contextmenu.upload%</p>
</li>
<li @click="urlUpload">
<p>%fa:cloud-upload-alt%%i18n:desktop.tags.mk-drive-browser-base-contextmenu.url-upload%</p>
</li>
</ul>
</mk-contextmenu>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
props: ['browser'],
mounted() {
},
methods: {
close() {
(this.$refs.menu as any).close();
},
onClosed() {
this.$emit('closed');
this.$destroy();
},
createFolder() {
this.browser.createFolder();
this.close();
},
upload() {
this.browser.selectLocalFile();
this.close();
},
urlUpload() {
this.browser.urlUpload();
this.close();
}
}
});
</script>

View File

@ -3,24 +3,24 @@
:data-is-selected="isSelected" :data-is-selected="isSelected"
:data-is-contextmenu-showing="isContextmenuShowing" :data-is-contextmenu-showing="isContextmenuShowing"
@click="onClick" @click="onClick"
@contextmenu.prevent.stop="onContextmenu"
draggable="true" draggable="true"
@dragstart="onDragstart" @dragstart="onDragstart"
@dragend="onDragend" @dragend="onDragend"
@contextmenu.prevent.stop="onContextmenu"
:title="title" :title="title"
> >
<div class="label" v-if="I.avatar_id == file.id"><img src="/assets/label.svg"/> <div class="label" v-if="os.i.avatar_id == file.id"><img src="/assets/label.svg"/>
<p>%i18n:desktop.tags.mk-drive-browser-file.avatar%</p> <p>%i18n:desktop.tags.mk-drive-browser-file.avatar%</p>
</div> </div>
<div class="label" v-if="I.banner_id == file.id"><img src="/assets/label.svg"/> <div class="label" v-if="os.i.banner_id == file.id"><img src="/assets/label.svg"/>
<p>%i18n:desktop.tags.mk-drive-browser-file.banner%</p> <p>%i18n:desktop.tags.mk-drive-browser-file.banner%</p>
</div> </div>
<div class="thumbnail" ref="thumbnail" style="background-color:{ file.properties.average_color ? 'rgb(' + file.properties.average_color.join(',') + ')' : 'transparent' }"> <div class="thumbnail" ref="thumbnail" :style="`background-color: ${ background }`">
<img src={ file.url + '?thumbnail&size=128' } alt="" @load="onThumbnailLoaded"/> <img :src="`${file.url}?thumbnail&size=128`" alt="" @load="onThumbnailLoaded"/>
</div> </div>
<p class="name"> <p class="name">
<span>{ file.name.lastIndexOf('.') != -1 ? file.name.substr(0, file.name.lastIndexOf('.')) : file.name }</span> <span>{{ file.name.lastIndexOf('.') != -1 ? file.name.substr(0, file.name.lastIndexOf('.')) : file.name }}</span>
<span class="ext" v-if="file.name.lastIndexOf('.') != -1">{ file.name.substr(file.name.lastIndexOf('.')) }</span> <span class="ext" v-if="file.name.lastIndexOf('.') != -1">{{ file.name.substr(file.name.lastIndexOf('.')) }}</span>
</p> </p>
</div> </div>
</template> </template>
@ -28,10 +28,12 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import * as anime from 'animejs'; import * as anime from 'animejs';
import contextmenu from '../../api/contextmenu';
import copyToClipboard from '../../../common/scripts/copy-to-clipboard';
import bytesToSize from '../../../common/scripts/bytes-to-size'; import bytesToSize from '../../../common/scripts/bytes-to-size';
export default Vue.extend({ export default Vue.extend({
props: ['file', 'browser'], props: ['file'],
data() { data() {
return { return {
isContextmenuShowing: false, isContextmenuShowing: false,
@ -39,11 +41,19 @@ export default Vue.extend({
}; };
}, },
computed: { computed: {
browser(): any {
return this.$parent;
},
isSelected(): boolean { isSelected(): boolean {
return this.browser.selectedFiles.some(f => f.id == this.file.id); return this.browser.selectedFiles.some(f => f.id == this.file.id);
}, },
title(): string { title(): string {
return `${this.file.name}\n${this.file.type} ${bytesToSize(this.file.datasize)}`; return `${this.file.name}\n${this.file.type} ${bytesToSize(this.file.datasize)}`;
},
background(): string {
return this.file.properties.average_color
? `rgb(${this.file.properties.average_color.join(',')})'`
: 'transparent';
} }
}, },
methods: { methods: {
@ -53,18 +63,55 @@ export default Vue.extend({
onContextmenu(e) { onContextmenu(e) {
this.isContextmenuShowing = true; this.isContextmenuShowing = true;
const ctx = new MkDriveFileContextmenu({ contextmenu(e, [{
parent: this, type: 'item',
propsData: { text: '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.rename%',
browser: this.browser, icon: '%fa:i-cursor%',
x: e.pageX - window.pageXOffset, onClick: this.rename
y: e.pageY - window.pageYOffset }, {
type: 'item',
text: '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.copy-url%',
icon: '%fa:link%',
onClick: this.copyUrl
}, {
type: 'link',
href: `${this.file.url}?download`,
text: '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.download%',
icon: '%fa:download%',
}, {
type: 'divider',
}, {
type: 'item',
text: '%i18n:common.delete%',
icon: '%fa:R trash-alt%',
onClick: this.deleteFile
}, {
type: 'divider',
}, {
type: 'nest',
text: '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.else-files%',
menu: [{
type: 'item',
text: '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.set-as-avatar%',
onClick: this.setAsAvatar
}, {
type: 'item',
text: '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.set-as-banner%',
onClick: this.setAsBanner
}]
}, {
type: 'nest',
text: '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.open-in-app%',
menu: [{
type: 'item',
text: '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.add-app%...',
onClick: this.addApp
}]
}], {
closed: () => {
this.isContextmenuShowing = false;
} }
}).$mount();
ctx.$once('closed', () => {
this.isContextmenuShowing = false;
}); });
document.body.appendChild(ctx.$el);
}, },
onDragstart(e) { onDragstart(e) {
@ -95,6 +142,46 @@ export default Vue.extend({
easing: 'linear' easing: 'linear'
}); });
} }
},
rename() {
(this as any).apis.input({
title: '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.rename-file%',
placeholder: '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.input-new-file-name%',
default: this.file.name
}).then(name => {
(this as any).api('drive/files/update', {
file_id: this.file.id,
name: name
})
});
},
copyUrl() {
copyToClipboard(this.file.url);
(this as any).apis.dialog({
title: '%fa:check%%i18n:desktop.tags.mk-drive-browser-file-contextmenu.copied%',
text: '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.copied-url-to-clipboard%',
actions: [{
text: '%i18n:common.ok%'
}]
});
},
setAsAvatar() {
(this as any).apis.updateAvatar(this.file);
},
setAsBanner() {
(this as any).apis.updateBanner(this.file);
},
addApp() {
alert('not implemented yet');
},
deleteFile() {
alert('not implemented yet');
} }
} }
}); });

View File

@ -9,10 +9,10 @@
@dragenter.prevent="onDragenter" @dragenter.prevent="onDragenter"
@dragleave="onDragleave" @dragleave="onDragleave"
@drop.prevent.stop="onDrop" @drop.prevent.stop="onDrop"
@contextmenu.prevent.stop="onContextmenu"
draggable="true" draggable="true"
@dragstart="onDragstart" @dragstart="onDragstart"
@dragend="onDragend" @dragend="onDragend"
@contextmenu.prevent.stop="onContextmenu"
:title="title" :title="title"
> >
<p class="name"> <p class="name">
@ -25,10 +25,10 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import dialog from '../../scripts/dialog'; import contextmenu from '../../api/contextmenu';
export default Vue.extend({ export default Vue.extend({
props: ['folder', 'browser'], props: ['folder'],
data() { data() {
return { return {
hover: false, hover: false,
@ -38,6 +38,9 @@ export default Vue.extend({
}; };
}, },
computed: { computed: {
browser(): any {
return this.$parent;
},
title(): string { title(): string {
return this.folder.name; return this.folder.name;
} }
@ -47,6 +50,39 @@ export default Vue.extend({
this.browser.move(this.folder); this.browser.move(this.folder);
}, },
onContextmenu(e) {
this.isContextmenuShowing = true;
contextmenu(e, [{
type: 'item',
text: '%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.move-to-this-folder%',
icon: '%fa:arrow-right%',
onClick: this.go
}, {
type: 'item',
text: '%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.show-in-new-window%',
icon: '%fa:R window-restore%',
onClick: this.newWindow
}, {
type: 'divider',
}, {
type: 'item',
text: '%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.rename%',
icon: '%fa:i-cursor%',
onClick: this.rename
}, {
type: 'divider',
}, {
type: 'item',
text: '%i18n:common.delete%',
icon: '%fa:R trash-alt%',
onClick: this.deleteFolder
}], {
closed: () => {
this.isContextmenuShowing = false;
}
});
},
onMouseover() { onMouseover() {
this.hover = true; this.hover = true;
}, },
@ -102,7 +138,7 @@ export default Vue.extend({
if (obj.type == 'file') { if (obj.type == 'file') {
const file = obj.id; const file = obj.id;
this.browser.removeFile(file); this.browser.removeFile(file);
this.$root.$data.os.api('drive/files/update', { (this as any).api('drive/files/update', {
file_id: file, file_id: file,
folder_id: this.folder.id folder_id: this.folder.id
}); });
@ -112,7 +148,7 @@ export default Vue.extend({
// reject // reject
if (folder == this.folder.id) return false; if (folder == this.folder.id) return false;
this.browser.removeFolder(folder); this.browser.removeFolder(folder);
this.$root.$data.os.api('drive/folders/update', { (this as any).api('drive/folders/update', {
folder_id: folder, folder_id: folder,
parent_id: this.folder.id parent_id: this.folder.id
}).then(() => { }).then(() => {
@ -120,10 +156,13 @@ export default Vue.extend({
}).catch(err => { }).catch(err => {
switch (err) { switch (err) {
case 'detected-circular-definition': case 'detected-circular-definition':
dialog('%fa:exclamation-triangle%%i18n:desktop.tags.mk-drive-browser-folder.unable-to-process%', (this as any).apis.dialog({
'%i18n:desktop.tags.mk-drive-browser-folder.circular-reference-detected%', [{ title: '%fa:exclamation-triangle%%i18n:desktop.tags.mk-drive-browser-folder.unable-to-process%',
text: '%i18n:common.ok%' text: '%i18n:desktop.tags.mk-drive-browser-folder.circular-reference-detected%',
}]); actions: [{
text: '%i18n:common.ok%'
}]
});
break; break;
default: default:
alert('%i18n:desktop.tags.mk-drive-browser-folder.unhandled-error% ' + err); alert('%i18n:desktop.tags.mk-drive-browser-folder.unhandled-error% ' + err);
@ -152,21 +191,29 @@ export default Vue.extend({
this.browser.isDragSource = false; this.browser.isDragSource = false;
}, },
onContextmenu(e) { go() {
this.isContextmenuShowing = true; this.browser.move(this.folder.id);
const ctx = new MkDriveFolderContextmenu({ },
parent: this,
propsData: { newWindow() {
browser: this.browser, this.browser.newWindow(this.folder.id);
x: e.pageX - window.pageXOffset, },
y: e.pageY - window.pageYOffset
} rename() {
}).$mount(); (this as any).apis.input({
ctx.$once('closed', () => { title: '%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.rename-folder%',
this.isContextmenuShowing = false; placeholder: '%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.input-new-folder-name%',
default: this.folder.name
}).then(name => {
(this as any).api('drive/folders/update', {
folder_id: this.folder.id,
name: name
});
}); });
document.body.appendChild(ctx.$el); },
return false;
deleteFolder() {
alert('not implemented yet');
} }
} }
}); });

View File

@ -73,7 +73,7 @@ export default Vue.extend({
if (obj.type == 'file') { if (obj.type == 'file') {
const file = obj.id; const file = obj.id;
this.browser.removeFile(file); this.browser.removeFile(file);
this.$root.$data.os.api('drive/files/update', { (this as any).api('drive/files/update', {
file_id: file, file_id: file,
folder_id: this.folder ? this.folder.id : null folder_id: this.folder ? this.folder.id : null
}); });
@ -83,7 +83,7 @@ export default Vue.extend({
// reject // reject
if (this.folder && folder == this.folder.id) return false; if (this.folder && folder == this.folder.id) return false;
this.browser.removeFolder(folder); this.browser.removeFolder(folder);
this.$root.$data.os.api('drive/folders/update', { (this as any).api('drive/folders/update', {
folder_id: folder, folder_id: folder,
parent_id: this.folder ? this.folder.id : null parent_id: this.folder ? this.folder.id : null
}); });

View File

@ -0,0 +1,53 @@
<template>
<mk-window ref="window" @closed="$destroy" width="800px" height="500px" :popout="popout">
<span slot="header" :class="$style.header">
<p class="info" v-if="usage" :class="$style.info"><b>{{ usage.toFixed(1) }}%</b> %i18n:desktop.tags.mk-drive-browser-window.used%</p>
%fa:cloud%%i18n:desktop.tags.mk-drive-browser-window.drive%
</span>
<mk-drive-browser multiple :folder="folder" ref="browser"/>
</mk-window>
</template>
<script lang="ts">
import Vue from 'vue';
import { url } from '../../../config';
export default Vue.extend({
props: ['folder'],
data() {
return {
usage: null
};
},
mounted() {
(this as any).api('drive').then(info => {
this.usage = info.usage / info.capacity * 100;
});
},
methods: {
popout() {
const folder = (this.$refs.browser as any).folder;
if (folder) {
return `${url}/i/drive/folder/${folder.id}`;
} else {
return `${url}/i/drive`;
}
}
}
});
</script>
<style lang="stylus" module>
.header
> [data-fa]
margin-right 4px
.info
position absolute
top 0
left 16px
margin 0
font-size 80%
</style>

View File

@ -2,17 +2,17 @@
<div class="mk-drive"> <div class="mk-drive">
<nav> <nav>
<div class="path" @contextmenu.prevent.stop="() => {}"> <div class="path" @contextmenu.prevent.stop="() => {}">
<mk-drive-browser-nav-folder :class="{ current: folder == null }" folder={ null }/> <mk-drive-nav-folder :class="{ current: folder == null }"/>
<template each={ folder in hierarchyFolders }> <template v-for="folder in hierarchyFolders">
<span class="separator">%fa:angle-right%</span> <span class="separator" :key="folder.id + '>'">%fa:angle-right%</span>
<mk-drive-browser-nav-folder folder={ folder }/> <mk-drive-nav-folder :folder="folder" :key="folder.id"/>
</template> </template>
<span class="separator" v-if="folder != null">%fa:angle-right%</span> <span class="separator" v-if="folder != null">%fa:angle-right%</span>
<span class="folder current" v-if="folder != null">{ folder.name }</span> <span class="folder current" v-if="folder != null">{{ folder.name }}</span>
</div> </div>
<input class="search" type="search" placeholder="&#xf002; %i18n:desktop.tags.mk-drive-browser.search%"/> <input class="search" type="search" placeholder="&#xf002; %i18n:desktop.tags.mk-drive-browser.search%"/>
</nav> </nav>
<div class="main { uploading: uploads.length > 0, fetching: fetching }" <div class="main" :class="{ uploading: uploadings.length > 0, fetching }"
ref="main" ref="main"
@mousedown="onMousedown" @mousedown="onMousedown"
@dragover.prevent.stop="onDragover" @dragover.prevent.stop="onDragover"
@ -24,19 +24,15 @@
<div class="selection" ref="selection"></div> <div class="selection" ref="selection"></div>
<div class="contents" ref="contents"> <div class="contents" ref="contents">
<div class="folders" ref="foldersContainer" v-if="folders.length > 0"> <div class="folders" ref="foldersContainer" v-if="folders.length > 0">
<template each={ folder in folders }> <mk-drive-folder v-for="folder in folders" :key="folder.id" class="folder" :folder="folder"/>
<mk-drive-browser-folder class="folder" folder={ folder }/>
</template>
<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid --> <!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
<div class="padding" each={ Array(10).fill(16) }></div> <div class="padding" v-for="n in 16" :key="n"></div>
<button v-if="moreFolders">%i18n:desktop.tags.mk-drive-browser.load-more%</button> <button v-if="moreFolders">%i18n:desktop.tags.mk-drive-browser.load-more%</button>
</div> </div>
<div class="files" ref="filesContainer" v-if="files.length > 0"> <div class="files" ref="filesContainer" v-if="files.length > 0">
<template each={ file in files }> <mk-drive-file v-for="file in files" :key="file.id" class="file" :file="file"/>
<mk-drive-browser-file class="file" file={ file }/>
</template>
<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid --> <!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
<div class="padding" each={ Array(10).fill(16) }></div> <div class="padding" v-for="n in 16" :key="n"></div>
<button v-if="moreFiles" @click="fetchMoreFiles">%i18n:desktop.tags.mk-drive-browser.load-more%</button> <button v-if="moreFiles" @click="fetchMoreFiles">%i18n:desktop.tags.mk-drive-browser.load-more%</button>
</div> </div>
<div class="empty" v-if="files.length == 0 && folders.length == 0 && !fetching"> <div class="empty" v-if="files.length == 0 && folders.length == 0 && !fetching">
@ -60,16 +56,18 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import MkDriveWindow from './drive-window.vue';
import contains from '../../../common/scripts/contains'; import contains from '../../../common/scripts/contains';
import dialog from '../../scripts/dialog'; import contextmenu from '../../api/contextmenu';
import inputDialog from '../../scripts/input-dialog';
export default Vue.extend({ export default Vue.extend({
props: { props: {
initFolder: { initFolder: {
type: Object,
required: false required: false
}, },
multiple: { multiple: {
type: Boolean,
default: false default: false
} }
}, },
@ -106,8 +104,8 @@ export default Vue.extend({
}; };
}, },
mounted() { mounted() {
this.connection = this.$root.$data.os.streams.driveStream.getConnection(); this.connection = (this as any).os.streams.driveStream.getConnection();
this.connectionId = this.$root.$data.os.streams.driveStream.use(); this.connectionId = (this as any).os.streams.driveStream.use();
this.connection.on('file_created', this.onStreamDriveFileCreated); this.connection.on('file_created', this.onStreamDriveFileCreated);
this.connection.on('file_updated', this.onStreamDriveFileUpdated); this.connection.on('file_updated', this.onStreamDriveFileUpdated);
@ -125,12 +123,32 @@ export default Vue.extend({
this.connection.off('file_updated', this.onStreamDriveFileUpdated); this.connection.off('file_updated', this.onStreamDriveFileUpdated);
this.connection.off('folder_created', this.onStreamDriveFolderCreated); this.connection.off('folder_created', this.onStreamDriveFolderCreated);
this.connection.off('folder_updated', this.onStreamDriveFolderUpdated); this.connection.off('folder_updated', this.onStreamDriveFolderUpdated);
this.$root.$data.os.streams.driveStream.dispose(this.connectionId); (this as any).os.streams.driveStream.dispose(this.connectionId);
}, },
methods: { methods: {
onContextmenu(e) {
contextmenu(e, [{
type: 'item',
text: '%i18n:desktop.tags.mk-drive-browser-base-contextmenu.create-folder%',
icon: '%fa:R folder%',
onClick: this.createFolder
}, {
type: 'item',
text: '%i18n:desktop.tags.mk-drive-browser-base-contextmenu.upload%',
icon: '%fa:upload%',
onClick: this.selectLocalFile
}, {
type: 'item',
text: '%i18n:desktop.tags.mk-drive-browser-base-contextmenu.url-upload%',
icon: '%fa:cloud-upload-alt%',
onClick: this.urlUpload
}]);
},
onStreamDriveFileCreated(file) { onStreamDriveFileCreated(file) {
this.addFile(file, true); this.addFile(file, true);
}, },
onStreamDriveFileUpdated(file) { onStreamDriveFileUpdated(file) {
const current = this.folder ? this.folder.id : null; const current = this.folder ? this.folder.id : null;
if (current != file.folder_id) { if (current != file.folder_id) {
@ -139,9 +157,11 @@ export default Vue.extend({
this.addFile(file, true); this.addFile(file, true);
} }
}, },
onStreamDriveFolderCreated(folder) { onStreamDriveFolderCreated(folder) {
this.addFolder(folder, true); this.addFolder(folder, true);
}, },
onStreamDriveFolderUpdated(folder) { onStreamDriveFolderUpdated(folder) {
const current = this.folder ? this.folder.id : null; const current = this.folder ? this.folder.id : null;
if (current != folder.parent_id) { if (current != folder.parent_id) {
@ -150,12 +170,15 @@ export default Vue.extend({
this.addFolder(folder, true); this.addFolder(folder, true);
} }
}, },
onChangeUploaderUploads(uploads) { onChangeUploaderUploads(uploads) {
this.uploadings = uploads; this.uploadings = uploads;
}, },
onUploaderUploaded(file) { onUploaderUploaded(file) {
this.addFile(file, true); this.addFile(file, true);
}, },
onMousedown(e): any { onMousedown(e): any {
if (contains(this.$refs.foldersContainer, e.target) || contains(this.$refs.filesContainer, e.target)) return true; if (contains(this.$refs.foldersContainer, e.target) || contains(this.$refs.filesContainer, e.target)) return true;
@ -202,6 +225,7 @@ export default Vue.extend({
document.documentElement.addEventListener('mousemove', move); document.documentElement.addEventListener('mousemove', move);
document.documentElement.addEventListener('mouseup', up); document.documentElement.addEventListener('mouseup', up);
}, },
onDragover(e): any { onDragover(e): any {
// //
if (!this.isDragSource) { if (!this.isDragSource) {
@ -214,12 +238,15 @@ export default Vue.extend({
return false; return false;
} }
}, },
onDragenter(e) { onDragenter(e) {
if (!this.isDragSource) this.draghover = true; if (!this.isDragSource) this.draghover = true;
}, },
onDragleave(e) { onDragleave(e) {
this.draghover = false; this.draghover = false;
}, },
onDrop(e): any { onDrop(e): any {
this.draghover = false; this.draghover = false;
@ -244,7 +271,7 @@ export default Vue.extend({
const file = obj.id; const file = obj.id;
if (this.files.some(f => f.id == file)) return false; if (this.files.some(f => f.id == file)) return false;
this.removeFile(file); this.removeFile(file);
this.$root.$data.os.api('drive/files/update', { (this as any).api('drive/files/update', {
file_id: file, file_id: file,
folder_id: this.folder ? this.folder.id : null folder_id: this.folder ? this.folder.id : null
}); });
@ -255,7 +282,7 @@ export default Vue.extend({
if (this.folder && folder == this.folder.id) return false; if (this.folder && folder == this.folder.id) return false;
if (this.folders.some(f => f.id == folder)) return false; if (this.folders.some(f => f.id == folder)) return false;
this.removeFolder(folder); this.removeFolder(folder);
this.$root.$data.os.api('drive/folders/update', { (this as any).api('drive/folders/update', {
folder_id: folder, folder_id: folder,
parent_id: this.folder ? this.folder.id : null parent_id: this.folder ? this.folder.id : null
}).then(() => { }).then(() => {
@ -263,10 +290,13 @@ export default Vue.extend({
}).catch(err => { }).catch(err => {
switch (err) { switch (err) {
case 'detected-circular-definition': case 'detected-circular-definition':
dialog('%fa:exclamation-triangle%%i18n:desktop.tags.mk-drive-browser.unable-to-process%', (this as any).apis.dialog({
'%i18n:desktop.tags.mk-drive-browser.circular-reference-detected%', [{ title: '%fa:exclamation-triangle%%i18n:desktop.tags.mk-drive-browser.unable-to-process%',
text: '%i18n:common.ok%' text: '%i18n:desktop.tags.mk-drive-browser.circular-reference-detected%',
}]); actions: [{
text: '%i18n:common.ok%'
}]
});
break; break;
default: default:
alert('%i18n:desktop.tags.mk-drive-browser.unhandled-error% ' + err); alert('%i18n:desktop.tags.mk-drive-browser.unhandled-error% ' + err);
@ -276,40 +306,37 @@ export default Vue.extend({
return false; return false;
}, },
onContextmenu(e) {
document.body.appendChild(new MkDriveContextmenu({
propsData: {
browser: this,
x: e.pageX - window.pageXOffset,
y: e.pageY - window.pageYOffset
}
}).$mount().$el);
return false;
},
selectLocalFile() { selectLocalFile() {
(this.$refs.fileInput as any).click(); (this.$refs.fileInput as any).click();
}, },
urlUpload() {
inputDialog('%i18n:desktop.tags.mk-drive-browser.url-upload%',
'%i18n:desktop.tags.mk-drive-browser.url-of-file%', null, url => {
this.$root.$data.os.api('drive/files/upload_from_url', { urlUpload() {
(this as any).apis.input({
title: '%i18n:desktop.tags.mk-drive-browser.url-upload%',
placeholder: '%i18n:desktop.tags.mk-drive-browser.url-of-file%'
}).then(url => {
(this as any).api('drive/files/upload_from_url', {
url: url, url: url,
folder_id: this.folder ? this.folder.id : undefined folder_id: this.folder ? this.folder.id : undefined
}); });
dialog('%fa:check%%i18n:desktop.tags.mk-drive-browser.url-upload-requested%', (this as any).apis.dialog({
'%i18n:desktop.tags.mk-drive-browser.may-take-time%', [{ title: '%fa:check%%i18n:desktop.tags.mk-drive-browser.url-upload-requested%',
text: '%i18n:common.ok%' text: '%i18n:desktop.tags.mk-drive-browser.may-take-time%',
}]); actions: [{
text: '%i18n:common.ok%'
}]
});
}); });
}, },
createFolder() {
inputDialog('%i18n:desktop.tags.mk-drive-browser.create-folder%',
'%i18n:desktop.tags.mk-drive-browser.folder-name%', null, name => {
this.$root.$data.os.api('drive/folders/create', { createFolder() {
(this as any).apis.input({
title: '%i18n:desktop.tags.mk-drive-browser.create-folder%',
placeholder: '%i18n:desktop.tags.mk-drive-browser.folder-name%'
}).then(name => {
(this as any).api('drive/folders/create', {
name: name, name: name,
folder_id: this.folder ? this.folder.id : undefined folder_id: this.folder ? this.folder.id : undefined
}).then(folder => { }).then(folder => {
@ -317,15 +344,18 @@ export default Vue.extend({
}); });
}); });
}, },
onChangeFileInput() { onChangeFileInput() {
Array.from((this.$refs.fileInput as any).files).forEach(file => { Array.from((this.$refs.fileInput as any).files).forEach(file => {
this.upload(file, this.folder); this.upload(file, this.folder);
}); });
}, },
upload(file, folder) { upload(file, folder) {
if (folder && typeof folder == 'object') folder = folder.id; if (folder && typeof folder == 'object') folder = folder.id;
(this.$refs.uploader as any).upload(file, folder); (this.$refs.uploader as any).upload(file, folder);
}, },
chooseFile(file) { chooseFile(file) {
const isAlreadySelected = this.selectedFiles.some(f => f.id == file.id); const isAlreadySelected = this.selectedFiles.some(f => f.id == file.id);
if (this.multiple) { if (this.multiple) {
@ -344,6 +374,7 @@ export default Vue.extend({
} }
} }
}, },
newWindow(folderId) { newWindow(folderId) {
document.body.appendChild(new MkDriveWindow({ document.body.appendChild(new MkDriveWindow({
propsData: { propsData: {
@ -351,6 +382,7 @@ export default Vue.extend({
} }
}).$mount().$el); }).$mount().$el);
}, },
move(target) { move(target) {
if (target == null) { if (target == null) {
this.goRoot(); this.goRoot();
@ -361,7 +393,7 @@ export default Vue.extend({
this.fetching = true; this.fetching = true;
this.$root.$data.os.api('drive/folders/show', { (this as any).api('drive/folders/show', {
folder_id: target folder_id: target
}).then(folder => { }).then(folder => {
this.folder = folder; this.folder = folder;
@ -378,6 +410,7 @@ export default Vue.extend({
this.fetch(); this.fetch();
}); });
}, },
addFolder(folder, unshift = false) { addFolder(folder, unshift = false) {
const current = this.folder ? this.folder.id : null; const current = this.folder ? this.folder.id : null;
if (current != folder.parent_id) return; if (current != folder.parent_id) return;
@ -394,6 +427,7 @@ export default Vue.extend({
this.folders.push(folder); this.folders.push(folder);
} }
}, },
addFile(file, unshift = false) { addFile(file, unshift = false) {
const current = this.folder ? this.folder.id : null; const current = this.folder ? this.folder.id : null;
if (current != file.folder_id) return; if (current != file.folder_id) return;
@ -410,26 +444,33 @@ export default Vue.extend({
this.files.push(file); this.files.push(file);
} }
}, },
removeFolder(folder) { removeFolder(folder) {
if (typeof folder == 'object') folder = folder.id; if (typeof folder == 'object') folder = folder.id;
this.folders = this.folders.filter(f => f.id != folder); this.folders = this.folders.filter(f => f.id != folder);
}, },
removeFile(file) { removeFile(file) {
if (typeof file == 'object') file = file.id; if (typeof file == 'object') file = file.id;
this.files = this.files.filter(f => f.id != file); this.files = this.files.filter(f => f.id != file);
}, },
appendFile(file) { appendFile(file) {
this.addFile(file); this.addFile(file);
}, },
appendFolder(folder) { appendFolder(folder) {
this.addFolder(folder); this.addFolder(folder);
}, },
prependFile(file) { prependFile(file) {
this.addFile(file, true); this.addFile(file, true);
}, },
prependFolder(folder) { prependFolder(folder) {
this.addFolder(folder, true); this.addFolder(folder, true);
}, },
goRoot() { goRoot() {
// root // root
if (this.folder == null) return; if (this.folder == null) return;
@ -439,6 +480,7 @@ export default Vue.extend({
this.$emit('move-root'); this.$emit('move-root');
this.fetch(); this.fetch();
}, },
fetch() { fetch() {
this.folders = []; this.folders = [];
this.files = []; this.files = [];
@ -453,7 +495,7 @@ export default Vue.extend({
const filesMax = 30; const filesMax = 30;
// //
this.$root.$data.os.api('drive/folders', { (this as any).api('drive/folders', {
folder_id: this.folder ? this.folder.id : null, folder_id: this.folder ? this.folder.id : null,
limit: foldersMax + 1 limit: foldersMax + 1
}).then(folders => { }).then(folders => {
@ -466,7 +508,7 @@ export default Vue.extend({
}); });
// //
this.$root.$data.os.api('drive/files', { (this as any).api('drive/files', {
folder_id: this.folder ? this.folder.id : null, folder_id: this.folder ? this.folder.id : null,
limit: filesMax + 1 limit: filesMax + 1
}).then(files => { }).then(files => {
@ -489,13 +531,14 @@ export default Vue.extend({
} }
}; };
}, },
fetchMoreFiles() { fetchMoreFiles() {
this.fetching = true; this.fetching = true;
const max = 30; const max = 30;
// //
this.$root.$data.os.api('drive/files', { (this as any).api('drive/files', {
folder_id: this.folder ? this.folder.id : null, folder_id: this.folder ? this.folder.id : null,
limit: max + 1 limit: max + 1
}).then(files => { }).then(files => {

View File

@ -29,8 +29,8 @@ export default Vue.extend({
}; };
}, },
mounted() { mounted() {
this.connection = this.$root.$data.os.stream.getConnection(); this.connection = (this as any).os.stream.getConnection();
this.connectionId = this.$root.$data.os.stream.use(); this.connectionId = (this as any).os.stream.use();
this.connection.on('follow', this.onFollow); this.connection.on('follow', this.onFollow);
this.connection.on('unfollow', this.onUnfollow); this.connection.on('unfollow', this.onUnfollow);
@ -38,7 +38,7 @@ export default Vue.extend({
beforeDestroy() { beforeDestroy() {
this.connection.off('follow', this.onFollow); this.connection.off('follow', this.onFollow);
this.connection.off('unfollow', this.onUnfollow); this.connection.off('unfollow', this.onUnfollow);
this.$root.$data.os.stream.dispose(this.connectionId); (this as any).os.stream.dispose(this.connectionId);
}, },
methods: { methods: {
@ -57,7 +57,7 @@ export default Vue.extend({
onClick() { onClick() {
this.wait = true; this.wait = true;
if (this.user.is_following) { if (this.user.is_following) {
this.$root.$data.os.api('following/delete', { (this as any).api('following/delete', {
user_id: this.user.id user_id: this.user.id
}).then(() => { }).then(() => {
this.user.is_following = false; this.user.is_following = false;
@ -67,7 +67,7 @@ export default Vue.extend({
this.wait = false; this.wait = false;
}); });
} else { } else {
this.$root.$data.os.api('following/create', { (this as any).api('following/create', {
user_id: this.user.id user_id: this.user.id
}).then(() => { }).then(() => {
this.user.is_following = true; this.user.is_following = true;

View File

@ -39,7 +39,7 @@ export default Vue.extend({
this.fetching = true; this.fetching = true;
this.users = []; this.users = [];
this.$root.$data.os.api('users/recommendation', { (this as any).api('users/recommendation', {
limit: this.limit, limit: this.limit,
offset: this.limit * this.page offset: this.limit * this.page
}).then(users => { }).then(users => {

View File

@ -101,7 +101,7 @@ export default Vue.extend({
}, },
methods: { methods: {
bakeHomeData() { bakeHomeData() {
return JSON.stringify(this.$root.$data.os.i.client_settings.home); return JSON.stringify((this as any).os.i.client_settings.home);
}, },
onTlLoaded() { onTlLoaded() {
this.$emit('loaded'); this.$emit('loaded');
@ -123,7 +123,7 @@ export default Vue.extend({
data: {} data: {}
}; };
this.$root.$data.os.i.client_settings.home.unshift(widget); (this as any).os.i.client_settings.home.unshift(widget);
this.saveHome(); this.saveHome();
}, },
@ -132,48 +132,48 @@ export default Vue.extend({
Array.from((this.$refs.left as Element).children).forEach(el => { Array.from((this.$refs.left as Element).children).forEach(el => {
const id = el.getAttribute('data-widget-id'); const id = el.getAttribute('data-widget-id');
const widget = this.$root.$data.os.i.client_settings.home.find(w => w.id == id); const widget = (this as any).os.i.client_settings.home.find(w => w.id == id);
widget.place = 'left'; widget.place = 'left';
data.push(widget); data.push(widget);
}); });
Array.from((this.$refs.right as Element).children).forEach(el => { Array.from((this.$refs.right as Element).children).forEach(el => {
const id = el.getAttribute('data-widget-id'); const id = el.getAttribute('data-widget-id');
const widget = this.$root.$data.os.i.client_settings.home.find(w => w.id == id); const widget = (this as any).os.i.client_settings.home.find(w => w.id == id);
widget.place = 'right'; widget.place = 'right';
data.push(widget); data.push(widget);
}); });
Array.from((this.$refs.maintop as Element).children).forEach(el => { Array.from((this.$refs.maintop as Element).children).forEach(el => {
const id = el.getAttribute('data-widget-id'); const id = el.getAttribute('data-widget-id');
const widget = this.$root.$data.os.i.client_settings.home.find(w => w.id == id); const widget = (this as any).os.i.client_settings.home.find(w => w.id == id);
widget.place = 'main'; widget.place = 'main';
data.push(widget); data.push(widget);
}); });
this.$root.$data.os.api('i/update_home', { (this as any).api('i/update_home', {
home: data home: data
}); });
} }
}, },
computed: { computed: {
leftWidgets(): any { leftWidgets(): any {
return this.$root.$data.os.i.client_settings.home.filter(w => w.place == 'left'); return (this as any).os.i.client_settings.home.filter(w => w.place == 'left');
}, },
centerWidgets(): any { centerWidgets(): any {
return this.$root.$data.os.i.client_settings.home.filter(w => w.place == 'center'); return (this as any).os.i.client_settings.home.filter(w => w.place == 'center');
}, },
rightWidgets(): any { rightWidgets(): any {
return this.$root.$data.os.i.client_settings.home.filter(w => w.place == 'right'); return (this as any).os.i.client_settings.home.filter(w => w.place == 'right');
} }
}, },
created() { created() {
this.bakedHomeData = this.bakeHomeData(); this.bakedHomeData = this.bakeHomeData();
}, },
mounted() { mounted() {
this.$root.$data.os.i.on('refreshed', this.onMeRefreshed); (this as any).os.i.on('refreshed', this.onMeRefreshed);
this.home = this.$root.$data.os.i.client_settings.home; this.home = (this as any).os.i.client_settings.home;
if (!this.customize) { if (!this.customize) {
if ((this.$refs.left as Element).children.length == 0) { if ((this.$refs.left as Element).children.length == 0) {
@ -214,14 +214,14 @@ export default Vue.extend({
const el = evt.item; const el = evt.item;
const id = el.getAttribute('data-widget-id'); const id = el.getAttribute('data-widget-id');
el.parentNode.removeChild(el); el.parentNode.removeChild(el);
this.$root.$data.os.i.client_settings.home = this.$root.$data.os.i.client_settings.home.filter(w => w.id != id); (this as any).os.i.client_settings.home = (this as any).os.i.client_settings.home.filter(w => w.id != id);
this.saveHome(); this.saveHome();
} }
})); }));
} }
}, },
beforeDestroy() { beforeDestroy() {
this.$root.$data.os.i.off('refreshed', this.onMeRefreshed); (this as any).os.i.off('refreshed', this.onMeRefreshed);
this.home.forEach(widget => { this.home.forEach(widget => {
widget.unmount(); widget.unmount();

View File

@ -27,6 +27,10 @@ import postForm from './post-form.vue';
import repostForm from './repost-form.vue'; import repostForm from './repost-form.vue';
import followButton from './follow-button.vue'; import followButton from './follow-button.vue';
import postPreview from './post-preview.vue'; import postPreview from './post-preview.vue';
import drive from './drive.vue';
import driveFile from './drive-file.vue';
import driveFolder from './drive-folder.vue';
import driveNavFolder from './drive-nav-folder.vue';
Vue.component('mk-ui', ui); Vue.component('mk-ui', ui);
Vue.component('mk-ui-header', uiHeader); Vue.component('mk-ui-header', uiHeader);
@ -55,3 +59,7 @@ Vue.component('mk-post-form', postForm);
Vue.component('mk-repost-form', repostForm); Vue.component('mk-repost-form', repostForm);
Vue.component('mk-follow-button', followButton); Vue.component('mk-follow-button', followButton);
Vue.component('mk-post-preview', postPreview); Vue.component('mk-post-preview', postPreview);
Vue.component('mk-drive', drive);
Vue.component('mk-drive-file', driveFile);
Vue.component('mk-drive-folder', driveFolder);
Vue.component('mk-drive-nav-folder', driveNavFolder);

View File

@ -33,12 +33,6 @@ export default Vue.extend({
}, },
type: { type: {
default: 'text' default: 'text'
},
onOk: {
type: Function
},
onCancel: {
type: Function
} }
}, },
data() { data() {
@ -63,9 +57,9 @@ export default Vue.extend({
}, },
beforeClose() { beforeClose() {
if (this.done) { if (this.done) {
this.onOk(this.text); this.$emit('done', this.text);
} else { } else {
if (this.onCancel) this.onCancel(); this.$emit('canceled');
} }
}, },
onKeydown(e) { onKeydown(e) {

View File

@ -11,7 +11,6 @@ export default Vue.extend({
methods: { methods: {
navigate(user) { navigate(user) {
document.body.appendChild(new MkMessagingRoomWindow({ document.body.appendChild(new MkMessagingRoomWindow({
parent: this,
propsData: { propsData: {
user: user user: user
} }

View File

@ -22,7 +22,7 @@ export default Vue.extend({
}; };
}, },
mounted() { mounted() {
this.$root.$data.os.api('mute/list').then(x => { (this as any).api('mute/list').then(x => {
this.fetching = false; this.fetching = false;
this.users = x.users; this.users = x.users;
}); });

View File

@ -128,14 +128,14 @@ export default Vue.extend({
} }
}, },
mounted() { mounted() {
this.connection = this.$root.$data.os.stream.getConnection(); this.connection = (this as any).os.stream.getConnection();
this.connectionId = this.$root.$data.os.stream.use(); this.connectionId = (this as any).os.stream.use();
this.connection.on('notification', this.onNotification); this.connection.on('notification', this.onNotification);
const max = 10; const max = 10;
this.$root.$data.os.api('i/notifications', { (this as any).api('i/notifications', {
limit: max + 1 limit: max + 1
}).then(notifications => { }).then(notifications => {
if (notifications.length == max + 1) { if (notifications.length == max + 1) {
@ -149,7 +149,7 @@ export default Vue.extend({
}, },
beforeDestroy() { beforeDestroy() {
this.connection.off('notification', this.onNotification); this.connection.off('notification', this.onNotification);
this.$root.$data.os.stream.dispose(this.connectionId); (this as any).os.stream.dispose(this.connectionId);
}, },
methods: { methods: {
fetchMoreNotifications() { fetchMoreNotifications() {
@ -157,7 +157,7 @@ export default Vue.extend({
const max = 30; const max = 30;
this.$root.$data.os.api('i/notifications', { (this as any).api('i/notifications', {
limit: max + 1, limit: max + 1,
until_id: this.notifications[this.notifications.length - 1].id until_id: this.notifications[this.notifications.length - 1].id
}).then(notifications => { }).then(notifications => {

View File

@ -22,7 +22,7 @@ export default Vue.extend({
}]); }]);
return; return;
} }
this.$root.$data.os.api('i/change_password', { (this as any).api('i/change_password', {
current_password: currentPassword, current_password: currentPassword,
new_password: newPassword new_password: newPassword
}).then(() => { }).then(() => {

View File

@ -16,7 +16,7 @@
</div> </div>
</header> </header>
<div class="body"> <div class="body">
<mk-post-html v-if="post.ast" :ast="post.ast" :i="$root.$data.os.i"/> <mk-post-html v-if="post.ast" :ast="post.ast" :i="os.i"/>
<div class="media" v-if="post.media"> <div class="media" v-if="post.media">
<mk-images images={ post.media }/> <mk-images images={ post.media }/>
</div> </div>

View File

@ -35,7 +35,7 @@
</a> </a>
</header> </header>
<div class="body"> <div class="body">
<mk-post-html v-if="p.ast" :ast="p.ast" :i="$root.$data.os.i"/> <mk-post-html v-if="p.ast" :ast="p.ast" :i="os.i"/>
<mk-url-preview v-for="url in urls" :url="url" :key="url"/> <mk-url-preview v-for="url in urls" :url="url" :key="url"/>
<div class="media" v-if="p.media"> <div class="media" v-if="p.media">
<mk-images images={ p.media }/> <mk-images images={ p.media }/>
@ -117,7 +117,7 @@ export default Vue.extend({
mounted() { mounted() {
// Get replies // Get replies
if (!this.compact) { if (!this.compact) {
this.$root.$data.os.api('posts/replies', { (this as any).api('posts/replies', {
post_id: this.p.id, post_id: this.p.id,
limit: 8 limit: 8
}).then(replies => { }).then(replies => {
@ -130,7 +130,7 @@ export default Vue.extend({
this.contextFetching = true; this.contextFetching = true;
// Fetch context // Fetch context
this.$root.$data.os.api('posts/context', { (this as any).api('posts/context', {
post_id: this.p.reply_id post_id: this.p.reply_id
}).then(context => { }).then(context => {
this.contextFetching = false; this.contextFetching = false;

View File

@ -113,18 +113,12 @@ export default Vue.extend({
chooseFile() { chooseFile() {
(this.$refs.file as any).click(); (this.$refs.file as any).click();
}, },
chooseFileFromDrive() {/* chooseFileFromDrive() {
const w = new MkDriveFileSelectorWindow({ (this as any).apis.chooseDriveFile({
propsData: { multiple: true
multiple: true }).then(files => {
}
}).$mount();
document.body.appendChild(w.$el);
w.$once('selected', files => {
files.forEach(this.attachMedia); files.forEach(this.attachMedia);
});*/ });
}, },
attachMedia(driveFile) { attachMedia(driveFile) {
this.files.push(driveFile); this.files.push(driveFile);
@ -196,7 +190,7 @@ export default Vue.extend({
post() { post() {
this.posting = true; this.posting = true;
this.$root.$data.os.api('posts/create', { (this as any).api('posts/create', {
text: this.text == '' ? undefined : this.text, text: this.text == '' ? undefined : this.text,
media_ids: this.files.length > 0 ? this.files.map(f => f.id) : undefined, media_ids: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
reply_id: this.reply ? this.reply.id : undefined, reply_id: this.reply ? this.reply.id : undefined,

View File

@ -32,7 +32,7 @@
<div class="text" ref="text"> <div class="text" ref="text">
<p class="channel" v-if="p.channel"><a :href="`${_CH_URL_}/${p.channel.id}`" target="_blank">{{ p.channel.title }}</a>:</p> <p class="channel" v-if="p.channel"><a :href="`${_CH_URL_}/${p.channel.id}`" target="_blank">{{ p.channel.title }}</a>:</p>
<a class="reply" v-if="p.reply">%fa:reply%</a> <a class="reply" v-if="p.reply">%fa:reply%</a>
<mk-post-html v-if="p.ast" :ast="p.ast" :i="$root.$data.os.i"/> <mk-post-html v-if="p.ast" :ast="p.ast" :i="os.i"/>
<a class="quote" v-if="p.repost">RP:</a> <a class="quote" v-if="p.repost">RP:</a>
<mk-url-preview v-for="url in urls" :url="url" :key="url"/> <mk-url-preview v-for="url in urls" :url="url" :key="url"/>
</div> </div>
@ -133,24 +133,24 @@ export default Vue.extend({
} }
}, },
created() { created() {
this.connection = this.$root.$data.os.stream.getConnection(); this.connection = (this as any).os.stream.getConnection();
this.connectionId = this.$root.$data.os.stream.use(); this.connectionId = (this as any).os.stream.use();
}, },
mounted() { mounted() {
this.capture(true); this.capture(true);
if (this.$root.$data.os.isSignedIn) { if ((this as any).os.isSignedIn) {
this.connection.on('_connected_', this.onStreamConnected); this.connection.on('_connected_', this.onStreamConnected);
} }
}, },
beforeDestroy() { beforeDestroy() {
this.decapture(true); this.decapture(true);
this.connection.off('_connected_', this.onStreamConnected); this.connection.off('_connected_', this.onStreamConnected);
this.$root.$data.os.stream.dispose(this.connectionId); (this as any).os.stream.dispose(this.connectionId);
}, },
methods: { methods: {
capture(withHandler = false) { capture(withHandler = false) {
if (this.$root.$data.os.isSignedIn) { if ((this as any).os.isSignedIn) {
this.connection.send({ this.connection.send({
type: 'capture', type: 'capture',
id: this.post.id id: this.post.id
@ -159,7 +159,7 @@ export default Vue.extend({
} }
}, },
decapture(withHandler = false) { decapture(withHandler = false) {
if (this.$root.$data.os.isSignedIn) { if ((this as any).os.isSignedIn) {
this.connection.send({ this.connection.send({
type: 'decapture', type: 'decapture',
id: this.post.id id: this.post.id
@ -178,7 +178,7 @@ export default Vue.extend({
}, },
reply() { reply() {
document.body.appendChild(new MkPostFormWindow({ document.body.appendChild(new MkPostFormWindow({
parent: this,
propsData: { propsData: {
reply: this.p reply: this.p
} }
@ -186,7 +186,7 @@ export default Vue.extend({
}, },
repost() { repost() {
document.body.appendChild(new MkRepostFormWindow({ document.body.appendChild(new MkRepostFormWindow({
parent: this,
propsData: { propsData: {
post: this.p post: this.p
} }
@ -194,7 +194,7 @@ export default Vue.extend({
}, },
react() { react() {
document.body.appendChild(new MkReactionPicker({ document.body.appendChild(new MkReactionPicker({
parent: this,
propsData: { propsData: {
source: this.$refs.reactButton, source: this.$refs.reactButton,
post: this.p post: this.p
@ -203,7 +203,7 @@ export default Vue.extend({
}, },
menu() { menu() {
document.body.appendChild(new MkPostMenu({ document.body.appendChild(new MkPostMenu({
parent: this,
propsData: { propsData: {
source: this.$refs.menuButton, source: this.$refs.menuButton,
post: this.p post: this.p

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="mk-profile-setting"> <div class="mk-profile-setting">
<label class="avatar ui from group"> <label class="avatar ui from group">
<p>%i18n:desktop.tags.mk-profile-setting.avatar%</p><img class="avatar" :src="`${$root.$data.os.i.avatar_url}?thumbnail&size=64`" alt="avatar"/> <p>%i18n:desktop.tags.mk-profile-setting.avatar%</p><img class="avatar" :src="`${os.i.avatar_url}?thumbnail&size=64`" alt="avatar"/>
<button class="ui" @click="updateAvatar">%i18n:desktop.tags.mk-profile-setting.choice-avatar%</button> <button class="ui" @click="updateAvatar">%i18n:desktop.tags.mk-profile-setting.choice-avatar%</button>
</label> </label>
<label class="ui from group"> <label class="ui from group">
@ -32,18 +32,18 @@ import notify from '../../scripts/notify';
export default Vue.extend({ export default Vue.extend({
data() { data() {
return { return {
name: this.$root.$data.os.i.name, name: (this as any).os.i.name,
location: this.$root.$data.os.i.location, location: (this as any).os.i.location,
description: this.$root.$data.os.i.description, description: (this as any).os.i.description,
birthday: this.$root.$data.os.i.birthday, birthday: (this as any).os.i.birthday,
}; };
}, },
methods: { methods: {
updateAvatar() { updateAvatar() {
updateAvatar(this.$root.$data.os.i); updateAvatar((this as any).os.i);
}, },
save() { save() {
this.$root.$data.os.api('i/update', { (this as any).api('i/update', {
name: this.name, name: this.name,
location: this.location || null, location: this.location || null,
description: this.description || null, description: this.description || null,

View File

@ -29,7 +29,7 @@ export default Vue.extend({
methods: { methods: {
ok() { ok() {
this.wait = true; this.wait = true;
this.$root.$data.os.api('posts/create', { (this as any).api('posts/create', {
repost_id: this.post.id repost_id: this.post.id
}).then(data => { }).then(data => {
this.$emit('posted'); this.$emit('posted');

View File

@ -2,7 +2,7 @@
<div class="mk-sub-post-content"> <div class="mk-sub-post-content">
<div class="body"> <div class="body">
<a class="reply" v-if="post.reply_id">%fa:reply%</a> <a class="reply" v-if="post.reply_id">%fa:reply%</a>
<mk-post-html :ast="post.ast" :i="$root.$data.os.i"/> <mk-post-html :ast="post.ast" :i="os.i"/>
<a class="quote" v-if="post.repost_id" :href="`/post:${post.repost_id}`">RP: ...</a> <a class="quote" v-if="post.repost_id" :href="`/post:${post.repost_id}`">RP: ...</a>
<mk-url-preview v-for="url in urls" :url="url" :key="url"/> <mk-url-preview v-for="url in urls" :url="url" :key="url"/>
</div> </div>

View File

@ -30,12 +30,12 @@ export default Vue.extend({
}, },
computed: { computed: {
alone(): boolean { alone(): boolean {
return this.$root.$data.os.i.following_count == 0; return (this as any).os.i.following_count == 0;
} }
}, },
mounted() { mounted() {
this.connection = this.$root.$data.os.stream.getConnection(); this.connection = (this as any).os.stream.getConnection();
this.connectionId = this.$root.$data.os.stream.use(); this.connectionId = (this as any).os.stream.use();
this.connection.on('post', this.onPost); this.connection.on('post', this.onPost);
this.connection.on('follow', this.onChangeFollowing); this.connection.on('follow', this.onChangeFollowing);
@ -50,7 +50,7 @@ export default Vue.extend({
this.connection.off('post', this.onPost); this.connection.off('post', this.onPost);
this.connection.off('follow', this.onChangeFollowing); this.connection.off('follow', this.onChangeFollowing);
this.connection.off('unfollow', this.onChangeFollowing); this.connection.off('unfollow', this.onChangeFollowing);
this.$root.$data.os.stream.dispose(this.connectionId); (this as any).os.stream.dispose(this.connectionId);
document.removeEventListener('keydown', this.onKeydown); document.removeEventListener('keydown', this.onKeydown);
window.removeEventListener('scroll', this.onScroll); window.removeEventListener('scroll', this.onScroll);
@ -59,7 +59,7 @@ export default Vue.extend({
fetch(cb?) { fetch(cb?) {
this.fetching = true; this.fetching = true;
this.$root.$data.os.api('posts/timeline', { (this as any).api('posts/timeline', {
until_date: this.date ? (this.date as any).getTime() : undefined until_date: this.date ? (this.date as any).getTime() : undefined
}).then(posts => { }).then(posts => {
this.fetching = false; this.fetching = false;
@ -70,7 +70,7 @@ export default Vue.extend({
more() { more() {
if (this.moreFetching || this.fetching || this.posts.length == 0) return; if (this.moreFetching || this.fetching || this.posts.length == 0) return;
this.moreFetching = true; this.moreFetching = true;
this.$root.$data.os.api('posts/timeline', { (this as any).api('posts/timeline', {
until_id: this.posts[this.posts.length - 1].id until_id: this.posts[this.posts.length - 1].id
}).then(posts => { }).then(posts => {
this.moreFetching = false; this.moreFetching = false;

View File

@ -1,13 +1,13 @@
<template> <template>
<div class="mk-ui-header-account"> <div class="mk-ui-header-account">
<button class="header" :data-active="isOpen" @click="toggle"> <button class="header" :data-active="isOpen" @click="toggle">
<span class="username">{{ $root.$data.os.i.username }}<template v-if="!isOpen">%fa:angle-down%</template><template v-if="isOpen">%fa:angle-up%</template></span> <span class="username">{{ os.i.username }}<template v-if="!isOpen">%fa:angle-down%</template><template v-if="isOpen">%fa:angle-up%</template></span>
<img class="avatar" :src="`${ $root.$data.os.i.avatar_url }?thumbnail&size=64`" alt="avatar"/> <img class="avatar" :src="`${ os.i.avatar_url }?thumbnail&size=64`" alt="avatar"/>
</button> </button>
<div class="menu" v-if="isOpen"> <div class="menu" v-if="isOpen">
<ul> <ul>
<li> <li>
<a :href="`/${ $root.$data.os.i.username }`">%fa:user%%i18n:desktop.tags.mk-ui-header-account.profile%%fa:angle-right%</a> <a :href="`/${ os.i.username }`">%fa:user%%i18n:desktop.tags.mk-ui-header-account.profile%%fa:angle-right%</a>
</li> </li>
<li @click="drive"> <li @click="drive">
<p>%fa:cloud%%i18n:desktop.tags.mk-ui-header-account.drive%%fa:angle-right%</p> <p>%fa:cloud%%i18n:desktop.tags.mk-ui-header-account.drive%%fa:angle-right%</p>

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="mk-ui-header-nav"> <div class="mk-ui-header-nav">
<ul> <ul>
<template v-if="$root.$data.os.isSignedIn"> <template v-if="os.isSignedIn">
<li class="home" :class="{ active: page == 'home' }"> <li class="home" :class="{ active: page == 'home' }">
<a href="/"> <a href="/">
%fa:home% %fa:home%
@ -44,15 +44,15 @@ export default Vue.extend({
}; };
}, },
mounted() { mounted() {
if (this.$root.$data.os.isSignedIn) { if ((this as any).os.isSignedIn) {
this.connection = this.$root.$data.os.stream.getConnection(); this.connection = (this as any).os.stream.getConnection();
this.connectionId = this.$root.$data.os.stream.use(); this.connectionId = (this as any).os.stream.use();
this.connection.on('read_all_messaging_messages', this.onReadAllMessagingMessages); this.connection.on('read_all_messaging_messages', this.onReadAllMessagingMessages);
this.connection.on('unread_messaging_message', this.onUnreadMessagingMessage); this.connection.on('unread_messaging_message', this.onUnreadMessagingMessage);
// Fetch count of unread messaging messages // Fetch count of unread messaging messages
this.$root.$data.os.api('messaging/unread').then(res => { (this as any).api('messaging/unread').then(res => {
if (res.count > 0) { if (res.count > 0) {
this.hasUnreadMessagingMessages = true; this.hasUnreadMessagingMessages = true;
} }
@ -60,10 +60,10 @@ export default Vue.extend({
} }
}, },
beforeDestroy() { beforeDestroy() {
if (this.$root.$data.os.isSignedIn) { if ((this as any).os.isSignedIn) {
this.connection.off('read_all_messaging_messages', this.onReadAllMessagingMessages); this.connection.off('read_all_messaging_messages', this.onReadAllMessagingMessages);
this.connection.off('unread_messaging_message', this.onUnreadMessagingMessage); this.connection.off('unread_messaging_message', this.onUnreadMessagingMessage);
this.$root.$data.os.stream.dispose(this.connectionId); (this as any).os.stream.dispose(this.connectionId);
} }
}, },
methods: { methods: {

View File

@ -23,15 +23,15 @@ export default Vue.extend({
}; };
}, },
mounted() { mounted() {
if (this.$root.$data.os.isSignedIn) { if ((this as any).os.isSignedIn) {
this.connection = this.$root.$data.os.stream.getConnection(); this.connection = (this as any).os.stream.getConnection();
this.connectionId = this.$root.$data.os.stream.use(); this.connectionId = (this as any).os.stream.use();
this.connection.on('read_all_notifications', this.onReadAllNotifications); this.connection.on('read_all_notifications', this.onReadAllNotifications);
this.connection.on('unread_notification', this.onUnreadNotification); this.connection.on('unread_notification', this.onUnreadNotification);
// Fetch count of unread notifications // Fetch count of unread notifications
this.$root.$data.os.api('notifications/get_unread_count').then(res => { (this as any).api('notifications/get_unread_count').then(res => {
if (res.count > 0) { if (res.count > 0) {
this.hasUnreadNotifications = true; this.hasUnreadNotifications = true;
} }
@ -39,10 +39,10 @@ export default Vue.extend({
} }
}, },
beforeDestroy() { beforeDestroy() {
if (this.$root.$data.os.isSignedIn) { if ((this as any).os.isSignedIn) {
this.connection.off('read_all_notifications', this.onReadAllNotifications); this.connection.off('read_all_notifications', this.onReadAllNotifications);
this.connection.off('unread_notification', this.onUnreadNotification); this.connection.off('unread_notification', this.onUnreadNotification);
this.$root.$data.os.stream.dispose(this.connectionId); (this as any).os.stream.dispose(this.connectionId);
} }
}, },
methods: { methods: {

View File

@ -10,9 +10,9 @@
</div> </div>
<div class="right"> <div class="right">
<mk-ui-header-search/> <mk-ui-header-search/>
<mk-ui-header-account v-if="$root.$data.os.isSignedIn"/> <mk-ui-header-account v-if="os.isSignedIn"/>
<mk-ui-header-notifications v-if="$root.$data.os.isSignedIn"/> <mk-ui-header-notifications v-if="os.isSignedIn"/>
<mk-ui-header-post-button v-if="$root.$data.os.isSignedIn"/> <mk-ui-header-post-button v-if="os.isSignedIn"/>
<mk-ui-header-clock/> <mk-ui-header-clock/>
</div> </div>
</div> </div>

View File

@ -4,7 +4,7 @@
<div class="content"> <div class="content">
<slot></slot> <slot></slot>
</div> </div>
<mk-stream-indicator v-if="$root.$data.os.isSignedIn"/> <mk-stream-indicator v-if="os.isSignedIn"/>
</div> </div>
</template> </template>

View File

@ -14,7 +14,7 @@ export default Vue.extend({
props: ['user'], props: ['user'],
methods: { methods: {
fetch(iknow, limit, cursor, cb) { fetch(iknow, limit, cursor, cb) {
this.$root.$data.os.api('users/followers', { (this as any).api('users/followers', {
user_id: this.user.id, user_id: this.user.id,
iknow: iknow, iknow: iknow,
limit: limit, limit: limit,

View File

@ -14,7 +14,7 @@ export default Vue.extend({
props: ['user'], props: ['user'],
methods: { methods: {
fetch(iknow, limit, cursor, cb) { fetch(iknow, limit, cursor, cb) {
this.$root.$data.os.api('users/following', { (this as any).api('users/following', {
user_id: this.user.id, user_id: this.user.id,
iknow: iknow, iknow: iknow,
limit: limit, limit: limit,

View File

@ -21,7 +21,7 @@
<p>フォロワー</p><a>{{ u.followers_count }}</a> <p>フォロワー</p><a>{{ u.followers_count }}</a>
</div> </div>
</div> </div>
<mk-follow-button v-if="$root.$data.os.isSignedIn && user.id != $root.$data.os.i.id" :user="u"/> <mk-follow-button v-if="os.isSignedIn && user.id != os.i.id" :user="u"/>
</template> </template>
</div> </div>
</template> </template>
@ -49,7 +49,7 @@ export default Vue.extend({
this.open(); this.open();
}); });
} else { } else {
this.$root.$data.os.api('users/show', { (this as any).api('users/show', {
user_id: this.user[0] == '@' ? undefined : this.user, user_id: this.user[0] == '@' ? undefined : this.user,
username: this.user[0] == '@' ? this.user.substr(1) : undefined username: this.user[0] == '@' ? this.user.substr(1) : undefined
}).then(user => { }).then(user => {

View File

@ -60,7 +60,7 @@ export default Vue.extend({
} }
}, },
fetch(cb?) { fetch(cb?) {
this.$root.$data.os.api('users/posts', { (this as any).api('users/posts', {
user_id: this.user.id, user_id: this.user.id,
until_date: this.date ? this.date.getTime() : undefined, until_date: this.date ? this.date.getTime() : undefined,
with_replies: this.mode == 'with-replies' with_replies: this.mode == 'with-replies'
@ -73,7 +73,7 @@ export default Vue.extend({
more() { more() {
if (this.moreFetching || this.fetching || this.posts.length == 0) return; if (this.moreFetching || this.fetching || this.posts.length == 0) return;
this.moreFetching = true; this.moreFetching = true;
this.$root.$data.os.api('users/posts', { (this as any).api('users/posts', {
user_id: this.user.id, user_id: this.user.id,
with_replies: this.mode == 'with-replies', with_replies: this.mode == 'with-replies',
until_id: this.posts[this.posts.length - 1].id until_id: this.posts[this.posts.length - 1].id

View File

@ -3,7 +3,7 @@
<nav> <nav>
<div> <div>
<span :data-is-active="mode == 'all'" @click="mode = 'all'">すべて<span>{{ count }}</span></span> <span :data-is-active="mode == 'all'" @click="mode = 'all'">すべて<span>{{ count }}</span></span>
<span v-if="$root.$data.os.isSignedIn && youKnowCount" :data-is-active="mode == 'iknow'" @click="mode = 'iknow'">知り合い<span>{{ youKnowCount }}</span></span> <span v-if="os.isSignedIn && youKnowCount" :data-is-active="mode == 'iknow'" @click="mode = 'iknow'">知り合い<span>{{ youKnowCount }}</span></span>
</div> </div>
</nav> </nav>
<div class="users" v-if="!fetching && users.length != 0"> <div class="users" v-if="!fetching && users.length != 0">

View File

@ -80,7 +80,7 @@ export default Vue.extend({
created() { created() {
// //
this.$root.$data.os.windows.add(this); (this as any).os.windows.add(this);
}, },
mounted() { mounted() {
@ -97,7 +97,7 @@ export default Vue.extend({
destroyed() { destroyed() {
// //
this.$root.$data.os.windows.remove(this); (this as any).os.windows.remove(this);
window.removeEventListener('resize', this.onBrowserResize); window.removeEventListener('resize', this.onBrowserResize);
}, },
@ -191,7 +191,7 @@ export default Vue.extend({
top() { top() {
let z = 0; let z = 0;
this.$root.$data.os.windows.getAll().forEach(w => { (this as any).os.windows.getAll().forEach(w => {
if (w == this) return; if (w == this) return;
const m = w.$refs.main; const m = w.$refs.main;
const mz = Number(document.defaultView.getComputedStyle(m, null).zIndex); const mz = Number(document.defaultView.getComputedStyle(m, null).zIndex);

View File

@ -26,8 +26,8 @@ export default Vue.extend({
mounted() { mounted() {
document.title = 'Misskey'; document.title = 'Misskey';
this.connection = this.$root.$data.os.stream.getConnection(); this.connection = (this as any).os.stream.getConnection();
this.connectionId = this.$root.$data.os.stream.use(); this.connectionId = (this as any).os.stream.use();
this.connection.on('post', this.onStreamPost); this.connection.on('post', this.onStreamPost);
document.addEventListener('visibilitychange', this.onVisibilitychange, false); document.addEventListener('visibilitychange', this.onVisibilitychange, false);
@ -36,12 +36,12 @@ export default Vue.extend({
}, },
beforeDestroy() { beforeDestroy() {
this.connection.off('post', this.onStreamPost); this.connection.off('post', this.onStreamPost);
this.$root.$data.os.stream.dispose(this.connectionId); (this as any).os.stream.dispose(this.connectionId);
document.removeEventListener('visibilitychange', this.onVisibilitychange); document.removeEventListener('visibilitychange', this.onVisibilitychange);
}, },
methods: { methods: {
onStreamPost(post) { onStreamPost(post) {
if (document.hidden && post.user_id != this.$root.$data.os.i.id) { if (document.hidden && post.user_id != (this as any).os.i.id) {
this.unreadCount++; this.unreadCount++;
document.title = `(${this.unreadCount}) ${getPostSummary(post)}`; document.title = `(${this.unreadCount}) ${getPostSummary(post)}`;
} }

View File

@ -1,5 +1,5 @@
<template> <template>
<component v-bind:is="$root.$data.os.isSignedIn ? 'home' : 'welcome'"></component> <component v-bind:is="os.isSignedIn ? 'home' : 'welcome'"></component>
</template> </template>
<script lang="ts"> <script lang="ts">

View File

@ -21,7 +21,7 @@ export default Vue.extend({
document.documentElement.style.background = '#fff'; document.documentElement.style.background = '#fff';
this.$root.$data.os.api('users/show', { (this as any).api('users/show', {
username: this.username username: this.username
}).then(user => { }).then(user => {
this.fetching = false; this.fetching = false;

View File

@ -23,7 +23,7 @@ export default Vue.extend({
mounted() { mounted() {
Progress.start(); Progress.start();
this.$root.$data.os.api('posts/show', { (this as any).api('posts/show', {
post_id: this.postId post_id: this.postId
}).then(post => { }).then(post => {
this.fetching = false; this.fetching = false;

View File

@ -44,7 +44,7 @@ export default Vue.extend({
document.addEventListener('keydown', this.onDocumentKeydown); document.addEventListener('keydown', this.onDocumentKeydown);
window.addEventListener('scroll', this.onScroll); window.addEventListener('scroll', this.onScroll);
this.$root.$data.os.api('posts/search', parse(this.query)).then(posts => { (this as any).api('posts/search', parse(this.query)).then(posts => {
this.fetching = false; this.fetching = false;
this.posts = posts; this.posts = posts;
}); });
@ -65,7 +65,7 @@ export default Vue.extend({
if (this.moreFetching || this.fetching || this.posts.length == 0) return; if (this.moreFetching || this.fetching || this.posts.length == 0) return;
this.offset += limit; this.offset += limit;
this.moreFetching = true; this.moreFetching = true;
return this.$root.$data.os.api('posts/search', Object.assign({}, parse(this.query), { return (this as any).api('posts/search', Object.assign({}, parse(this.query), {
limit: limit, limit: limit,
offset: this.offset offset: this.offset
})).then(posts => { })).then(posts => {

View File

@ -22,7 +22,7 @@ export default Vue.extend({
}; };
}, },
mounted() { mounted() {
this.$root.$data.os.api('users/followers', { (this as any).api('users/followers', {
user_id: this.user.id, user_id: this.user.id,
iknow: true, iknow: true,
limit: 16 limit: 16

View File

@ -27,7 +27,7 @@ export default Vue.extend({
}; };
}, },
mounted() { mounted() {
this.$root.$data.os.api('users/get_frequently_replied_users', { (this as any).api('users/get_frequently_replied_users', {
user_id: this.user.id, user_id: this.user.id,
limit: 4 limit: 4
}).then(docs => { }).then(docs => {

View File

@ -51,9 +51,9 @@ export default Vue.extend({
}, },
onBannerClick() { onBannerClick() {
if (!this.$root.$data.os.isSignedIn || this.$root.$data.os.i.id != this.user.id) return; if (!(this as any).os.isSignedIn || (this as any).os.i.id != this.user.id) return;
updateBanner(this.$root.$data.os.i, i => { updateBanner((this as any).os.i, i => {
this.user.banner_url = i.banner_url; this.user.banner_url = i.banner_url;
}); });
} }

View File

@ -4,7 +4,7 @@
<div ref="left"> <div ref="left">
<mk-user-profile :user="user"/> <mk-user-profile :user="user"/>
<mk-user-photos :user="user"/> <mk-user-photos :user="user"/>
<mk-user-followers-you-know v-if="$root.$data.os.isSignedIn && $root.$data.os.i.id != user.id" :user="user"/> <mk-user-followers-you-know v-if="os.isSignedIn && os.i.id != user.id" :user="user"/>
<p>%i18n:desktop.tags.mk-user.last-used-at%: <b><mk-time :time="user.last_used_at"/></b></p> <p>%i18n:desktop.tags.mk-user.last-used-at%: <b><mk-time :time="user.last_used_at"/></b></p>
</div> </div>
</div> </div>

View File

@ -23,7 +23,7 @@ export default Vue.extend({
}; };
}, },
mounted() { mounted() {
this.$root.$data.os.api('users/posts', { (this as any).api('users/posts', {
user_id: this.user.id, user_id: this.user.id,
with_media: true, with_media: true,
limit: 9 limit: 9

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="mk-user-profile"> <div class="mk-user-profile">
<div class="friend-form" v-if="$root.$data.os.isSignedIn && $root.$data.os.i.id != user.id"> <div class="friend-form" v-if="os.isSignedIn && os.i.id != user.id">
<mk-follow-button :user="user" size="big"/> <mk-follow-button :user="user" size="big"/>
<p class="followed" v-if="user.is_followed">%i18n:desktop.tags.mk-user.follows-you%</p> <p class="followed" v-if="user.is_followed">%i18n:desktop.tags.mk-user.follows-you%</p>
<p v-if="user.is_muted">%i18n:desktop.tags.mk-user.muted% <a @click="unmute">%i18n:desktop.tags.mk-user.unmute%</a></p> <p v-if="user.is_muted">%i18n:desktop.tags.mk-user.muted% <a @click="unmute">%i18n:desktop.tags.mk-user.unmute%</a></p>
@ -35,7 +35,7 @@ export default Vue.extend({
methods: { methods: {
showFollowing() { showFollowing() {
document.body.appendChild(new MkUserFollowingWindow({ document.body.appendChild(new MkUserFollowingWindow({
parent: this,
propsData: { propsData: {
user: this.user user: this.user
} }
@ -44,7 +44,7 @@ export default Vue.extend({
showFollowers() { showFollowers() {
document.body.appendChild(new MkUserFollowersWindow({ document.body.appendChild(new MkUserFollowersWindow({
parent: this,
propsData: { propsData: {
user: this.user user: this.user
} }
@ -52,7 +52,7 @@ export default Vue.extend({
}, },
mute() { mute() {
this.$root.$data.os.api('mute/create', { (this as any).api('mute/create', {
user_id: this.user.id user_id: this.user.id
}).then(() => { }).then(() => {
this.user.is_muted = true; this.user.is_muted = true;
@ -62,7 +62,7 @@ export default Vue.extend({
}, },
unmute() { unmute() {
this.$root.$data.os.api('mute/delete', { (this as any).api('mute/delete', {
user_id: this.user.id user_id: this.user.id
}).then(() => { }).then(() => {
this.user.is_muted = false; this.user.is_muted = false;

View File

@ -29,7 +29,7 @@ export default Vue.extend({
}, },
mounted() { mounted() {
Progress.start(); Progress.start();
this.$root.$data.os.api('users/show', { (this as any).api('users/show', {
username: this.username username: this.username
}).then(user => { }).then(user => {
this.fetching = false; this.fetching = false;

View File

@ -78,37 +78,50 @@ if (localStorage.getItem('should-refresh') == 'true') {
type API = { type API = {
chooseDriveFile: (opts: { chooseDriveFile: (opts: {
title: string; title?: string;
currentFolder: any; currentFolder?: any;
multiple: boolean; multiple?: boolean;
}) => Promise<any>; }) => Promise<any>;
chooseDriveFolder: (opts: { chooseDriveFolder: (opts: {
title: string; title?: string;
currentFolder: any; currentFolder?: any;
}) => Promise<any>; }) => Promise<any>;
dialog: (opts: {
title: string;
text: string;
actions: Array<{
text: string;
id: string;
}>;
}) => Promise<string>;
input: (opts: {
title: string;
placeholder?: string;
default?: string;
}) => Promise<string>;
}; };
// MiOSを初期化してコールバックする // MiOSを初期化してコールバックする
export default (callback: (launch: (api: API) => Vue) => void, sw = false) => { export default (callback: (launch: (api: API) => Vue) => void, sw = false) => {
const mios = new MiOS(sw); const os = new MiOS(sw);
Vue.mixin({ os.init(() => {
data: {
$os: mios
}
});
mios.init(() => {
// アプリ基底要素マウント // アプリ基底要素マウント
document.body.innerHTML = '<div id="app"></div>'; document.body.innerHTML = '<div id="app"></div>';
const launch = (api: API) => { const launch = (api: API) => {
Vue.mixin({
created() {
(this as any).os = os;
(this as any).api = os.api;
(this as any).apis = api;
}
});
return new Vue({ return new Vue({
data: {
os: mios,
api: api
},
router: new VueRouter({ router: new VueRouter({
mode: 'history' mode: 'history'
}), }),
@ -124,7 +137,7 @@ export default (callback: (launch: (api: API) => Vue) => void, sw = false) => {
// 更新チェック // 更新チェック
setTimeout(() => { setTimeout(() => {
checkForUpdate(mios); checkForUpdate(os);
}, 3000); }, 3000);
}); });
}; };

View File

@ -87,8 +87,8 @@ export default Vue.extend({
} }
}, },
mounted() { mounted() {
this.connection = this.$root.$data.os.streams.driveStream.getConnection(); this.connection = (this as any).os.streams.driveStream.getConnection();
this.connectionId = this.$root.$data.os.streams.driveStream.use(); this.connectionId = (this as any).os.streams.driveStream.use();
this.connection.on('file_created', this.onStreamDriveFileCreated); this.connection.on('file_created', this.onStreamDriveFileCreated);
this.connection.on('file_updated', this.onStreamDriveFileUpdated); this.connection.on('file_updated', this.onStreamDriveFileUpdated);
@ -112,7 +112,7 @@ export default Vue.extend({
this.connection.off('file_updated', this.onStreamDriveFileUpdated); this.connection.off('file_updated', this.onStreamDriveFileUpdated);
this.connection.off('folder_created', this.onStreamDriveFolderCreated); this.connection.off('folder_created', this.onStreamDriveFolderCreated);
this.connection.off('folder_updated', this.onStreamDriveFolderUpdated); this.connection.off('folder_updated', this.onStreamDriveFolderUpdated);
this.$root.$data.os.streams.driveStream.dispose(this.connectionId); (this as any).os.streams.driveStream.dispose(this.connectionId);
}, },
methods: { methods: {
onStreamDriveFileCreated(file) { onStreamDriveFileCreated(file) {
@ -158,7 +158,7 @@ export default Vue.extend({
this.fetching = true; this.fetching = true;
this.$root.$data.os.api('drive/folders/show', { (this as any).api('drive/folders/show', {
folder_id: target folder_id: target
}).then(folder => { }).then(folder => {
this.folder = folder; this.folder = folder;
@ -253,7 +253,7 @@ export default Vue.extend({
const filesMax = 20; const filesMax = 20;
// //
this.$root.$data.os.api('drive/folders', { (this as any).api('drive/folders', {
folder_id: this.folder ? this.folder.id : null, folder_id: this.folder ? this.folder.id : null,
limit: foldersMax + 1 limit: foldersMax + 1
}).then(folders => { }).then(folders => {
@ -266,7 +266,7 @@ export default Vue.extend({
}); });
// //
this.$root.$data.os.api('drive/files', { (this as any).api('drive/files', {
folder_id: this.folder ? this.folder.id : null, folder_id: this.folder ? this.folder.id : null,
limit: filesMax + 1 limit: filesMax + 1
}).then(files => { }).then(files => {
@ -296,7 +296,7 @@ export default Vue.extend({
if (this.folder == null) { if (this.folder == null) {
// Fetch addtional drive info // Fetch addtional drive info
this.$root.$data.os.api('drive').then(info => { (this as any).api('drive').then(info => {
this.info = info; this.info = info;
}); });
} }
@ -309,7 +309,7 @@ export default Vue.extend({
const max = 30; const max = 30;
// //
this.$root.$data.os.api('drive/files', { (this as any).api('drive/files', {
folder_id: this.folder ? this.folder.id : null, folder_id: this.folder ? this.folder.id : null,
limit: max + 1, limit: max + 1,
until_id: this.files[this.files.length - 1].id until_id: this.files[this.files.length - 1].id
@ -348,7 +348,7 @@ export default Vue.extend({
this.fetching = true; this.fetching = true;
this.$root.$data.os.api('drive/files/show', { (this as any).api('drive/files/show', {
file_id: file file_id: file
}).then(file => { }).then(file => {
this.fetching = false; this.fetching = false;
@ -394,7 +394,7 @@ export default Vue.extend({
createFolder() { createFolder() {
const name = window.prompt('フォルダー名'); const name = window.prompt('フォルダー名');
if (name == null || name == '') return; if (name == null || name == '') return;
this.$root.$data.os.api('drive/folders/create', { (this as any).api('drive/folders/create', {
name: name, name: name,
parent_id: this.folder ? this.folder.id : undefined parent_id: this.folder ? this.folder.id : undefined
}).then(folder => { }).then(folder => {
@ -409,7 +409,7 @@ export default Vue.extend({
} }
const name = window.prompt('フォルダー名', this.folder.name); const name = window.prompt('フォルダー名', this.folder.name);
if (name == null || name == '') return; if (name == null || name == '') return;
this.$root.$data.os.api('drive/folders/update', { (this as any).api('drive/folders/update', {
name: name, name: name,
folder_id: this.folder.id folder_id: this.folder.id
}).then(folder => { }).then(folder => {
@ -424,7 +424,7 @@ export default Vue.extend({
} }
const dialog = riot.mount(document.body.appendChild(document.createElement('mk-drive-folder-selector')))[0]; const dialog = riot.mount(document.body.appendChild(document.createElement('mk-drive-folder-selector')))[0];
dialog.one('selected', folder => { dialog.one('selected', folder => {
this.$root.$data.os.api('drive/folders/update', { (this as any).api('drive/folders/update', {
parent_id: folder ? folder.id : null, parent_id: folder ? folder.id : null,
folder_id: this.folder.id folder_id: this.folder.id
}).then(folder => { }).then(folder => {
@ -436,7 +436,7 @@ export default Vue.extend({
urlUpload() { urlUpload() {
const url = window.prompt('アップロードしたいファイルのURL'); const url = window.prompt('アップロードしたいファイルのURL');
if (url == null || url == '') return; if (url == null || url == '') return;
this.$root.$data.os.api('drive/files/upload_from_url', { (this as any).api('drive/files/upload_from_url', {
url: url, url: url,
folder_id: this.folder ? this.folder.id : undefined folder_id: this.folder ? this.folder.id : undefined
}); });

View File

@ -28,8 +28,8 @@ export default Vue.extend({
}; };
}, },
mounted() { mounted() {
this.connection = this.$root.$data.os.stream.getConnection(); this.connection = (this as any).os.stream.getConnection();
this.connectionId = this.$root.$data.os.stream.use(); this.connectionId = (this as any).os.stream.use();
this.connection.on('follow', this.onFollow); this.connection.on('follow', this.onFollow);
this.connection.on('unfollow', this.onUnfollow); this.connection.on('unfollow', this.onUnfollow);
@ -37,7 +37,7 @@ export default Vue.extend({
beforeDestroy() { beforeDestroy() {
this.connection.off('follow', this.onFollow); this.connection.off('follow', this.onFollow);
this.connection.off('unfollow', this.onUnfollow); this.connection.off('unfollow', this.onUnfollow);
this.$root.$data.os.stream.dispose(this.connectionId); (this as any).os.stream.dispose(this.connectionId);
}, },
methods: { methods: {
@ -56,7 +56,7 @@ export default Vue.extend({
onClick() { onClick() {
this.wait = true; this.wait = true;
if (this.user.is_following) { if (this.user.is_following) {
this.$root.$data.os.api('following/delete', { (this as any).api('following/delete', {
user_id: this.user.id user_id: this.user.id
}).then(() => { }).then(() => {
this.user.is_following = false; this.user.is_following = false;
@ -66,7 +66,7 @@ export default Vue.extend({
this.wait = false; this.wait = false;
}); });
} else { } else {
this.$root.$data.os.api('following/create', { (this as any).api('following/create', {
user_id: this.user.id user_id: this.user.id
}).then(() => { }).then(() => {
this.user.is_following = true; this.user.is_following = true;

View File

@ -32,7 +32,7 @@ export default Vue.extend({
this.fetching = true; this.fetching = true;
this.users = []; this.users = [];
this.$root.$data.os.api('users/recommendation', { (this as any).api('users/recommendation', {
limit: this.limit, limit: this.limit,
offset: this.limit * this.page offset: this.limit * this.page
}).then(users => { }).then(users => {

View File

@ -42,14 +42,14 @@ export default Vue.extend({
} }
}, },
mounted() { mounted() {
this.connection = this.$root.$data.os.stream.getConnection(); this.connection = (this as any).os.stream.getConnection();
this.connectionId = this.$root.$data.os.stream.use(); this.connectionId = (this as any).os.stream.use();
this.connection.on('notification', this.onNotification); this.connection.on('notification', this.onNotification);
const max = 10; const max = 10;
this.$root.$data.os.api('i/notifications', { (this as any).api('i/notifications', {
limit: max + 1 limit: max + 1
}).then(notifications => { }).then(notifications => {
if (notifications.length == max + 1) { if (notifications.length == max + 1) {
@ -63,7 +63,7 @@ export default Vue.extend({
}, },
beforeDestroy() { beforeDestroy() {
this.connection.off('notification', this.onNotification); this.connection.off('notification', this.onNotification);
this.$root.$data.os.stream.dispose(this.connectionId); (this as any).os.stream.dispose(this.connectionId);
}, },
methods: { methods: {
fetchMoreNotifications() { fetchMoreNotifications() {
@ -71,7 +71,7 @@ export default Vue.extend({
const max = 30; const max = 30;
this.$root.$data.os.api('i/notifications', { (this as any).api('i/notifications', {
limit: max + 1, limit: max + 1,
until_id: this.notifications[this.notifications.length - 1].id until_id: this.notifications[this.notifications.length - 1].id
}).then(notifications => { }).then(notifications => {

View File

@ -33,7 +33,7 @@
</div> </div>
</header> </header>
<div class="body"> <div class="body">
<mk-post-html v-if="p.ast" :ast="p.ast" :i="$root.$data.os.i"/> <mk-post-html v-if="p.ast" :ast="p.ast" :i="os.i"/>
<mk-url-preview v-for="url in urls" :url="url" :key="url"/> <mk-url-preview v-for="url in urls" :url="url" :key="url"/>
<div class="media" v-if="p.media"> <div class="media" v-if="p.media">
<mk-images images={ p.media }/> <mk-images images={ p.media }/>
@ -116,7 +116,7 @@ export default Vue.extend({
mounted() { mounted() {
// Get replies // Get replies
if (!this.compact) { if (!this.compact) {
this.$root.$data.os.api('posts/replies', { (this as any).api('posts/replies', {
post_id: this.p.id, post_id: this.p.id,
limit: 8 limit: 8
}).then(replies => { }).then(replies => {
@ -129,7 +129,7 @@ export default Vue.extend({
this.contextFetching = true; this.contextFetching = true;
// Fetch context // Fetch context
this.$root.$data.os.api('posts/context', { (this as any).api('posts/context', {
post_id: this.p.reply_id post_id: this.p.reply_id
}).then(context => { }).then(context => {
this.contextFetching = false; this.contextFetching = false;

View File

@ -106,24 +106,24 @@ export default Vue.extend({
} }
}, },
created() { created() {
this.connection = this.$root.$data.os.stream.getConnection(); this.connection = (this as any).os.stream.getConnection();
this.connectionId = this.$root.$data.os.stream.use(); this.connectionId = (this as any).os.stream.use();
}, },
mounted() { mounted() {
this.capture(true); this.capture(true);
if (this.$root.$data.os.isSignedIn) { if ((this as any).os.isSignedIn) {
this.connection.on('_connected_', this.onStreamConnected); this.connection.on('_connected_', this.onStreamConnected);
} }
}, },
beforeDestroy() { beforeDestroy() {
this.decapture(true); this.decapture(true);
this.connection.off('_connected_', this.onStreamConnected); this.connection.off('_connected_', this.onStreamConnected);
this.$root.$data.os.stream.dispose(this.connectionId); (this as any).os.stream.dispose(this.connectionId);
}, },
methods: { methods: {
capture(withHandler = false) { capture(withHandler = false) {
if (this.$root.$data.os.isSignedIn) { if ((this as any).os.isSignedIn) {
this.connection.send({ this.connection.send({
type: 'capture', type: 'capture',
id: this.post.id id: this.post.id
@ -132,7 +132,7 @@ export default Vue.extend({
} }
}, },
decapture(withHandler = false) { decapture(withHandler = false) {
if (this.$root.$data.os.isSignedIn) { if ((this as any).os.isSignedIn) {
this.connection.send({ this.connection.send({
type: 'decapture', type: 'decapture',
id: this.post.id id: this.post.id

View File

@ -2,7 +2,7 @@
<div class="mk-sub-post-content"> <div class="mk-sub-post-content">
<div class="body"> <div class="body">
<a class="reply" v-if="post.reply_id">%fa:reply%</a> <a class="reply" v-if="post.reply_id">%fa:reply%</a>
<mk-post-html v-if="post.ast" :ast="post.ast" :i="$root.$data.os.i"/> <mk-post-html v-if="post.ast" :ast="post.ast" :i="os.i"/>
<a class="quote" v-if="post.repost_id">RP: ...</a> <a class="quote" v-if="post.repost_id">RP: ...</a>
</div> </div>
<details v-if="post.media"> <details v-if="post.media">

View File

@ -37,12 +37,12 @@ export default Vue.extend({
}, },
computed: { computed: {
alone(): boolean { alone(): boolean {
return this.$root.$data.os.i.following_count == 0; return (this as any).os.i.following_count == 0;
} }
}, },
mounted() { mounted() {
this.connection = this.$root.$data.os.stream.getConnection(); this.connection = (this as any).os.stream.getConnection();
this.connectionId = this.$root.$data.os.stream.use(); this.connectionId = (this as any).os.stream.use();
this.connection.on('post', this.onPost); this.connection.on('post', this.onPost);
this.connection.on('follow', this.onChangeFollowing); this.connection.on('follow', this.onChangeFollowing);
@ -54,13 +54,13 @@ export default Vue.extend({
this.connection.off('post', this.onPost); this.connection.off('post', this.onPost);
this.connection.off('follow', this.onChangeFollowing); this.connection.off('follow', this.onChangeFollowing);
this.connection.off('unfollow', this.onChangeFollowing); this.connection.off('unfollow', this.onChangeFollowing);
this.$root.$data.os.stream.dispose(this.connectionId); (this as any).os.stream.dispose(this.connectionId);
}, },
methods: { methods: {
fetch(cb?) { fetch(cb?) {
this.fetching = true; this.fetching = true;
this.$root.$data.os.api('posts/timeline', { (this as any).api('posts/timeline', {
until_date: this.date ? (this.date as any).getTime() : undefined until_date: this.date ? (this.date as any).getTime() : undefined
}).then(posts => { }).then(posts => {
this.fetching = false; this.fetching = false;
@ -71,7 +71,7 @@ export default Vue.extend({
more() { more() {
if (this.moreFetching || this.fetching || this.posts.length == 0) return; if (this.moreFetching || this.fetching || this.posts.length == 0) return;
this.moreFetching = true; this.moreFetching = true;
this.$root.$data.os.api('posts/timeline', { (this as any).api('posts/timeline', {
until_id: this.posts[this.posts.length - 1].id until_id: this.posts[this.posts.length - 1].id
}).then(posts => { }).then(posts => {
this.moreFetching = false; this.moreFetching = false;

View File

@ -31,9 +31,9 @@ export default Vue.extend({
}; };
}, },
mounted() { mounted() {
if (this.$root.$data.os.isSignedIn) { if ((this as any).os.isSignedIn) {
this.connection = this.$root.$data.os.stream.getConnection(); this.connection = (this as any).os.stream.getConnection();
this.connectionId = this.$root.$data.os.stream.use(); this.connectionId = (this as any).os.stream.use();
this.connection.on('read_all_notifications', this.onReadAllNotifications); this.connection.on('read_all_notifications', this.onReadAllNotifications);
this.connection.on('unread_notification', this.onUnreadNotification); this.connection.on('unread_notification', this.onUnreadNotification);
@ -41,14 +41,14 @@ export default Vue.extend({
this.connection.on('unread_messaging_message', this.onUnreadMessagingMessage); this.connection.on('unread_messaging_message', this.onUnreadMessagingMessage);
// Fetch count of unread notifications // Fetch count of unread notifications
this.$root.$data.os.api('notifications/get_unread_count').then(res => { (this as any).api('notifications/get_unread_count').then(res => {
if (res.count > 0) { if (res.count > 0) {
this.hasUnreadNotifications = true; this.hasUnreadNotifications = true;
} }
}); });
// Fetch count of unread messaging messages // Fetch count of unread messaging messages
this.$root.$data.os.api('messaging/unread').then(res => { (this as any).api('messaging/unread').then(res => {
if (res.count > 0) { if (res.count > 0) {
this.hasUnreadMessagingMessages = true; this.hasUnreadMessagingMessages = true;
} }
@ -56,12 +56,12 @@ export default Vue.extend({
} }
}, },
beforeDestroy() { beforeDestroy() {
if (this.$root.$data.os.isSignedIn) { if ((this as any).os.isSignedIn) {
this.connection.off('read_all_notifications', this.onReadAllNotifications); this.connection.off('read_all_notifications', this.onReadAllNotifications);
this.connection.off('unread_notification', this.onUnreadNotification); this.connection.off('unread_notification', this.onUnreadNotification);
this.connection.off('read_all_messaging_messages', this.onReadAllMessagingMessages); this.connection.off('read_all_messaging_messages', this.onReadAllMessagingMessages);
this.connection.off('unread_messaging_message', this.onUnreadMessagingMessage); this.connection.off('unread_messaging_message', this.onUnreadMessagingMessage);
this.$root.$data.os.stream.dispose(this.connectionId); (this as any).os.stream.dispose(this.connectionId);
} }
}, },
methods: { methods: {

View File

@ -2,7 +2,7 @@
<div class="mk-ui-nav" :style="{ display: isOpen ? 'block' : 'none' }"> <div class="mk-ui-nav" :style="{ display: isOpen ? 'block' : 'none' }">
<div class="backdrop" @click="parent.toggleDrawer"></div> <div class="backdrop" @click="parent.toggleDrawer"></div>
<div class="body"> <div class="body">
<a class="me" v-if="$root.$data.os.isSignedIn" href={ '/' + I.username }> <a class="me" v-if="os.isSignedIn" href={ '/' + I.username }>
<img class="avatar" src={ I.avatar_url + '?thumbnail&size=128' } alt="avatar"/> <img class="avatar" src={ I.avatar_url + '?thumbnail&size=128' } alt="avatar"/>
<p class="name">{ I.name }</p> <p class="name">{ I.name }</p>
</a> </a>
@ -41,9 +41,9 @@ export default Vue.extend({
}; };
}, },
mounted() { mounted() {
if (this.$root.$data.os.isSignedIn) { if ((this as any).os.isSignedIn) {
this.connection = this.$root.$data.os.stream.getConnection(); this.connection = (this as any).os.stream.getConnection();
this.connectionId = this.$root.$data.os.stream.use(); this.connectionId = (this as any).os.stream.use();
this.connection.on('read_all_notifications', this.onReadAllNotifications); this.connection.on('read_all_notifications', this.onReadAllNotifications);
this.connection.on('unread_notification', this.onUnreadNotification); this.connection.on('unread_notification', this.onUnreadNotification);
@ -51,14 +51,14 @@ export default Vue.extend({
this.connection.on('unread_messaging_message', this.onUnreadMessagingMessage); this.connection.on('unread_messaging_message', this.onUnreadMessagingMessage);
// Fetch count of unread notifications // Fetch count of unread notifications
this.$root.$data.os.api('notifications/get_unread_count').then(res => { (this as any).api('notifications/get_unread_count').then(res => {
if (res.count > 0) { if (res.count > 0) {
this.hasUnreadNotifications = true; this.hasUnreadNotifications = true;
} }
}); });
// Fetch count of unread messaging messages // Fetch count of unread messaging messages
this.$root.$data.os.api('messaging/unread').then(res => { (this as any).api('messaging/unread').then(res => {
if (res.count > 0) { if (res.count > 0) {
this.hasUnreadMessagingMessages = true; this.hasUnreadMessagingMessages = true;
} }
@ -66,12 +66,12 @@ export default Vue.extend({
} }
}, },
beforeDestroy() { beforeDestroy() {
if (this.$root.$data.os.isSignedIn) { if ((this as any).os.isSignedIn) {
this.connection.off('read_all_notifications', this.onReadAllNotifications); this.connection.off('read_all_notifications', this.onReadAllNotifications);
this.connection.off('unread_notification', this.onUnreadNotification); this.connection.off('unread_notification', this.onUnreadNotification);
this.connection.off('read_all_messaging_messages', this.onReadAllMessagingMessages); this.connection.off('read_all_messaging_messages', this.onReadAllMessagingMessages);
this.connection.off('unread_messaging_message', this.onUnreadMessagingMessage); this.connection.off('unread_messaging_message', this.onUnreadMessagingMessage);
this.$root.$data.os.stream.dispose(this.connectionId); (this as any).os.stream.dispose(this.connectionId);
} }
}, },
methods: { methods: {

View File

@ -7,7 +7,7 @@
<div class="content"> <div class="content">
<slot></slot> <slot></slot>
</div> </div>
<mk-stream-indicator v-if="$root.$data.os.isSignedIn"/> <mk-stream-indicator v-if="os.isSignedIn"/>
</div> </div>
</template> </template>
@ -23,17 +23,17 @@ export default Vue.extend({
}; };
}, },
mounted() { mounted() {
if (this.$root.$data.os.isSignedIn) { if ((this as any).os.isSignedIn) {
this.connection = this.$root.$data.os.stream.getConnection(); this.connection = (this as any).os.stream.getConnection();
this.connectionId = this.$root.$data.os.stream.use(); this.connectionId = (this as any).os.stream.use();
this.connection.on('notification', this.onNotification); this.connection.on('notification', this.onNotification);
} }
}, },
beforeDestroy() { beforeDestroy() {
if (this.$root.$data.os.isSignedIn) { if ((this as any).os.isSignedIn) {
this.connection.off('notification', this.onNotification); this.connection.off('notification', this.onNotification);
this.$root.$data.os.stream.dispose(this.connectionId); (this as any).os.stream.dispose(this.connectionId);
} }
}, },
methods: { methods: {

View File

@ -14,7 +14,7 @@ export default Vue.extend({
props: ['user'], props: ['user'],
methods: { methods: {
fetch(iknow, limit, cursor, cb) { fetch(iknow, limit, cursor, cb) {
this.$root.$data.os.api('users/followers', { (this as any).api('users/followers', {
user_id: this.user.id, user_id: this.user.id,
iknow: iknow, iknow: iknow,
limit: limit, limit: limit,

View File

@ -14,7 +14,7 @@ export default Vue.extend({
props: ['user'], props: ['user'],
methods: { methods: {
fetch(iknow, limit, cursor, cb) { fetch(iknow, limit, cursor, cb) {
this.$root.$data.os.api('users/following', { (this as any).api('users/following', {
user_id: this.user.id, user_id: this.user.id,
iknow: iknow, iknow: iknow,
limit: limit, limit: limit,

View File

@ -27,7 +27,7 @@ export default Vue.extend({
}; };
}, },
mounted() { mounted() {
this.$root.$data.os.api('users/posts', { (this as any).api('users/posts', {
user_id: this.user.id, user_id: this.user.id,
with_media: this.withMedia with_media: this.withMedia
}).then(posts => { }).then(posts => {

View File

@ -2,7 +2,7 @@
<div class="mk-users-list"> <div class="mk-users-list">
<nav> <nav>
<span :data-is-active="mode == 'all'" @click="mode = 'all'">%i18n:mobile.tags.mk-users-list.all%<span>{{ count }}</span></span> <span :data-is-active="mode == 'all'" @click="mode = 'all'">%i18n:mobile.tags.mk-users-list.all%<span>{{ count }}</span></span>
<span v-if="$root.$data.os.isSignedIn && youKnowCount" :data-is-active="mode == 'iknow'" @click="mode = 'iknow'">%i18n:mobile.tags.mk-users-list.known%<span>{{ youKnowCount }}</span></span> <span v-if="os.isSignedIn && youKnowCount" :data-is-active="mode == 'iknow'" @click="mode = 'iknow'">%i18n:mobile.tags.mk-users-list.known%<span>{{ youKnowCount }}</span></span>
</nav> </nav>
<div class="users" v-if="!fetching && users.length != 0"> <div class="users" v-if="!fetching && users.length != 0">
<mk-user-preview v-for="u in users" :user="u" :key="u.id"/> <mk-user-preview v-for="u in users" :user="u" :key="u.id"/>

View File

@ -23,7 +23,7 @@ export default Vue.extend({
mounted() { mounted() {
Progress.start(); Progress.start();
this.$root.$data.os.api('users/show', { (this as any).api('users/show', {
username: this.username username: this.username
}).then(user => { }).then(user => {
this.fetching = false; this.fetching = false;

View File

@ -23,7 +23,7 @@ export default Vue.extend({
mounted() { mounted() {
Progress.start(); Progress.start();
this.$root.$data.os.api('users/show', { (this as any).api('users/show', {
username: this.username username: this.username
}).then(user => { }).then(user => {
this.fetching = false; this.fetching = false;

View File

@ -23,8 +23,8 @@ export default Vue.extend({
document.title = 'Misskey'; document.title = 'Misskey';
document.documentElement.style.background = '#313a42'; document.documentElement.style.background = '#313a42';
this.connection = this.$root.$data.os.stream.getConnection(); this.connection = (this as any).os.stream.getConnection();
this.connectionId = this.$root.$data.os.stream.use(); this.connectionId = (this as any).os.stream.use();
this.connection.on('post', this.onStreamPost); this.connection.on('post', this.onStreamPost);
document.addEventListener('visibilitychange', this.onVisibilitychange, false); document.addEventListener('visibilitychange', this.onVisibilitychange, false);
@ -33,7 +33,7 @@ export default Vue.extend({
}, },
beforeDestroy() { beforeDestroy() {
this.connection.off('post', this.onStreamPost); this.connection.off('post', this.onStreamPost);
this.$root.$data.os.stream.dispose(this.connectionId); (this as any).os.stream.dispose(this.connectionId);
document.removeEventListener('visibilitychange', this.onVisibilitychange); document.removeEventListener('visibilitychange', this.onVisibilitychange);
}, },
methods: { methods: {
@ -44,7 +44,7 @@ export default Vue.extend({
Progress.done(); Progress.done();
}, },
onStreamPost(post) { onStreamPost(post) {
if (document.hidden && post.user_id !== this.$root.$data.os.i.id) { if (document.hidden && post.user_id !== (this as any).os.i.id) {
this.unreadCount++; this.unreadCount++;
document.title = `(${this.unreadCount}) ${getPostSummary(post)}`; document.title = `(${this.unreadCount}) ${getPostSummary(post)}`;
} }

View File

@ -21,7 +21,7 @@ export default Vue.extend({
const ok = window.confirm('%i18n:mobile.tags.mk-notifications-page.read-all%'); const ok = window.confirm('%i18n:mobile.tags.mk-notifications-page.read-all%');
if (!ok) return; if (!ok) return;
this.$root.$data.os.api('notifications/mark_as_read_all'); (this as any).api('notifications/mark_as_read_all');
}, },
onFetched() { onFetched() {
Progress.done(); Progress.done();

View File

@ -29,7 +29,7 @@ export default Vue.extend({
Progress.start(); Progress.start();
this.$root.$data.os.api('posts/show', { (this as any).api('posts/show', {
post_id: this.postId post_id: this.postId
}).then(post => { }).then(post => {
this.fetching = false; this.fetching = false;

View File

@ -35,7 +35,7 @@ export default Vue.extend({
Progress.start(); Progress.start();
this.$root.$data.os.api('posts/search', Object.assign({}, parse(this.query), { (this as any).api('posts/search', Object.assign({}, parse(this.query), {
limit: limit limit: limit
})).then(posts => { })).then(posts => {
this.posts = posts; this.posts = posts;
@ -46,7 +46,7 @@ export default Vue.extend({
methods: { methods: {
more() { more() {
this.offset += limit; this.offset += limit;
return this.$root.$data.os.api('posts/search', Object.assign({}, parse(this.query), { return (this as any).api('posts/search', Object.assign({}, parse(this.query), {
limit: limit, limit: limit,
offset: this.offset offset: this.offset
})); }));

View File

@ -9,7 +9,7 @@
<a class="avatar"> <a class="avatar">
<img :src="`${user.avatar_url}?thumbnail&size=200`" alt="avatar"/> <img :src="`${user.avatar_url}?thumbnail&size=200`" alt="avatar"/>
</a> </a>
<mk-follow-button v-if="$root.$data.os.isSignedIn && $root.$data.os.i.id != user.id" :user="user"/> <mk-follow-button v-if="os.isSignedIn && os.i.id != user.id" :user="user"/>
</div> </div>
<div class="title"> <div class="title">
<h1>{{ user.name }}</h1> <h1>{{ user.name }}</h1>
@ -85,7 +85,7 @@ export default Vue.extend({
document.documentElement.style.background = '#313a42'; document.documentElement.style.background = '#313a42';
Progress.start(); Progress.start();
this.$root.$data.os.api('users/show', { (this as any).api('users/show', {
username: this.username username: this.username
}).then(user => { }).then(user => {
this.fetching = false; this.fetching = false;

View File

@ -21,7 +21,7 @@ export default Vue.extend({
}; };
}, },
mounted() { mounted() {
this.$root.$data.os.api('users/followers', { (this as any).api('users/followers', {
user_id: this.user.id, user_id: this.user.id,
iknow: true, iknow: true,
limit: 30 limit: 30

View File

@ -28,7 +28,7 @@ export default Vue.extend({
}; };
}, },
mounted() { mounted() {
this.$root.$data.os.api('aggregation/users/activity', { (this as any).api('aggregation/users/activity', {
user_id: this.user.id, user_id: this.user.id,
limit: 30 limit: 30
}).then(data => { }).then(data => {

View File

@ -19,7 +19,7 @@ export default Vue.extend({
}; };
}, },
mounted() { mounted() {
this.$root.$data.os.api('users/get_frequently_replied_users', { (this as any).api('users/get_frequently_replied_users', {
user_id: this.user.id user_id: this.user.id
}).then(res => { }).then(res => {
this.fetching = false; this.fetching = false;

Some files were not shown because too many files have changed in this diff Show More