This commit is contained in:
syuilo 2020-09-07 14:19:53 +09:00
parent 02701d852d
commit cd79314f69
38 changed files with 240 additions and 317 deletions

View File

@ -376,7 +376,7 @@ export default defineComponent({
chooseUser() { chooseUser() {
this.close(); this.close();
const vm = os.popup(MkUserSelect, {}); const vm = os.modal(MkUserSelect, {});
vm.$once('selected', user => { vm.$once('selected', user => {
this.complete('user', user); this.complete('user', user);
}); });

View File

@ -43,7 +43,7 @@ export default defineComponent({
icon: faCog, icon: faCog,
text: this.$t('notificationSetting'), text: this.$t('notificationSetting'),
action: async () => { action: async () => {
os.popup(await import('../notification-setting-window.vue'), { os.modal(await import('../notification-setting-window.vue'), {
includingTypes: this.column.includingTypes, includingTypes: this.column.includingTypes,
}).$on('ok', async ({ includingTypes }) => { }).$on('ok', async ({ includingTypes }) => {
this.$set(this.column, 'includingTypes', includingTypes); this.$set(this.column, 'includingTypes', includingTypes);

View File

@ -1,5 +1,4 @@
<template> <template>
<x-modal @closed="$emit('closed')" @click="onBgClick" :showing="showing">
<div class="mk-dialog" :class="{ iconOnly }"> <div class="mk-dialog" :class="{ iconOnly }">
<template v-if="type == 'signin'"> <template v-if="type == 'signin'">
<mk-signin/> <mk-signin/>
@ -40,7 +39,6 @@
</div> </div>
</template> </template>
</div> </div>
</x-modal>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -52,12 +50,10 @@ import MkInput from './ui/input.vue';
import MkSelect from './ui/select.vue'; import MkSelect from './ui/select.vue';
import MkSignin from './signin.vue'; import MkSignin from './signin.vue';
import parseAcct from '../../misc/acct/parse'; import parseAcct from '../../misc/acct/parse';
import XModal from './modal.vue';
import * as os from '@/os'; import * as os from '@/os';
export default defineComponent({ export default defineComponent({
components: { components: {
XModal,
MkButton, MkButton,
MkInput, MkInput,
MkSelect, MkSelect,
@ -65,9 +61,6 @@ export default defineComponent({
}, },
props: { props: {
showing: {
required: true
},
type: { type: {
type: String, type: String,
required: false, required: false,
@ -118,7 +111,7 @@ export default defineComponent({
}, },
}, },
emits: ['done', 'closed'], emits: ['done'],
data() { data() {
return { return {

View File

@ -72,7 +72,7 @@ export default defineComponent({
if (this.$store.state.device.imageNewTab) { if (this.$store.state.device.imageNewTab) {
window.open(this.image.url, '_blank'); window.open(this.image.url, '_blank');
} else { } else {
const viewer = os.popup(ImageViewer, { const viewer = os.modal(ImageViewer, {
image: this.image image: this.image
}); });
this.$once('hook:beforeDestroy', () => { this.$once('hook:beforeDestroy', () => {

View File

@ -1,5 +1,4 @@
<template> <template>
<x-modal :source="source" :no-center="noCenter" @click="close()" @closed="$emit('closed')" :showing="showing">
<div class="rrevdjwt" :class="{ left: align === 'left' }" ref="items" :style="{ width: width + 'px' }"> <div class="rrevdjwt" :class="{ left: align === 'left' }" ref="items" :style="{ width: width + 'px' }">
<template v-for="(item, i) in items.filter(item => item !== undefined)"> <template v-for="(item, i) in items.filter(item => item !== undefined)">
<div v-if="item === null" class="divider" :key="i"></div> <div v-if="item === null" class="divider" :key="i"></div>
@ -29,27 +28,16 @@
</button> </button>
</template> </template>
</div> </div>
</x-modal>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { faCircle } from '@fortawesome/free-solid-svg-icons'; import { faCircle } from '@fortawesome/free-solid-svg-icons';
import XModal from './modal.vue';
import { focusPrev, focusNext } from '@/scripts/focus'; import { focusPrev, focusNext } from '@/scripts/focus';
import * as os from '@/os'; import * as os from '@/os';
export default defineComponent({ export default defineComponent({
components: {
XModal
},
props: { props: {
showing: {
required: true
},
source: {
required: true
},
items: { items: {
type: Array, type: Array,
required: true required: true

View File

@ -18,6 +18,9 @@ import * as os from '@/os';
// memo: popup.vuefixedsource辿 // memo: popup.vuefixedsource辿
export default defineComponent({ export default defineComponent({
provide: {
modal: true
},
props: { props: {
showing: { showing: {
type: Boolean, type: Boolean,
@ -59,6 +62,7 @@ export default defineComponent({
const popover = this.$refs.content as any; const popover = this.$refs.content as any;
new ResizeObserver((entries, observer) => {
const rect = this.source.getBoundingClientRect(); const rect = this.source.getBoundingClientRect();
const width = popover.offsetWidth; const width = popover.offsetWidth;
const height = popover.offsetHeight; const height = popover.offsetHeight;
@ -111,6 +115,7 @@ export default defineComponent({
popover.style.left = left + 'px'; popover.style.left = left + 'px';
popover.style.top = top + 'px'; popover.style.top = top + 'px';
}).observe(popover);
}); });
}, },
}); });

View File

@ -96,7 +96,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineAsyncComponent, defineComponent } from 'vue';
import { faSatelliteDish, faBolt, faTimes, faBullhorn, faStar, faLink, faExternalLinkSquareAlt, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faQuoteRight, faInfoCircle, faBiohazard, faPlug } from '@fortawesome/free-solid-svg-icons'; import { faSatelliteDish, faBolt, faTimes, faBullhorn, faStar, faLink, faExternalLinkSquareAlt, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faQuoteRight, faInfoCircle, faBiohazard, faPlug } from '@fortawesome/free-solid-svg-icons';
import { faCopy, faTrashAlt, faEdit, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons'; import { faCopy, faTrashAlt, faEdit, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
import { parse } from '../../mfm/parse'; import { parse } from '../../mfm/parse';
@ -108,8 +108,6 @@ import XReactionsViewer from './reactions-viewer.vue';
import XMediaList from './media-list.vue'; import XMediaList from './media-list.vue';
import XCwButton from './cw-button.vue'; import XCwButton from './cw-button.vue';
import XPoll from './poll.vue'; import XPoll from './poll.vue';
import MkUrlPreview from './url-preview.vue';
import MkReactionPicker from './reaction-picker.vue';
import { pleaseLogin } from '@/scripts/please-login'; import { pleaseLogin } from '@/scripts/please-login';
import { focusPrev, focusNext } from '@/scripts/focus'; import { focusPrev, focusNext } from '@/scripts/focus';
import { url } from '@/config'; import { url } from '@/config';
@ -133,7 +131,7 @@ export default defineComponent({
XMediaList, XMediaList,
XCwButton, XCwButton,
XPoll, XPoll,
MkUrlPreview, MkUrlPreview: defineAsyncComponent(() => import('@/components/url-preview.vue')),
}, },
inject: { inject: {
@ -485,17 +483,17 @@ export default defineComponent({
react(viaKeyboard = false) { react(viaKeyboard = false) {
pleaseLogin(); pleaseLogin();
this.blur(); this.blur();
const close = os.popup(MkReactionPicker, { os.modal(defineAsyncComponent(() => import('@/components/reaction-picker.vue')), {
source: this.$refs.reactButton,
showFocus: viaKeyboard, showFocus: viaKeyboard,
}, reaction => { }, reaction => {
os.api('notes/reactions/create', { os.api('notes/reactions/create', {
noteId: this.appearNote.id, noteId: this.appearNote.id,
reaction: reaction reaction: reaction
}).then(() => {
close();
}); });
}, this.focus); this.focus();
}, {
source: this.$refs.reactButton
});
}, },
reactDirectly(reaction) { reactDirectly(reaction) {

View File

@ -32,12 +32,11 @@ import { defineComponent } from 'vue';
import paging from '@/scripts/paging'; import paging from '@/scripts/paging';
import XNote from './note.vue'; import XNote from './note.vue';
import XList from './date-separated-list.vue'; import XList from './date-separated-list.vue';
import MkButton from './ui/button.vue';
import * as os from '@/os'; import * as os from '@/os';
export default defineComponent({ export default defineComponent({
components: { components: {
XNote, XList, MkButton XNote, XList,
}, },
mixins: [ mixins: [
@ -83,9 +82,9 @@ export default defineComponent({
updated(oldValue, newValue) { updated(oldValue, newValue) {
const i = this.notes.findIndex(n => n === oldValue); const i = this.notes.findIndex(n => n === oldValue);
if (this.prop) { if (this.prop) {
Vue.set(this.items[i], this.prop, newValue); this.items[i][this.prop] = newValue;
} else { } else {
Vue.set(this.items, i, newValue); this.items[i] = newValue;
} }
}, },

View File

@ -57,9 +57,6 @@ import * as os from '@/os';
export default defineComponent({ export default defineComponent({
props: { props: {
destroy: {
required: true
},
x: { x: {
type: Number, type: Number,
required: true required: true
@ -94,7 +91,7 @@ export default defineComponent({
}, },
mounted() { mounted() {
setTimeout(() => { setTimeout(() => {
this.destroy(); this.$emit('closed');
}, 1100); }, 1100);
} }
}); });

View File

@ -1,94 +0,0 @@
<template>
<x-modal @closed="$emit('closed')" @click="onBgClick" :showing="showing">
<x-post-form ref="form" class="ulveipgl"
:reply="reply"
:renote="renote"
:mention="mention"
:specified="specified"
:initial-text="initialText"
:initial-note="initialNote"
:instant="instant"
@posted="onPosted"
@cancel="onCanceled"
/>
</x-modal>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import XModal from './modal.vue';
import XPostForm from './post-form.vue';
import * as os from '@/os';
export default defineComponent({
components: {
XModal,
XPostForm,
},
props: {
showing: {
required: true
},
reply: {
type: Object,
required: false
},
renote: {
type: Object,
required: false
},
mention: {
type: Object,
required: false
},
specified: {
type: Object,
required: false
},
initialText: {
type: String,
required: false
},
initialNote: {
type: Object,
required: false
},
instant: {
type: Boolean,
required: false,
default: false
}
},
methods: {
focus() {
this.$refs.form.focus();
},
onPosted() {
this.$emit('done', 'posted');
},
onCanceled() {
this.$emit('done', 'canceled');
},
onKeydown(e) {
if (e.which === 27) { // Esc
e.preventDefault();
e.stopPropagation();
this.$emit('done', 'canceled');
}
},
}
});
</script>
<style lang="scss" scoped>
.ulveipgl {
width: 100%;
max-width: 500px;
border-radius: var(--radius);
}
</style>

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="gafaadew" <div class="gafaadew" :class="{ modal }"
@dragover.stop="onDragover" @dragover.stop="onDragover"
@dragenter="onDragenter" @dragenter="onDragenter"
@dragleave="onDragleave" @dragleave="onDragleave"
@ -80,6 +80,8 @@ export default defineComponent({
XPollEditor: defineAsyncComponent(() => import('./poll-editor.vue')) XPollEditor: defineAsyncComponent(() => import('./poll-editor.vue'))
}, },
inject: ['modal'],
props: { props: {
reply: { reply: {
type: Object, type: Object,
@ -417,7 +419,7 @@ export default defineComponent({
// TODO: information dialog // TODO: information dialog
return; return;
} }
const w = os.popup(MkVisibilityChooser, { const w = os.modal(MkVisibilityChooser, {
source: this.$refs.visibilityButton, source: this.$refs.visibilityButton,
currentVisibility: this.visibility, currentVisibility: this.visibility,
currentLocalOnly: this.localOnly currentLocalOnly: this.localOnly
@ -433,7 +435,7 @@ export default defineComponent({
}, },
addVisibleUser() { addVisibleUser() {
const vm = os.popup(MkUserSelect, {}); const vm = os.modal(MkUserSelect, {});
vm.$once('selected', user => { vm.$once('selected', user => {
this.visibleUsers.push(user); this.visibleUsers.push(user);
}); });
@ -593,18 +595,18 @@ export default defineComponent({
}, },
cancel() { cancel() {
this.$emit('cancel'); this.$emit('done');
}, },
insertMention() { insertMention() {
const vm = os.popup(MkUserSelect, {}); const vm = os.modal(MkUserSelect, {});
vm.$once('selected', user => { vm.$once('selected', user => {
insertTextAtCursor(this.$refs.text, getAcct(user) + ' '); insertTextAtCursor(this.$refs.text, getAcct(user) + ' ');
}); });
}, },
async insertEmoji(ev) { async insertEmoji(ev) {
const vm = os.popup(await import('./emoji-picker.vue'), { const vm = os.modal(await import('./emoji-picker.vue'), {
source: ev.currentTarget || ev.target source: ev.currentTarget || ev.target
}).$once('chosen', emoji => { }).$once('chosen', emoji => {
insertTextAtCursor(this.$refs.text, emoji); insertTextAtCursor(this.$refs.text, emoji);
@ -636,6 +638,12 @@ export default defineComponent({
position: relative; position: relative;
background: var(--panel); background: var(--panel);
&.modal {
width: 100%;
max-width: 500px;
border-radius: var(--radius);
}
> header { > header {
z-index: 1000; z-index: 1000;
height: 66px; height: 66px;

View File

@ -1,36 +1,24 @@
<template> <template>
<XModal :source="source" @closed="$emit('closed')" :showing="showing" @click="close" v-hotkey.global="keymap">
<div class="rdfaahpb"> <div class="rdfaahpb">
<div class="buttons" ref="buttons" :class="{ showFocus }"> <div class="buttons" ref="buttons" :class="{ showFocus }">
<button class="_button" v-for="(reaction, i) in rs" :key="reaction" @click="react(reaction)" :tabindex="i + 1" :title="reaction" v-particle><x-reaction-icon :reaction="reaction"/></button> <button class="_button" v-for="(reaction, i) in rs" :key="reaction" @click="react(reaction)" :tabindex="i + 1" :title="reaction" v-particle><x-reaction-icon :reaction="reaction"/></button>
</div> </div>
<input class="text" v-model.trim="text" :placeholder="$t('enterEmoji')" @keyup.enter="reactText" @input="tryReactText" v-autocomplete="{ model: 'text' }"> <input class="text" v-model.trim="text" :placeholder="$t('enterEmoji')" @keyup.enter="reactText" @input="tryReactText" v-autocomplete="{ model: 'text' }">
</div> </div>
</XModal>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { emojiRegex } from '../../misc/emoji-regex'; import { emojiRegex } from '../../misc/emoji-regex';
import XReactionIcon from './reaction-icon.vue'; import XReactionIcon from './reaction-icon.vue';
import XModal from './modal.vue';
import * as os from '@/os'; import * as os from '@/os';
export default defineComponent({ export default defineComponent({
components: { components: {
XModal,
XReactionIcon, XReactionIcon,
}, },
props: { props: {
showing: {
required: true
},
source: {
required: true
},
reactions: { reactions: {
required: false required: false
}, },

View File

@ -111,7 +111,7 @@ export default defineComponent({
this.closeDetails(); this.closeDetails();
if (!this.isHovering) return; if (!this.isHovering) return;
this.details = os.popup(XDetails, { this.details = os.modal(XDetails, {
reaction: this.reaction, reaction: this.reaction,
users, users,
count: this.count, count: this.count,

View File

@ -276,7 +276,7 @@ export default defineComponent({
}, },
async addAcount() { async addAcount() {
os.popup(await import('./signin-dialog.vue')).$once('login', res => { os.modal(await import('./signin-dialog.vue')).$once('login', res => {
this.$store.dispatch('addAcount', res); this.$store.dispatch('addAcount', res);
os.dialog({ os.dialog({
type: 'success', type: 'success',
@ -286,7 +286,7 @@ export default defineComponent({
}, },
async createAccount() { async createAccount() {
os.popup(await import('./signup-dialog.vue')).$once('signup', res => { os.modal(await import('./signup-dialog.vue')).$once('signup', res => {
this.$store.dispatch('addAcount', res); this.$store.dispatch('addAcount', res);
this.switchAccountWithToken(res.i); this.switchAccountWithToken(res.i);
}); });

View File

@ -166,7 +166,7 @@ export default defineComponent({
id: notification.id id: notification.id
}); });
os.popup(await import('@/components/toast.vue'), { os.modal(await import('@/components/toast.vue'), {
notification notification
}); });
} }

View File

@ -335,7 +335,7 @@ export default defineComponent({
id: notification.id id: notification.id
}); });
os.popup(await import('@/components/toast.vue'), { os.modal(await import('@/components/toast.vue'), {
notification notification
}); });
} }

View File

@ -1,4 +1,4 @@
import { Component, defineAsyncComponent, ref } from 'vue'; import { Component, defineAsyncComponent, markRaw, ref } from 'vue';
import Stream from '@/scripts/stream'; import Stream from '@/scripts/stream';
import { store } from '@/store'; import { store } from '@/store';
import { apiUrl } from '@/config'; import { apiUrl } from '@/config';
@ -44,46 +44,81 @@ export function api(endpoint: string, data: Record<string, any> = {}, token?: st
return promise; return promise;
} }
export function popup(component: Component, props: Record<string, any>, callback?: Function) { export function popup(component: Component, props: Record<string, any>, callback?: Function, option?) {
markRaw(component);
const id = Math.random().toString(); // TODO: uuidとか使う const id = Math.random().toString(); // TODO: uuidとか使う
const showing = ref(true); const showing = ref(true);
const popup = { const close = (...args) => {
component,
props: {
...props,
showing
},
showing,
done: (...args) => {
if (callback) callback(...args); if (callback) callback(...args);
showing.value = false; showing.value = false;
}, };
const modal = {
type: 'popup',
component,
props,
showing,
source: option?.source,
done: close,
bgClick: () => close(),
closed: () => { closed: () => {
store.commit('removePopup', id); store.commit('removePopup', id);
}, },
id, id,
}; };
store.commit('addPopup', popup); store.commit('addPopup', modal);
return () => { return close;
}
export function modal(component: Component, props: Record<string, any>, callback?: Function, option?) {
markRaw(component);
const id = Math.random().toString(); // TODO: uuidとか使う
const showing = ref(true);
const close = (...args) => {
if (callback) callback(...args);
showing.value = false; showing.value = false;
}; };
const modal = {
type: 'modal',
component,
props,
showing,
source: option?.source,
done: close,
bgClick: () => close(),
closed: () => {
store.commit('removePopup', id);
},
id,
};
store.commit('addPopup', modal);
return close;
} }
export function dialog(props: Record<string, any>) { export function dialog(props: Record<string, any>) {
return new Promise((res, rej) => { return new Promise((res, rej) => {
popup(defineAsyncComponent(() => import('@/components/dialog.vue')), props, res); modal(defineAsyncComponent(() => import('@/components/dialog.vue')), props, result => {
if (result) {
res(result);
} else {
res({ canceled: true });
}
});
}); });
} }
export function menu(props: Record<string, any>) { export function menu(props: Record<string, any>) {
const source = props.source; // TODO: sourceはpropsの外に出して追加の引数として受け取るようにする
return new Promise((res, rej) => { return new Promise((res, rej) => {
popup(defineAsyncComponent(() => import('@/components/menu.vue')), props, res); modal(defineAsyncComponent(() => import('@/components/menu.vue')), props, res, {
position: 'source',
source
});
}); });
} }
export function post(props: Record<string, any>) { export function post(props: Record<string, any>) {
return new Promise((res, rej) => { return new Promise((res, rej) => {
popup(defineAsyncComponent(() => import('@/components/post-form-dialog.vue')), props, res); modal(defineAsyncComponent(() => import('@/components/post-form.vue')), props, res);
}); });
} }

View File

@ -54,13 +54,13 @@ export default defineComponent({
methods: { methods: {
signin() { signin() {
os.popup(XSigninDialog, { os.modal(XSigninDialog, {
autoSet: true autoSet: true
}); });
}, },
signup() { signup() {
os.popup(XSignupDialog, { os.modal(XSignupDialog, {
autoSet: true autoSet: true
}); });
} }

View File

@ -125,7 +125,7 @@ export default defineComponent({
}, },
info(instance) { info(instance) {
os.popup(MkInstanceInfo, { os.modal(MkInstanceInfo, {
instance: instance instance: instance
}); });
} }

View File

@ -550,7 +550,7 @@ export default defineComponent({
host: q host: q
}); });
} }
os.popup(MkInstanceInfo, { os.modal(MkInstanceInfo, {
instance: instance instance: instance
}); });
}, },

View File

@ -439,7 +439,7 @@ export default defineComponent({
}, },
showFollowing() { showFollowing() {
os.popup(MkUsersDialog, { os.modal(MkUsersDialog, {
title: this.$t('instanceFollowing'), title: this.$t('instanceFollowing'),
pagination: { pagination: {
endpoint: 'federation/following', endpoint: 'federation/following',
@ -453,7 +453,7 @@ export default defineComponent({
}, },
showFollowers() { showFollowers() {
os.popup(MkUsersDialog, { os.modal(MkUsersDialog, {
title: this.$t('instanceFollowers'), title: this.$t('instanceFollowers'),
pagination: { pagination: {
endpoint: 'federation/followers', endpoint: 'federation/followers',
@ -467,7 +467,7 @@ export default defineComponent({
}, },
showUsers() { showUsers() {
os.popup(MkUsersDialog, { os.modal(MkUsersDialog, {
title: this.$t('instanceUsers'), title: this.$t('instanceUsers'),
pagination: { pagination: {
endpoint: 'federation/users', endpoint: 'federation/users',

View File

@ -452,7 +452,7 @@ export default defineComponent({
}, },
addPinUser() { addPinUser() {
os.popup(MkUserSelect, {}).$once('selected', user => { os.modal(MkUserSelect, {}).$once('selected', user => {
this.pinnedUsers = this.pinnedUsers.trim(); this.pinnedUsers = this.pinnedUsers.trim();
this.pinnedUsers += '\n@' + getAcct(user); this.pinnedUsers += '\n@' + getAcct(user);
this.pinnedUsers = this.pinnedUsers.trim(); this.pinnedUsers = this.pinnedUsers.trim();
@ -460,7 +460,7 @@ export default defineComponent({
}, },
chooseProxyAccount() { chooseProxyAccount() {
os.popup(MkUserSelect, {}).$once('selected', user => { os.modal(MkUserSelect, {}).$once('selected', user => {
this.proxyAccount = user; this.proxyAccount = user;
this.proxyAccountId = user.id; this.proxyAccountId = user.id;
this.save(true); this.save(true);

View File

@ -180,7 +180,7 @@ export default defineComponent({
}, },
searchUser() { searchUser() {
os.popup(MkUserSelect, {}).$once('selected', user => { os.modal(MkUserSelect, {}).$once('selected', user => {
this.show(user); this.show(user);
}); });
}, },

View File

@ -132,7 +132,7 @@ export default defineComponent({
}, },
async startUser() { async startUser() {
os.popup(MkUserSelect, {}).$once('selected', user => { os.modal(MkUserSelect, {}).$once('selected', user => {
this.$router.push(`/my/messaging/${getAcct(user)}`); this.$router.push(`/my/messaging/${getAcct(user)}`);
}); });
}, },

View File

@ -220,7 +220,7 @@ export default defineComponent({
}, },
async insertEmoji(ev) { async insertEmoji(ev) {
const vm = os.popup(await import('@/components/emoji-picker.vue'), { const vm = os.modal(await import('@/components/emoji-picker.vue'), {
source: ev.currentTarget || ev.target source: ev.currentTarget || ev.target
}).$once('chosen', emoji => { }).$once('chosen', emoji => {
insertTextAtCursor(this.$refs.text, emoji); insertTextAtCursor(this.$refs.text, emoji);

View File

@ -177,7 +177,7 @@ export default defineComponent({
}, },
addUser() { addUser() {
os.popup(MkUserSelect, {}).$once('selected', user => { os.modal(MkUserSelect, {}).$once('selected', user => {
this.users = this.users.trim(); this.users = this.users.trim();
this.users += '\n@' + getAcct(user); this.users += '\n@' + getAcct(user);
this.users = this.users.trim(); this.users = this.users.trim();

View File

@ -89,7 +89,7 @@ export default defineComponent({
}, },
invite() { invite() {
os.popup(MkUserSelect, {}).$once('selected', user => { os.modal(MkUserSelect, {}).$once('selected', user => {
os.api('users/groups/invite', { os.api('users/groups/invite', {
groupId: this.group.id, groupId: this.group.id,
userId: user.id userId: user.id
@ -134,7 +134,7 @@ export default defineComponent({
}, },
transfer() { transfer() {
os.popup(MkUserSelect, {}).$once('selected', user => { os.modal(MkUserSelect, {}).$once('selected', user => {
os.api('users/groups/transfer', { os.api('users/groups/transfer', {
groupId: this.group.id, groupId: this.group.id,
userId: user.id userId: user.id

View File

@ -88,7 +88,7 @@ export default defineComponent({
}, },
addUser() { addUser() {
os.popup(MkUserSelect, {}).$once('selected', user => { os.modal(MkUserSelect, {}).$once('selected', user => {
os.api('users/lists/push', { os.api('users/lists/push', {
listId: this.list.id, listId: this.list.id,
userId: user.id userId: user.id

View File

@ -26,7 +26,7 @@ export default defineComponent({
}, },
methods: { methods: {
async generateToken() { async generateToken() {
os.popup(await import('@/components/token-generate-window.vue'), { os.modal(await import('@/components/token-generate-window.vue'), {
}).$on('ok', async ({ name, permissions }) => { }).$on('ok', async ({ name, permissions }) => {
const { token } = await os.api('miauth/gen-token', { const { token } = await os.api('miauth/gen-token', {
session: null, session: null,

View File

@ -114,7 +114,7 @@ export default defineComponent({
}, },
async configure() { async configure() {
os.popup(await import('@/components/notification-setting-window.vue'), { os.modal(await import('@/components/notification-setting-window.vue'), {
includingTypes: this.$store.state.i.includingNotificationTypes, includingTypes: this.$store.state.i.includingNotificationTypes,
showGlobalToggle: false, showGlobalToggle: false,
}).$on('ok', async ({ includingTypes: value }: any) => { }).$on('ok', async ({ includingTypes: value }: any) => {

View File

@ -58,7 +58,7 @@ export default defineComponent({
}, },
preview(ev) { preview(ev) {
const picker = os.popup(MkReactionPicker, { const picker = os.modal(MkReactionPicker, {
source: ev.currentTarget || ev.target, source: ev.currentTarget || ev.target,
reactions: this.splited, reactions: this.splited,
showFocus: false, showFocus: false,
@ -73,7 +73,7 @@ export default defineComponent({
}, },
async chooseEmoji(ev) { async chooseEmoji(ev) {
const vm = os.popup(await import('@/components/emoji-picker.vue'), { const vm = os.modal(await import('@/components/emoji-picker.vue'), {
source: ev.currentTarget || ev.target source: ev.currentTarget || ev.target
}).$once('chosen', emoji => { }).$once('chosen', emoji => {
this.reactions += emoji; this.reactions += emoji;

View File

@ -118,7 +118,7 @@ export default defineComponent({
} }
const token = permissions == null || permissions.length === 0 ? null : await new Promise(async (res, rej) => { const token = permissions == null || permissions.length === 0 ? null : await new Promise(async (res, rej) => {
os.popup(await import('@/components/token-generate-window.vue'), { os.modal(await import('@/components/token-generate-window.vue'), {
title: this.$t('tokenRequested'), title: this.$t('tokenRequested'),
information: this.$t('pluginTokenRequestedDescription'), information: this.$t('pluginTokenRequestedDescription'),
initialName: name, initialName: name,

View File

@ -60,7 +60,7 @@ export default defineComponent({
if (this.title) text += `${this.title}\n`; if (this.title) text += `${this.title}\n`;
if (this.text) text += `${this.text}\n`; if (this.text) text += `${this.text}\n`;
if (this.url) text += `${this.url}`; if (this.url) text += `${this.url}`;
os.popup(PostFormDialog, { os.modal(PostFormDialog, {
instant: true, instant: true,
initialText: text.trim() initialText: text.trim()
}).$once('posted', () => { }).$once('posted', () => {

View File

@ -189,7 +189,7 @@ export default defineComponent({
}, },
menu() { menu() {
os.popup(XUserMenu, { os.modal(XUserMenu, {
source: this.$refs.menu, source: this.$refs.menu,
user: this.user user: this.user
}); });

View File

@ -2,13 +2,17 @@
<DeckUI v-if="deckmode"/> <DeckUI v-if="deckmode"/>
<DefaultUI v-else/> <DefaultUI v-else/>
<component v-for="popup in $store.state.popups" :is="popup.component" v-bind="popup.props" :key="popup.id" @done="popup.done" @closed="popup.closed"/> <XModal v-for="modal in $store.state.popups.filter(x => x.type === 'modal')" :key="modal.id" @closed="modal.closed" @click="modal.bgClick" :showing="modal.showing" :source="modal.source">
<component :is="modal.component" v-bind="modal.props" @done="modal.done"/>
</XModal>
<component v-for="popup in $store.state.popups.filter(x => x.type === 'popup')" :key="popup.id" :is="popup.component" v-bind="popup.props" @done="popup.done" @closed="popup.closed"/>
<div id="wait" v-if="$store.state.pendingApiRequestsCount > 0"></div> <div id="wait" v-if="$store.state.pendingApiRequestsCount > 0"></div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineAsyncComponent, defineComponent } from 'vue';
import DefaultUI from './default.vue'; import DefaultUI from './default.vue';
import DeckUI from './deck.vue'; import DeckUI from './deck.vue';
import { instanceName, deckmode } from '@/config'; import { instanceName, deckmode } from '@/config';
@ -17,6 +21,7 @@ export default defineComponent({
components: { components: {
DefaultUI, DefaultUI,
DeckUI, DeckUI,
XModal: defineAsyncComponent(() => import('@/components/modal.vue'))
}, },
metaInfo: { metaInfo: {

View File

@ -111,6 +111,7 @@ export const store = createStore({
popups: [] as { popups: [] as {
id: any; id: any;
component: any; component: any;
type: 'popup' | 'modal',
props: Record<string, any>; props: Record<string, any>;
}[], }[],
fullView: false, fullView: false,

View File

@ -51,7 +51,7 @@ export default defineComponent({
methods: { methods: {
async configure() { async configure() {
os.popup(await import('@/components/notification-setting-window.vue'), { os.modal(await import('@/components/notification-setting-window.vue'), {
includingTypes: this.props.includingTypes, includingTypes: this.props.includingTypes,
}).$on('ok', async ({ includingTypes }) => { }).$on('ok', async ({ includingTypes }) => {
this.props.includingTypes = includingTypes; this.props.includingTypes = includingTypes;

View File

@ -52,13 +52,13 @@ export default defineComponent({
methods: { methods: {
signin() { signin() {
os.popup(XSigninDialog, { os.modal(XSigninDialog, {
autoSet: true autoSet: true
}); });
}, },
signup() { signup() {
os.popup(XSignupDialog, { os.modal(XSignupDialog, {
autoSet: true autoSet: true
}); });
} }