Refactor client (#3178)

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip
This commit is contained in:
syuilo 2018-11-09 16:00:29 +09:00 committed by GitHub
parent 5d882dc3df
commit 3f79c9ae49
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
52 changed files with 328 additions and 421 deletions

View file

@ -1,8 +1,7 @@
import MiOS from '../../mios';
import { clientVersion as current } from '../../config'; import { clientVersion as current } from '../../config';
export default async function(mios: MiOS, force = false, silent = false) { export default async function($root: any, force = false, silent = false) {
const meta = await mios.getMeta(force); const meta = await $root.getMeta(force);
const newer = meta.clientVersion; const newer = meta.clientVersion;
if (newer != current) { if (newer != current) {
@ -23,7 +22,7 @@ export default async function(mios: MiOS, force = false, silent = false) {
} }
if (!silent) { if (!silent) {
mios.apis.dialog({ $root.$dialog({
title: '%i18n:common.update-available-title%', title: '%i18n:common.update-available-title%',
text: '%i18n:common.update-available%'.replace('{newer}', newer).replace('{current}', current) text: '%i18n:common.update-available%'.replace('{newer}', newer).replace('{current}', current)
}); });

View file

@ -1,10 +1,10 @@
declare const fuckAdBlock: any; declare const fuckAdBlock: any;
export default (os) => { export default ($root: any) => {
require('fuckadblock'); require('fuckadblock');
function adBlockDetected() { function adBlockDetected() {
os.apis.dialog({ $root.$dialog({
title: '%fa:exclamation-triangle%%i18n:common.adblock.detected%', title: '%fa:exclamation-triangle%%i18n:common.adblock.detected%',
text: '%i18n:common.adblock.warning%', text: '%i18n:common.adblock.warning%',
actins: [{ actins: [{

View file

@ -88,7 +88,7 @@ export default (opts: Opts = {}) => ({
methods: { methods: {
reply(viaKeyboard = false) { reply(viaKeyboard = false) {
this.$root.apis.post({ this.$root.$post({
reply: this.appearNote, reply: this.appearNote,
animation: !viaKeyboard, animation: !viaKeyboard,
cb: () => { cb: () => {
@ -98,7 +98,7 @@ export default (opts: Opts = {}) => ({
}, },
renote(viaKeyboard = false) { renote(viaKeyboard = false) {
this.$root.apis.post({ this.$root.$post({
renote: this.appearNote, renote: this.appearNote,
animation: !viaKeyboard, animation: !viaKeyboard,
cb: () => { cb: () => {

View file

@ -50,7 +50,7 @@ export default Vue.extend({
methods: { methods: {
regenerateToken() { regenerateToken() {
this.$root.apis.input({ this.$input({
title: this.$t('enter-password'), title: this.$t('enter-password'),
type: 'password' type: 'password'
}).then(password => { }).then(password => {

View file

@ -100,7 +100,7 @@ export default Vue.extend({
}, },
match() { match() {
this.$root.apis.input({ this.$input({
title: this.$t('enter-username') title: this.$t('enter-username')
}).then(username => { }).then(username => {
this.$root.api('users/show', { this.$root.api('users/show', {

View file

@ -131,7 +131,7 @@ export default Vue.extend({
}, },
chooseFileFromDrive() { chooseFileFromDrive() {
this.$root.apis.chooseDriveFile({ this.$chooseDriveFile({
multiple: false multiple: false
}).then(file => { }).then(file => {
this.file = file; this.file = file;

View file

@ -12,20 +12,20 @@ export default Vue.extend({
i18n: i18n('common/views/components/password-settings.vue'), i18n: i18n('common/views/components/password-settings.vue'),
methods: { methods: {
reset() { reset() {
this.$root.apis.input({ this.$input({
title: this.$t('enter-current-password'), title: this.$t('enter-current-password'),
type: 'password' type: 'password'
}).then(currentPassword => { }).then(currentPassword => {
this.$root.apis.input({ this.$input({
title: this.$t('enter-new-password'), title: this.$t('enter-new-password'),
type: 'password' type: 'password'
}).then(newPassword => { }).then(newPassword => {
this.$root.apis.input({ this.$input({
title: this.$t('enter-new-password-again'), title: this.$t('enter-new-password-again'),
type: 'password' type: 'password'
}).then(newPassword2 => { }).then(newPassword2 => {
if (newPassword !== newPassword2) { if (newPassword !== newPassword2) {
this.$root.apis.dialog({ this.$dialog({
title: null, title: null,
text: this.$t('not-match'), text: this.$t('not-match'),
actions: [{ actions: [{
@ -38,7 +38,7 @@ export default Vue.extend({
currentPasword: currentPassword, currentPasword: currentPassword,
newPassword: newPassword newPassword: newPassword
}).then(() => { }).then(() => {
this.$root.apis.notify(this.$t('changed')); this.$notify(this.$t('changed'));
}); });
}); });
}); });

View file

@ -114,7 +114,7 @@ export default define({
}); });
}, },
choose() { choose() {
this.$root.apis.chooseDriveFolder().then(folder => { this.$chooseDriveFolder().then(folder => {
this.props.folder = folder ? folder.id : null; this.props.folder = folder ? folder.id : null;
this.save(); this.save();
this.fetch(); this.fetch();

View file

@ -1,29 +0,0 @@
import OS from '../../mios';
import { url } from '../../config';
import MkChooseFileFromDriveWindow from '../views/components/choose-file-from-drive-window.vue';
export default (os: OS) => opts => {
return new Promise((res, rej) => {
const o = opts || {};
if (document.body.clientWidth > 800) {
const w = os.new(MkChooseFileFromDriveWindow, {
title: o.title,
multiple: o.multiple,
initFolder: o.currentFolder
});
w.$once('selected', file => {
res(file);
});
document.body.appendChild(w.$el);
} else {
window['cb'] = file => {
res(file);
};
window.open(url + `/selectdrive?multiple=${o.multiple}`,
'choose_drive_window',
'height=500, width=800');
}
});
};

View file

@ -1,16 +0,0 @@
import OS from '../../mios';
import MkChooseFolderFromDriveWindow from '../views/components/choose-folder-from-drive-window.vue';
export default (os: OS) => opts => {
return new Promise((res, rej) => {
const o = opts || {};
const w = os.new(MkChooseFolderFromDriveWindow, {
title: o.title,
initFolder: o.currentFolder
});
w.$once('selected', folder => {
res(folder);
});
document.body.appendChild(w.$el);
});
};

View file

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

View file

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

View file

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

View file

@ -1,9 +0,0 @@
import OS from '../../mios';
import Notification from '../views/components/ui-notification.vue';
export default (os: OS) => message => {
const vm = os.new(Notification, {
message
});
document.body.appendChild(vm.$el);
};

View file

@ -1,22 +0,0 @@
import OS from '../../mios';
import PostFormWindow from '../views/components/post-form-window.vue';
import RenoteFormWindow from '../views/components/renote-form-window.vue';
export default (os: OS) => opts => {
const o = opts || {};
if (o.renote) {
const vm = os.new(RenoteFormWindow, {
note: o.renote,
animation: o.animation == null ? true : o.animation
});
if (o.cb) vm.$once('closed', o.cb);
document.body.appendChild(vm.$el);
} else {
const vm = os.new(PostFormWindow, {
reply: o.reply,
animation: o.animation == null ? true : o.animation
});
if (o.cb) vm.$once('closed', o.cb);
document.body.appendChild(vm.$el);
}
};

View file

@ -1,15 +1,14 @@
import OS from '../../mios';
import { apiUrl } from '../../config'; import { apiUrl } from '../../config';
import CropWindow from '../views/components/crop-window.vue'; import CropWindow from '../views/components/crop-window.vue';
import ProgressDialog from '../views/components/progress-dialog.vue'; import ProgressDialog from '../views/components/progress-dialog.vue';
export default (os: OS) => { export default ($root: any) => {
const cropImage = file => new Promise((resolve, reject) => { const cropImage = file => new Promise((resolve, reject) => {
const regex = RegExp('\.(jpg|jpeg|png|gif|webp|bmp|tiff)$'); const regex = RegExp('\.(jpg|jpeg|png|gif|webp|bmp|tiff)$');
if (!regex.test(file.name) ) { if (!regex.test(file.name) ) {
os.apis.dialog({ $root.$dialog({
title: '%fa:info-circle% %i18n:desktop.invalid-filetype%', title: '%fa:info-circle% %i18n:desktop.invalid-filetype%',
text: null, text: null,
actions: [{ actions: [{
@ -19,7 +18,7 @@ export default (os: OS) => {
return reject('invalid-filetype'); return reject('invalid-filetype');
} }
const w = os.new(CropWindow, { const w = $root.new(CropWindow, {
image: file, image: file,
title: '%i18n:desktop.avatar-crop-title%', title: '%i18n:desktop.avatar-crop-title%',
aspectRatio: 1 / 1 aspectRatio: 1 / 1
@ -27,14 +26,14 @@ export default (os: OS) => {
w.$once('cropped', blob => { w.$once('cropped', blob => {
const data = new FormData(); const data = new FormData();
data.append('i', os.store.state.i.token); data.append('i', $root.$store.state.i.token);
data.append('file', blob, file.name + '.cropped.png'); data.append('file', blob, file.name + '.cropped.png');
os.api('drive/folders/find', { $root.api('drive/folders/find', {
name: '%i18n:desktop.avatar%' name: '%i18n:desktop.avatar%'
}).then(avatarFolder => { }).then(avatarFolder => {
if (avatarFolder.length === 0) { if (avatarFolder.length === 0) {
os.api('drive/folders/create', { $root.api('drive/folders/create', {
name: '%i18n:desktop.avatar%' name: '%i18n:desktop.avatar%'
}).then(iconFolder => { }).then(iconFolder => {
resolve(upload(data, iconFolder)); resolve(upload(data, iconFolder));
@ -55,7 +54,7 @@ export default (os: OS) => {
}); });
const upload = (data, folder) => new Promise((resolve, reject) => { const upload = (data, folder) => new Promise((resolve, reject) => {
const dialog = os.new(ProgressDialog, { const dialog = $root.new(ProgressDialog, {
title: '%i18n:desktop.uploading-avatar%' title: '%i18n:desktop.uploading-avatar%'
}); });
document.body.appendChild(dialog.$el); document.body.appendChild(dialog.$el);
@ -79,19 +78,19 @@ export default (os: OS) => {
}); });
const setAvatar = file => { const setAvatar = file => {
return os.api('i/update', { return $root.api('i/update', {
avatarId: file.id avatarId: file.id
}).then(i => { }).then(i => {
os.store.commit('updateIKeyValue', { $root.$store.commit('updateIKeyValue', {
key: 'avatarId', key: 'avatarId',
value: i.avatarId value: i.avatarId
}); });
os.store.commit('updateIKeyValue', { $root.$store.commit('updateIKeyValue', {
key: 'avatarUrl', key: 'avatarUrl',
value: i.avatarUrl value: i.avatarUrl
}); });
os.apis.dialog({ $root.$dialog({
title: '%fa:info-circle% %i18n:desktop.avatar-updated%', title: '%fa:info-circle% %i18n:desktop.avatar-updated%',
text: null, text: null,
actions: [{ actions: [{
@ -106,7 +105,7 @@ export default (os: OS) => {
return (file = null) => { return (file = null) => {
const selectedFile = file const selectedFile = file
? Promise.resolve(file) ? Promise.resolve(file)
: os.apis.chooseDriveFile({ : $root.$chooseDriveFile({
multiple: false, multiple: false,
title: '%fa:image% %i18n:desktop.choose-avatar%' title: '%fa:image% %i18n:desktop.choose-avatar%'
}); });

View file

@ -1,15 +1,14 @@
import OS from '../../mios';
import { apiUrl } from '../../config'; import { apiUrl } from '../../config';
import CropWindow from '../views/components/crop-window.vue'; import CropWindow from '../views/components/crop-window.vue';
import ProgressDialog from '../views/components/progress-dialog.vue'; import ProgressDialog from '../views/components/progress-dialog.vue';
export default (os: OS) => { export default ($root: any) => {
const cropImage = file => new Promise((resolve, reject) => { const cropImage = file => new Promise((resolve, reject) => {
const regex = RegExp('\.(jpg|jpeg|png|gif|webp|bmp|tiff)$'); const regex = RegExp('\.(jpg|jpeg|png|gif|webp|bmp|tiff)$');
if (!regex.test(file.name) ) { if (!regex.test(file.name) ) {
os.apis.dialog({ $root.dialog({
title: '%fa:info-circle% %i18n:desktop.invalid-filetype%', title: '%fa:info-circle% %i18n:desktop.invalid-filetype%',
text: null, text: null,
actions: [{ actions: [{
@ -19,7 +18,7 @@ export default (os: OS) => {
return reject('invalid-filetype'); return reject('invalid-filetype');
} }
const w = os.new(CropWindow, { const w = $root.new(CropWindow, {
image: file, image: file,
title: '%i18n:desktop.banner-crop-title%', title: '%i18n:desktop.banner-crop-title%',
aspectRatio: 16 / 9 aspectRatio: 16 / 9
@ -27,14 +26,14 @@ export default (os: OS) => {
w.$once('cropped', blob => { w.$once('cropped', blob => {
const data = new FormData(); const data = new FormData();
data.append('i', os.store.state.i.token); data.append('i', $root.$store.state.i.token);
data.append('file', blob, file.name + '.cropped.png'); data.append('file', blob, file.name + '.cropped.png');
os.api('drive/folders/find', { $root.api('drive/folders/find', {
name: '%i18n:desktop.banner%' name: '%i18n:desktop.banner%'
}).then(bannerFolder => { }).then(bannerFolder => {
if (bannerFolder.length === 0) { if (bannerFolder.length === 0) {
os.api('drive/folders/create', { $root.api('drive/folders/create', {
name: '%i18n:desktop.banner%' name: '%i18n:desktop.banner%'
}).then(iconFolder => { }).then(iconFolder => {
resolve(upload(data, iconFolder)); resolve(upload(data, iconFolder));
@ -55,7 +54,7 @@ export default (os: OS) => {
}); });
const upload = (data, folder) => new Promise((resolve, reject) => { const upload = (data, folder) => new Promise((resolve, reject) => {
const dialog = os.new(ProgressDialog, { const dialog = $root.new(ProgressDialog, {
title: '%i18n:desktop.uploading-banner%' title: '%i18n:desktop.uploading-banner%'
}); });
document.body.appendChild(dialog.$el); document.body.appendChild(dialog.$el);
@ -79,19 +78,19 @@ export default (os: OS) => {
}); });
const setBanner = file => { const setBanner = file => {
return os.api('i/update', { return $root.api('i/update', {
bannerId: file.id bannerId: file.id
}).then(i => { }).then(i => {
os.store.commit('updateIKeyValue', { $root.$store.commit('updateIKeyValue', {
key: 'bannerId', key: 'bannerId',
value: i.bannerId value: i.bannerId
}); });
os.store.commit('updateIKeyValue', { $root.$store.commit('updateIKeyValue', {
key: 'bannerUrl', key: 'bannerUrl',
value: i.bannerUrl value: i.bannerUrl
}); });
os.apis.dialog({ $root.$dialog({
title: '%fa:info-circle% %i18n:desktop.banner-updated%', title: '%fa:info-circle% %i18n:desktop.banner-updated%',
text: null, text: null,
actions: [{ actions: [{
@ -106,7 +105,7 @@ export default (os: OS) => {
return (file = null) => { return (file = null) => {
const selectedFile = file const selectedFile = file
? Promise.resolve(file) ? Promise.resolve(file)
: os.apis.chooseDriveFile({ : $root.$chooseDriveFile({
multiple: false, multiple: false,
title: '%fa:image% %i18n:desktop.choose-banner%' title: '%fa:image% %i18n:desktop.choose-banner%'
}); });

View file

@ -2,6 +2,7 @@
* Desktop Client * Desktop Client
*/ */
import Vue from 'vue';
import VueRouter from 'vue-router'; import VueRouter from 'vue-router';
// Style // Style
@ -11,15 +12,6 @@ import init from '../init';
import fuckAdBlock from '../common/scripts/fuck-ad-block'; import fuckAdBlock from '../common/scripts/fuck-ad-block';
import composeNotification from '../common/scripts/compose-notification'; import composeNotification from '../common/scripts/compose-notification';
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 post from './api/post';
import notify from './api/notify';
import updateAvatar from './api/update-avatar';
import updateBanner from './api/update-banner';
import MkIndex from './views/pages/index.vue'; import MkIndex from './views/pages/index.vue';
import MkHome from './views/pages/home.vue'; import MkHome from './views/pages/home.vue';
import MkDeck from './views/pages/deck/deck.vue'; import MkDeck from './views/pages/deck/deck.vue';
@ -36,12 +28,131 @@ import MkTag from './views/pages/tag.vue';
import MkReversi from './views/pages/games/reversi.vue'; import MkReversi from './views/pages/games/reversi.vue';
import MkShare from './views/pages/share.vue'; import MkShare from './views/pages/share.vue';
import MkFollow from '../common/views/pages/follow.vue'; import MkFollow from '../common/views/pages/follow.vue';
import Ctx from './views/components/context-menu.vue';
import PostFormWindow from './views/components/post-form-window.vue';
import RenoteFormWindow from './views/components/renote-form-window.vue';
import MkChooseFileFromDriveWindow from './views/components/choose-file-from-drive-window.vue';
import MkChooseFolderFromDriveWindow from './views/components/choose-folder-from-drive-window.vue';
import Dialog from './views/components/dialog.vue';
import InputDialog from './views/components/input-dialog.vue';
import Notification from './views/components/ui-notification.vue';
import { url } from '../config';
import MiOS from '../mios'; import MiOS from '../mios';
/** /**
* init * init
*/ */
init(async (launch) => { init(async (launch) => {
Vue.mixin({
methods: {
$contextmenu(e, menu, opts?) {
const o = opts || {};
const vm = this.$root.new(Ctx, {
menu,
x: e.pageX - window.pageXOffset,
y: e.pageY - window.pageYOffset,
});
vm.$once('closed', () => {
if (o.closed) o.closed();
});
},
$post(opts) {
const o = opts || {};
if (o.renote) {
const vm = this.$root.new(RenoteFormWindow, {
note: o.renote,
animation: o.animation == null ? true : o.animation
});
if (o.cb) vm.$once('closed', o.cb);
} else {
const vm = this.$root.new(PostFormWindow, {
reply: o.reply,
animation: o.animation == null ? true : o.animation
});
if (o.cb) vm.$once('closed', o.cb);
}
},
$chooseDriveFile(opts) {
return new Promise((res, rej) => {
const o = opts || {};
if (document.body.clientWidth > 800) {
const w = this.$root.new(MkChooseFileFromDriveWindow, {
title: o.title,
multiple: o.multiple,
initFolder: o.currentFolder
});
w.$once('selected', file => {
res(file);
});
} else {
window['cb'] = file => {
res(file);
};
window.open(url + `/selectdrive?multiple=${o.multiple}`,
'choose_drive_window',
'height=500, width=800');
}
});
},
$chooseDriveFolder(opts) {
return new Promise((res, rej) => {
const o = opts || {};
const w = this.$root.new(MkChooseFolderFromDriveWindow, {
title: o.title,
initFolder: o.currentFolder
});
w.$once('selected', folder => {
res(folder);
});
});
},
$dialog(opts) {
return new Promise<string>((res, rej) => {
const o = opts || {};
const d = this.$root.new(Dialog, {
title: o.title,
text: o.text,
modal: o.modal,
buttons: o.actions
});
d.$once('clicked', id => {
res(id);
});
});
},
$input(opts) {
return new Promise<string>((res, rej) => {
const o = opts || {};
const d = this.$root.new(InputDialog, {
title: o.title,
placeholder: o.placeholder,
default: o.default,
type: o.type || 'text',
allowEmpty: o.allowEmpty
});
d.$once('done', text => {
res(text);
});
});
},
$notify(message) {
this.$root.new(Notification, {
message
});
}
}
});
// Register directives // Register directives
require('./views/directives'); require('./views/directives');
@ -75,22 +186,13 @@ init(async (launch) => {
}); });
// Launch the app // Launch the app
const [, os] = launch(router, os => ({ const [app, os] = launch(router);
chooseDriveFolder: chooseDriveFolder(os),
chooseDriveFile: chooseDriveFile(os),
dialog: dialog(os),
input: input(os),
post: post(os),
notify: notify(os),
updateAvatar: updateAvatar(os),
updateBanner: updateBanner(os)
}));
if (os.store.getters.isSignedIn) { if (os.store.getters.isSignedIn) {
/** /**
* Fuck AD Block * Fuck AD Block
*/ */
fuckAdBlock(os); fuckAdBlock(app);
} }
/** /**

View file

@ -35,7 +35,6 @@
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n'; import i18n from '../../../i18n';
import * as anime from 'animejs'; import * as anime from 'animejs';
import contextmenu from '../../api/contextmenu';
import copyToClipboard from '../../../common/scripts/copy-to-clipboard'; import copyToClipboard from '../../../common/scripts/copy-to-clipboard';
export default Vue.extend({ export default Vue.extend({
@ -70,7 +69,7 @@ export default Vue.extend({
onContextmenu(e) { onContextmenu(e) {
this.isContextmenuShowing = true; this.isContextmenuShowing = true;
contextmenu((this as any).os)(e, [{ this.$contextmenu(e, [{
type: 'item', type: 'item',
text: this.$t('contextmenu.rename'), text: this.$t('contextmenu.rename'),
icon: 'i-cursor', icon: 'i-cursor',
@ -149,7 +148,7 @@ export default Vue.extend({
}, },
rename() { rename() {
this.$root.apis.input({ this.$input({
title: this.$t('contextmenu.rename-file'), title: this.$t('contextmenu.rename-file'),
placeholder: this.$t('contextmenu.input-new-file-name'), placeholder: this.$t('contextmenu.input-new-file-name'),
default: this.file.name, default: this.file.name,
@ -171,7 +170,7 @@ export default Vue.extend({
copyUrl() { copyUrl() {
copyToClipboard(this.file.url); copyToClipboard(this.file.url);
this.$root.apis.dialog({ this.$dialog({
title: this.$t('contextmenu.copied'), title: this.$t('contextmenu.copied'),
text: this.$t('contextmenu.copied-url-to-clipboard'), text: this.$t('contextmenu.copied-url-to-clipboard'),
actions: [{ actions: [{
@ -181,11 +180,11 @@ export default Vue.extend({
}, },
setAsAvatar() { setAsAvatar() {
this.$root.apis.updateAvatar(this.file); this.$updateAvatar(this.file);
}, },
setAsBanner() { setAsBanner() {
this.$root.apis.updateBanner(this.file); this.$updateBanner(this.file);
}, },
addApp() { addApp() {

View file

@ -26,7 +26,6 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n'; import i18n from '../../../i18n';
import contextmenu from '../../api/contextmenu';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('desktop/views/components/drive.folder.vue'), i18n: i18n('desktop/views/components/drive.folder.vue'),
@ -54,7 +53,7 @@ export default Vue.extend({
onContextmenu(e) { onContextmenu(e) {
this.isContextmenuShowing = true; this.isContextmenuShowing = true;
contextmenu((this as any).os)(e, [{ this.$contextmenu(e, [{
type: 'item', type: 'item',
text: this.$t('contextmenu.move-to-this-folder'), text: this.$t('contextmenu.move-to-this-folder'),
icon: 'arrow-right', icon: 'arrow-right',
@ -156,7 +155,7 @@ export default Vue.extend({
}).catch(err => { }).catch(err => {
switch (err) { switch (err) {
case 'detected-circular-definition': case 'detected-circular-definition':
this.$root.apis.dialog({ this.$dialog({
title: this.$t('unable-to-process'), title: this.$t('unable-to-process'),
text: this.$t('circular-reference-detected'), text: this.$t('circular-reference-detected'),
actions: [{ actions: [{
@ -196,7 +195,7 @@ export default Vue.extend({
}, },
rename() { rename() {
this.$root.apis.input({ this.$input({
title: this.$t('contextmenu.rename-folder'), title: this.$t('contextmenu.rename-folder'),
placeholder: this.$t('contextmenu.input-new-folder-name'), placeholder: this.$t('contextmenu.input-new-folder-name'),
default: this.folder.name default: this.folder.name

View file

@ -65,7 +65,6 @@ import XNavFolder from './drive.nav-folder.vue';
import XFolder from './drive.folder.vue'; import XFolder from './drive.folder.vue';
import XFile from './drive.file.vue'; import XFile from './drive.file.vue';
import contains from '../../../common/scripts/contains'; import contains from '../../../common/scripts/contains';
import contextmenu from '../../api/contextmenu';
import { url } from '../../../config'; import { url } from '../../../config';
export default Vue.extend({ export default Vue.extend({
@ -137,7 +136,7 @@ export default Vue.extend({
}, },
methods: { methods: {
onContextmenu(e) { onContextmenu(e) {
contextmenu((this as any).os)(e, [{ this.$contextmenu(e, [{
type: 'item', type: 'item',
text: this.$t('contextmenu.create-folder'), text: this.$t('contextmenu.create-folder'),
icon: ['far', 'folder'], icon: ['far', 'folder'],
@ -314,7 +313,7 @@ export default Vue.extend({
}).catch(err => { }).catch(err => {
switch (err) { switch (err) {
case 'detected-circular-definition': case 'detected-circular-definition':
this.$root.apis.dialog({ this.$dialog({
title: this.$t('unable-to-process'), title: this.$t('unable-to-process'),
text: this.$t('circular-reference-detected'), text: this.$t('circular-reference-detected'),
actions: [{ actions: [{
@ -335,7 +334,7 @@ export default Vue.extend({
}, },
urlUpload() { urlUpload() {
this.$root.apis.input({ this.$input({
title: this.$t('url-upload'), title: this.$t('url-upload'),
placeholder: this.$t('url-of-file') placeholder: this.$t('url-of-file')
}).then(url => { }).then(url => {
@ -344,7 +343,7 @@ export default Vue.extend({
folderId: this.folder ? this.folder.id : undefined folderId: this.folder ? this.folder.id : undefined
}); });
this.$root.apis.dialog({ this.$dialog({
title: this.$t('url-upload-requested'), title: this.$t('url-upload-requested'),
text: this.$t('may-take-time'), text: this.$t('may-take-time'),
actions: [{ actions: [{
@ -355,7 +354,7 @@ export default Vue.extend({
}, },
createFolder() { createFolder() {
this.$root.apis.input({ this.$input({
title: this.$t('create-folder'), title: this.$t('create-folder'),
placeholder: this.$t('folder-name') placeholder: this.$t('folder-name')
}).then(name => { }).then(name => {

View file

@ -186,7 +186,7 @@ export default Vue.extend({
methods: { methods: {
hint() { hint() {
this.$root.apis.dialog({ this.$dialog({
title: this.$t('@.customization-tips.title'), title: this.$t('@.customization-tips.title'),
text: this.$t('@.customization-tips.paragraph'), text: this.$t('@.customization-tips.paragraph'),
actions: [{ actions: [{

View file

@ -245,7 +245,7 @@ export default Vue.extend({
}, },
chooseFileFromDrive() { chooseFileFromDrive() {
this.$root.apis.chooseDriveFile({ this.$chooseDriveFile({
multiple: true multiple: true
}).then(files => { }).then(files => {
files.forEach(this.attachMedia); files.forEach(this.attachMedia);
@ -363,7 +363,7 @@ export default Vue.extend({
}, },
addVisibleUser() { addVisibleUser() {
this.$root.apis.input({ this.$input({
title: this.$t('enter-username') title: this.$t('enter-username')
}).then(acct => { }).then(acct => {
if (acct.startsWith('@')) acct = acct.substr(1); if (acct.startsWith('@')) acct = acct.substr(1);
@ -401,13 +401,13 @@ export default Vue.extend({
this.clear(); this.clear();
this.deleteDraft(); this.deleteDraft();
this.$emit('posted'); this.$emit('posted');
this.$root.apis.notify(this.renote this.$notify(this.renote
? this.$t('reposted') ? this.$t('reposted')
: this.reply : this.reply
? this.$t('replied') ? this.$t('replied')
: this.$t('posted')); : this.$t('posted'));
}).catch(err => { }).catch(err => {
this.$root.apis.notify(this.renote this.$notify(this.renote
? this.$t('renote-failed') ? this.$t('renote-failed')
: this.reply : this.reply
? this.$t('reply-failed') ? this.$t('reply-failed')

View file

@ -34,9 +34,9 @@ export default Vue.extend({
renoteId: this.note.id renoteId: this.note.id
}).then(data => { }).then(data => {
this.$emit('posted'); this.$emit('posted');
this.$root.apis.notify(this.$t('success')); this.$notify(this.$t('success'));
}).catch(err => { }).catch(err => {
this.$root.apis.notify(this.$t('failure')); this.$notify(this.$t('failure'));
}).then(() => { }).then(() => {
this.wait = false; this.wait = false;
}); });

View file

@ -35,7 +35,7 @@ export default Vue.extend({
}, },
methods: { methods: {
register() { register() {
this.$root.apis.input({ this.$input({
title: this.$t('enter-password'), title: this.$t('enter-password'),
type: 'password' type: 'password'
}).then(password => { }).then(password => {
@ -48,14 +48,14 @@ export default Vue.extend({
}, },
unregister() { unregister() {
this.$root.apis.input({ this.$input({
title: this.$t('enter-password'), title: this.$t('enter-password'),
type: 'password' type: 'password'
}).then(password => { }).then(password => {
this.$root.api('i/2fa/unregister', { this.$root.api('i/2fa/unregister', {
password: password password: password
}).then(() => { }).then(() => {
this.$root.apis.notify(this.$t('unregistered')); this.$notify(this.$t('unregistered'));
this.$store.state.i.twoFactorEnabled = false; this.$store.state.i.twoFactorEnabled = false;
}); });
}); });
@ -65,10 +65,10 @@ export default Vue.extend({
this.$root.api('i/2fa/done', { this.$root.api('i/2fa/done', {
token: this.token token: this.token
}).then(() => { }).then(() => {
this.$root.apis.notify(this.$t('success')); this.$notify(this.$t('success'));
this.$store.state.i.twoFactorEnabled = true; this.$store.state.i.twoFactorEnabled = true;
}).catch(() => { }).catch(() => {
this.$root.apis.notify(this.$t('failed')); this.$notify(this.$t('failed'));
}); });
} }
} }

View file

@ -513,7 +513,7 @@ export default Vue.extend({
this.$emit('done'); this.$emit('done');
}, },
updateWallpaper() { updateWallpaper() {
this.$root.apis.chooseDriveFile({ this.$chooseDriveFile({
multiple: false multiple: false
}).then(file => { }).then(file => {
this.$root.api('i/update', { this.$root.api('i/update', {
@ -533,16 +533,16 @@ export default Vue.extend({
}, },
checkForUpdate() { checkForUpdate() {
this.checkingForUpdate = true; this.checkingForUpdate = true;
checkForUpdate((this as any).os, true, true).then(newer => { checkForUpdate(this.$root, true, true).then(newer => {
this.checkingForUpdate = false; this.checkingForUpdate = false;
this.latestVersion = newer; this.latestVersion = newer;
if (newer == null) { if (newer == null) {
this.$root.apis.dialog({ this.$dialog({
title: this.$t('no-updates'), title: this.$t('no-updates'),
text: this.$t('no-updates-desc') text: this.$t('no-updates-desc')
}); });
} else { } else {
this.$root.apis.dialog({ this.$dialog({
title: this.$t('update-available'), title: this.$t('update-available'),
text: this.$t('update-available-desc') text: this.$t('update-available-desc')
}); });
@ -551,7 +551,7 @@ export default Vue.extend({
}, },
clean() { clean() {
localStorage.clear(); localStorage.clear();
this.$root.apis.dialog({ this.$dialog({
title: this.$t('cache-cleared'), title: this.$t('cache-cleared'),
text: this.$t('cache-cleared-desc') text: this.$t('cache-cleared-desc')
}); });

View file

@ -109,7 +109,7 @@ export default Vue.extend({
icon: 'plus', icon: 'plus',
text: this.$t('add-list'), text: this.$t('add-list'),
action: () => { action: () => {
this.$root.apis.input({ this.$input({
title: this.$t('list-name'), title: this.$t('list-name'),
}).then(async title => { }).then(async title => {
const list = await this.$root.api('users/lists/create', { const list = await this.$root.api('users/lists/create', {

View file

@ -12,7 +12,7 @@ export default Vue.extend({
i18n: i18n('desktop/views/components/ui.header.post.vue'), i18n: i18n('desktop/views/components/ui.header.post.vue'),
methods: { methods: {
post() { post() {
this.$root.apis.post(); this.$post();
} }
} }
}); });

View file

@ -139,7 +139,7 @@ export default Vue.extend({
}, },
post() { post() {
this.$root.apis.post(); this.$post();
}, },
drive() { drive() {

View file

@ -67,7 +67,7 @@ export default Vue.extend({
methods: { methods: {
post() { post() {
this.$root.apis.post(); this.$post();
}, },
toggleZenMode() { toggleZenMode() {

View file

@ -29,7 +29,7 @@ export default Vue.extend({
}, },
methods: { methods: {
add() { add() {
this.$root.apis.input({ this.$input({
title: this.$t('list-name'), title: this.$t('list-name'),
}).then(async title => { }).then(async title => {
const list = await this.$root.api('users/lists/create', { const list = await this.$root.api('users/lists/create', {

View file

@ -29,7 +29,6 @@
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../../i18n'; import i18n from '../../../../i18n';
import Menu from '../../../../common/views/components/menu.vue'; import Menu from '../../../../common/views/components/menu.vue';
import contextmenu from '../../../api/contextmenu';
import { countIf } from '../../../../../../prelude/array'; import { countIf } from '../../../../../../prelude/array';
export default Vue.extend({ export default Vue.extend({
@ -168,7 +167,7 @@ export default Vue.extend({
icon: 'pencil-alt', icon: 'pencil-alt',
text: this.$t('rename'), text: this.$t('rename'),
action: () => { action: () => {
this.$root.apis.input({ this.$input({
title: this.$t('rename'), title: this.$t('rename'),
default: this.name, default: this.name,
allowEmpty: false allowEmpty: false
@ -230,7 +229,7 @@ export default Vue.extend({
onContextmenu(e) { onContextmenu(e) {
if (this.isTemporaryColumn) return; if (this.isTemporaryColumn) return;
contextmenu((this as any).os)(e, this.getMenu()); this.$contextmenu(e, this.getMenu());
}, },
showMenu() { showMenu() {

View file

@ -255,7 +255,7 @@ export default Vue.extend({
icon: 'hashtag', icon: 'hashtag',
text: this.$t('@deck.hashtag'), text: this.$t('@deck.hashtag'),
action: () => { action: () => {
this.$root.apis.input({ this.$input({
title: this.$t('enter-hashtag-tl-title') title: this.$t('enter-hashtag-tl-title')
}).then(title => { }).then(title => {
this.$store.dispatch('settings/addDeckColumn', { this.$store.dispatch('settings/addDeckColumn', {

View file

@ -81,7 +81,7 @@ export default Vue.extend({
onBannerClick() { onBannerClick() {
if (!this.$store.getters.isSignedIn || this.$store.state.i.id != this.user.id) return; if (!this.$store.getters.isSignedIn || this.$store.state.i.id != this.user.id) return;
this.$root.apis.updateBanner().then(i => { this.$updateBanner().then(i => {
this.user.bannerUrl = i.bannerUrl; this.user.bannerUrl = i.bannerUrl;
}); });
} }

View file

@ -101,7 +101,7 @@ export default Vue.extend({
listId: list.id, listId: list.id,
userId: this.user.id userId: this.user.id
}); });
this.$root.apis.dialog({ this.$dialog({
title: 'Done!', title: 'Done!',
text: this.$t('list-pushed').replace('{user}', this.user.name).replace('{list}', list.title) text: this.$t('list-pushed').replace('{user}', this.user.name).replace('{list}', list.title)
}); });

View file

@ -13,7 +13,7 @@ import VueI18n from 'vue-i18n';
import VueHotkey from './common/hotkey'; import VueHotkey from './common/hotkey';
import App from './app.vue'; import App from './app.vue';
import checkForUpdate from './common/scripts/check-for-update'; import checkForUpdate from './common/scripts/check-for-update';
import MiOS, { API } from './mios'; import MiOS from './mios';
import { clientVersion as version, codename, lang } from './config'; import { clientVersion as version, codename, lang } from './config';
import { builtinThemes, lightTheme, applyTheme } from './theme'; import { builtinThemes, lightTheme, applyTheme } from './theme';
@ -180,16 +180,14 @@ if (localStorage.getItem('should-refresh') == 'true') {
} }
// MiOSを初期化してコールバックする // MiOSを初期化してコールバックする
export default (callback: (launch: (router: VueRouter, api?: (os: MiOS) => API) => [Vue, MiOS]) => void, sw = false) => { export default (callback: (launch: (router: VueRouter) => [Vue, MiOS]) => void, sw = false) => {
const os = new MiOS(sw); const os = new MiOS(sw);
os.init(() => { os.init(() => {
// アプリ基底要素マウント // アプリ基底要素マウント
document.body.innerHTML = '<div id="app"></div>'; document.body.innerHTML = '<div id="app"></div>';
const launch = (router: VueRouter, api?: (os: MiOS) => API) => { const launch = (router: VueRouter) => {
os.apis = api ? api(os) : null;
//#region theme //#region theme
os.store.watch(s => { os.store.watch(s => {
return s.device.darkmode; return s.device.darkmode;
@ -285,7 +283,6 @@ export default (callback: (launch: (router: VueRouter, api?: (os: MiOS) => API)
windows: os.windows windows: os.windows
}, },
stream: os.stream, stream: os.stream,
apis: os.apis,
instanceName: os.instanceName instanceName: os.instanceName
}; };
}, },
@ -293,7 +290,14 @@ export default (callback: (launch: (router: VueRouter, api?: (os: MiOS) => API)
api: os.api, api: os.api,
getMeta: os.getMeta, getMeta: os.getMeta,
getMetaSync: os.getMetaSync, getMetaSync: os.getMetaSync,
new: os.new, new(vm, props) {
const x = new vm({
parent: this,
propsData: props
}).$mount();
document.body.appendChild(x.$el);
return x;
},
}, },
router, router,
render: createEl => createEl(App) render: createEl => createEl(App)
@ -304,18 +308,18 @@ export default (callback: (launch: (router: VueRouter, api?: (os: MiOS) => API)
// マウント // マウント
app.$mount('#app'); app.$mount('#app');
return [app, os] as [Vue, MiOS];
};
callback(launch);
//#region 更新チェック //#region 更新チェック
const preventUpdate = os.store.state.device.preventUpdate; const preventUpdate = os.store.state.device.preventUpdate;
if (!preventUpdate) { if (!preventUpdate) {
setTimeout(() => { setTimeout(() => {
checkForUpdate(os); checkForUpdate(app);
}, 3000); }, 3000);
} }
//#endregion //#endregion
return [app, os] as [Vue, MiOS];
};
callback(launch);
}); });
}; };

View file

@ -15,41 +15,6 @@ let spinner = null;
let pending = 0; let pending = 0;
//#endregion //#endregion
export type API = {
chooseDriveFile: (opts: {
title?: string;
currentFolder?: any;
multiple?: boolean;
}) => Promise<any>;
chooseDriveFolder: (opts: {
title?: string;
currentFolder?: 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>;
post: (opts?: {
reply?: any;
renote?: any;
}) => void;
notify: (message: string) => void;
};
/** /**
* Misskey Operating System * Misskey Operating System
*/ */
@ -70,16 +35,6 @@ export default class MiOS extends EventEmitter {
public app: Vue; public app: Vue;
@autobind
public new(vm, props) {
const x = new vm({
parent: this.app,
propsData: props
}).$mount();
document.body.appendChild(x.$el);
return x;
}
/** /**
* Whether is debug mode * Whether is debug mode
*/ */
@ -89,8 +44,6 @@ export default class MiOS extends EventEmitter {
public store: ReturnType<typeof initStore>; public store: ReturnType<typeof initStore>;
public apis: API;
/** /**
* A connection manager of home stream * A connection manager of home stream
*/ */

View file

@ -1,18 +0,0 @@
import Chooser from '../views/components/drive-file-chooser.vue';
export default function(opts) {
return new Promise((res, rej) => {
const o = opts || {};
const w = new Chooser({
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,17 +0,0 @@
import Chooser from '../views/components/drive-folder-chooser.vue';
export default function(opts) {
return new Promise((res, rej) => {
const o = opts || {};
const w = new Chooser({
propsData: {
title: o.title,
initFolder: o.currentFolder
}
}).$mount();
w.$once('selected', folder => {
res(folder);
});
document.body.appendChild(w.$el);
});
}

View file

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

View file

@ -1,8 +0,0 @@
export default function(opts) {
return new Promise<string>((res, rej) => {
const x = window.prompt(opts.title);
if (x) {
res(x);
}
});
}

View file

@ -1,3 +0,0 @@
export default function(message) {
alert(message);
}

View file

@ -1,24 +0,0 @@
import PostForm from '../views/components/post-form-dialog.vue';
export default (os) => (opts) => {
const o = opts || {};
document.documentElement.style.overflow = 'hidden';
function recover() {
document.documentElement.style.overflow = 'auto';
}
const vm = new PostForm({
parent: os.app,
propsData: {
reply: o.reply,
renote: o.renote
}
}).$mount();
vm.$once('cancel', recover);
vm.$once('posted', recover);
if (o.cb) vm.$once('closed', o.cb);
document.body.appendChild(vm.$el);
(vm as any).focus();
};

View file

@ -2,6 +2,7 @@
* Mobile Client * Mobile Client
*/ */
import Vue from 'vue';
import VueRouter from 'vue-router'; import VueRouter from 'vue-router';
// Style // Style
@ -9,13 +10,6 @@ import './style.styl';
import init from '../init'; import init from '../init';
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 post from './api/post';
import notify from './api/notify';
import MkIndex from './views/pages/index.vue'; import MkIndex from './views/pages/index.vue';
import MkSignup from './views/pages/signup.vue'; import MkSignup from './views/pages/signup.vue';
import MkUser from './views/pages/user.vue'; import MkUser from './views/pages/user.vue';
@ -39,10 +33,94 @@ import MkTag from './views/pages/tag.vue';
import MkShare from './views/pages/share.vue'; import MkShare from './views/pages/share.vue';
import MkFollow from '../common/views/pages/follow.vue'; import MkFollow from '../common/views/pages/follow.vue';
import PostForm from './views/components/post-form-dialog.vue';
import FileChooser from './views/components/drive-file-chooser.vue';
import FolderChooser from './views/components/drive-folder-chooser.vue';
import Dialog from './views/components/dialog.vue';
/** /**
* init * init
*/ */
init((launch) => { init((launch) => {
Vue.mixin({
methods: {
$post(opts) {
const o = opts || {};
document.documentElement.style.overflow = 'hidden';
function recover() {
document.documentElement.style.overflow = 'auto';
}
const vm = this.$root.new(PostForm, {
reply: o.reply,
renote: o.renote
});
vm.$once('cancel', recover);
vm.$once('posted', recover);
if (o.cb) vm.$once('closed', o.cb);
(vm as any).focus();
},
$chooseDriveFile(opts) {
return new Promise((res, rej) => {
const o = opts || {};
const vm = this.$root.new(FileChooser, {
title: o.title,
multiple: o.multiple,
initFolder: o.currentFolder
});
vm.$once('selected', file => {
res(file);
});
});
},
$chooseDriveFolder(opts) {
return new Promise((res, rej) => {
const o = opts || {};
const vm = this.$root.new(FolderChooser, {
title: o.title,
initFolder: o.currentFolder
});
vm.$once('selected', folder => {
res(folder);
});
});
},
$input(opts) {
return new Promise<string>((res, rej) => {
const x = window.prompt(opts.title);
if (x) {
res(x);
}
});
},
$dialog(opts) {
return new Promise<string>((res, rej) => {
const o = opts || {};
const d = this.$root.new(Dialog, {
title: o.title,
text: o.text,
modal: o.modal,
buttons: o.actions
});
d.$once('clicked', id => {
res(id);
});
});
},
$notify(message) {
alert(message);
}
}
});
// Register directives // Register directives
require('./views/directives'); require('./views/directives');
@ -85,12 +163,5 @@ init((launch) => {
}); });
// Launch the app // Launch the app
launch(router, os => ({ launch(router);
chooseDriveFolder,
chooseDriveFile,
dialog: dialog(os),
input,
post: post(os),
notify
}));
}, true); }, true);

View file

@ -101,7 +101,7 @@ export default Vue.extend({
}, },
move() { move() {
this.$root.apis.chooseDriveFolder().then(folder => { this.$chooseDriveFolder().then(folder => {
this.$root.api('drive/files/update', { this.$root.api('drive/files/update', {
fileId: this.file.id, fileId: this.file.id,
folderId: folder == null ? null : folder.id folderId: folder == null ? null : folder.id

View file

@ -439,7 +439,7 @@ export default Vue.extend({
alert(this.$t('root-move-alert')); alert(this.$t('root-move-alert'));
return; return;
} }
this.$root.apis.chooseDriveFolder().then(folder => { this.$chooseDriveFolder().then(folder => {
this.$root.api('drive/folders/update', { this.$root.api('drive/folders/update', {
parentId: folder ? folder.id : null, parentId: folder ? folder.id : null,
folderId: this.folder.id folderId: this.folder.id

View file

@ -196,13 +196,13 @@ export default Vue.extend({
}, },
reply() { reply() {
this.$root.apis.post({ this.$post({
reply: this.p reply: this.p
}); });
}, },
renote() { renote() {
this.$root.apis.post({ this.$post({
renote: this.p renote: this.p
}); });
}, },

View file

@ -220,7 +220,7 @@ export default Vue.extend({
}, },
chooseFileFromDrive() { chooseFileFromDrive() {
this.$root.apis.chooseDriveFile({ this.$chooseDriveFile({
multiple: true multiple: true
}).then(files => { }).then(files => {
files.forEach(this.attachMedia); files.forEach(this.attachMedia);
@ -279,7 +279,7 @@ export default Vue.extend({
}, },
addVisibleUser() { addVisibleUser() {
this.$root.apis.input({ this.$input({
title: this.$t('username-prompt') title: this.$t('username-prompt')
}).then(acct => { }).then(acct => {
if (acct.startsWith('@')) acct = acct.substr(1); if (acct.startsWith('@')) acct = acct.substr(1);

View file

@ -7,7 +7,7 @@
<button class="nav" @click="$parent.isDrawerOpening = true"><fa icon="bars"/></button> <button class="nav" @click="$parent.isDrawerOpening = true"><fa icon="bars"/></button>
<i v-if="hasUnreadNotification || hasUnreadMessagingMessage || hasGameInvitation" class="circle"><fa icon="circle"/></i> <i v-if="hasUnreadNotification || hasUnreadMessagingMessage || hasGameInvitation" class="circle"><fa icon="circle"/></i>
<h1> <h1>
<slot>{{ os.instanceName }}</slot> <slot>{{ $root.instanceName }}</slot>
</h1> </h1>
<slot name="func"></slot> <slot name="func"></slot>
</div> </div>

View file

@ -139,7 +139,7 @@ export default Vue.extend({
methods: { methods: {
fn() { fn() {
this.$root.apis.post(); this.$post();
}, },
saveSrc() { saveSrc() {

View file

@ -339,16 +339,16 @@ export default Vue.extend({
checkForUpdate() { checkForUpdate() {
this.checkingForUpdate = true; this.checkingForUpdate = true;
checkForUpdate((this as any).os, true, true).then(newer => { checkForUpdate(this.$root, true, true).then(newer => {
this.checkingForUpdate = false; this.checkingForUpdate = false;
this.latestVersion = newer; this.latestVersion = newer;
if (newer == null) { if (newer == null) {
this.$root.apis.dialog({ this.$dialog({
title: this.$t('no-updates'), title: this.$t('no-updates'),
text: this.$t('no-updates-desc') text: this.$t('no-updates-desc')
}); });
} else { } else {
this.$root.apis.dialog({ this.$dialog({
title: this.$t('update-available'), title: this.$t('update-available'),
text: this.$t('update-available-desc') text: this.$t('update-available-desc')
}); });

View file

@ -38,7 +38,7 @@ export default Vue.extend({
}, },
methods: { methods: {
fn() { fn() {
this.$root.apis.input({ this.$input({
title: this.$t('enter-list-name'), title: this.$t('enter-list-name'),
}).then(async title => { }).then(async title => {
const list = await this.$root.api('users/lists/create', { const list = await this.$root.api('users/lists/create', {