diff --git a/src/client/components/page/page.post.vue b/src/client/components/page/page.post.vue index e2b712667a..ac8be4a397 100644 --- a/src/client/components/page/page.post.vue +++ b/src/client/components/page/page.post.vue @@ -44,14 +44,7 @@ export default defineComponent({ }, methods: { upload() { - return new Promise((ok) => { - const dialog = os.dialog({ - type: 'waiting', - text: this.$t('uploading') + '...', - showOkButton: false, - showCancelButton: false, - cancelableByBgClick: false - }); + const promise = new Promise((ok) => { const canvas = this.hpml.canvases[this.value.canvasId]; canvas.toBlob(blob => { const data = new FormData(); @@ -67,11 +60,12 @@ export default defineComponent({ }) .then(response => response.json()) .then(f => { - dialog.close(); ok(f); }) }); }); + os.promiseDialog(promise); + return promise; }, async post() { this.posting = true; diff --git a/src/client/components/icon-dialog.vue b/src/client/components/waiting-dialog.vue similarity index 53% rename from src/client/components/icon-dialog.vue rename to src/client/components/waiting-dialog.vue index e8eae3342f..7e8ebeaec0 100644 --- a/src/client/components/icon-dialog.vue +++ b/src/client/components/waiting-dialog.vue @@ -1,8 +1,9 @@ <template> -<MkModal ref="modal" @click="type === 'success' ? done() : () => {}" @closed="$emit('closed')"> - <div class="iuyakobc" :class="type"> - <Fa class="icon" v-if="type === 'success'" :icon="faCheck"/> - <Fa class="icon" v-else-if="type === 'waiting'" :icon="faSpinner" pulse/> +<MkModal ref="modal" @click="success ? done() : () => {}" @closed="$emit('closed')"> + <div class="iuyakobc" :class="{ iconOnly: (text == null) || success }"> + <Fa class="icon success" v-if="success" :icon="faCheck"/> + <Fa class="icon waiting" v-else :icon="faSpinner" pulse/> + <div class="text" v-if="text && !success">{{ text }}<MkEllipsis/></div> </div> </MkModal> </template> @@ -18,12 +19,18 @@ export default defineComponent({ }, props: { - type: { - required: true + success: { + type: Boolean, + required: true, }, showing: { - required: true - } + type: Boolean, + required: true, + }, + text: { + type: String, + required: false, + }, }, emits: ['done', 'closed'], @@ -57,17 +64,32 @@ export default defineComponent({ text-align: center; background: var(--panel); border-radius: var(--radius); - width: initial; - font-size: 32px; + width: 250px; - &.success { - color: var(--accent); + &.iconOnly { + padding: 0; + width: 96px; + height: 96px; + + > .icon { + height: 100%; + } } - &.waiting { - > .icon { + > .icon { + font-size: 32px; + + &.success { + color: var(--accent); + } + + &.waiting { opacity: 0.7; } } + + > .text { + margin-top: 16px; + } } </style> diff --git a/src/client/os.ts b/src/client/os.ts index 03bae65539..688bc136df 100644 --- a/src/client/os.ts +++ b/src/client/os.ts @@ -62,17 +62,34 @@ export function api(endpoint: string, data: Record<string, any> = {}, token?: st return promise; } -export function apiWithDialog(endpoint: string, data: Record<string, any> = {}, token?: string | null | undefined, onSuccess?: (res: any) => void, onFailure?: (e: Error) => void) { - const showing = ref(true); - const state = ref('waiting'); - +export function apiWithDialog( + endpoint: string, + data: Record<string, any> = {}, + token?: string | null | undefined, + onSuccess?: (res: any) => void, + onFailure?: (e: Error) => void, +) { const promise = api(endpoint, data, token); + promiseDialog(promise, onSuccess, onFailure); + + return promise; +} + +export function promiseDialog<T extends Promise<any>>( + promise: T, + onSuccess?: (res: any) => void, + onFailure?: (e: Error) => void, + text?: string, +): T { + const showing = ref(true); + const success = ref(false); + promise.then(res => { if (onSuccess) { showing.value = false; onSuccess(res); } else { - state.value = 'success'; + success.value = true; setTimeout(() => { showing.value = false; }, 1000); @@ -89,9 +106,10 @@ export function apiWithDialog(endpoint: string, data: Record<string, any> = {}, } }); - popup(defineAsyncComponent(() => import('@/components/icon-dialog.vue')), { - type: state, - showing: showing + popup(defineAsyncComponent(() => import('@/components/waiting-dialog.vue')), { + success: success, + showing: showing, + text: text, }, {}, 'closed'); return promise; @@ -161,8 +179,8 @@ export function success() { setTimeout(() => { showing.value = false; }, 1000); - popup(defineAsyncComponent(() => import('@/components/icon-dialog.vue')), { - type: 'success', + popup(defineAsyncComponent(() => import('@/components/waiting-dialog.vue')), { + success: true, showing: showing }, { done: () => resolve(), @@ -173,8 +191,8 @@ export function success() { export function waiting() { return new Promise((resolve, reject) => { const showing = ref(true); - popup(defineAsyncComponent(() => import('@/components/icon-dialog.vue')), { - type: 'waiting', + popup(defineAsyncComponent(() => import('@/components/waiting-dialog.vue')), { + success: false, showing: showing }, { done: () => resolve(), diff --git a/src/client/pages/follow.vue b/src/client/pages/follow.vue index 13e0a62a0d..b1ab7f9f6b 100644 --- a/src/client/pages/follow.vue +++ b/src/client/pages/follow.vue @@ -6,26 +6,20 @@ <script lang="ts"> import { defineComponent } from 'vue'; import * as os from '@/os'; +import parseAcct from '../../misc/acct/parse'; export default defineComponent({ created() { const acct = new URL(location.href).searchParams.get('acct'); if (acct == null) return; - /* - const dialog = os.dialog({ - type: 'waiting', - text: this.$t('fetchingAsApObject') + '...', - showOkButton: false, - showCancelButton: false, - cancelableByBgClick: false - }); - */ + let promise; if (acct.startsWith('https://')) { - os.api('ap/show', { + promise = os.api('ap/show', { uri: acct - }).then(res => { + }); + promise.then(res => { if (res.type == 'User') { this.follow(res.object); } else if (res.type === 'Note') { @@ -38,30 +32,15 @@ export default defineComponent({ window.close(); }); } - }).catch(e => { - os.dialog({ - type: 'error', - text: e - }).then(() => { - window.close(); - }); - }).finally(() => { - //dialog.close(); }); } else { - os.api('users/show', parseAcct(acct)).then(user => { + promise = os.api('users/show', parseAcct(acct)); + promise.then(user => { this.follow(user); - }).catch(e => { - os.dialog({ - type: 'error', - text: e - }).then(() => { - window.close(); - }); - }).finally(() => { - //dialog.close(); }); } + + os.promiseDialog(promise, null, null, this.$t('fetchingAsApObject')); }, methods: { @@ -77,19 +56,8 @@ export default defineComponent({ return; } - os.api('following/create', { + os.apiWithDialog('following/create', { userId: user.id - }).then(() => { - os.success().then(() => { - window.close(); - }); - }).catch(e => { - os.dialog({ - type: 'error', - text: e - }).then(() => { - window.close(); - }); }); } } diff --git a/src/client/pages/instance/emojis.vue b/src/client/pages/instance/emojis.vue index 465a9ebe00..b254b65765 100644 --- a/src/client/pages/instance/emojis.vue +++ b/src/client/pages/instance/emojis.vue @@ -106,24 +106,13 @@ export default defineComponent({ async add(e) { const files = await selectFile(e.currentTarget || e.target, null, true); - const dialog = os.dialog({ - type: 'waiting', - text: this.$t('doing') + '...', - showOkButton: false, - showCancelButton: false, - cancelableByBgClick: false - }); - - Promise.all(files.map(file => os.api('admin/emoji/add', { + const promise = Promise.all(files.map(file => os.api('admin/emoji/add', { fileId: file.id, - }))) - .then(() => { + }))); + promise.then(() => { this.$refs.emojis.reload(); - os.success(); - }) - .finally(() => { - dialog.cancel(); }); + os.promiseDialog(promise); }, async edit(emoji) { diff --git a/src/client/pages/settings/import-export.vue b/src/client/pages/settings/import-export.vue index a5a0085277..8943695dfd 100644 --- a/src/client/pages/settings/import-export.vue +++ b/src/client/pages/settings/import-export.vue @@ -69,31 +69,15 @@ export default defineComponent({ data.append('file', file); data.append('i', this.$store.state.i.token); - const dialog = os.dialog({ - type: 'waiting', - text: this.$t('uploading') + '...', - showOkButton: false, - showCancelButton: false, - cancelableByBgClick: false - }); - - fetch(apiUrl + '/drive/files/create', { + const promise = fetch(apiUrl + '/drive/files/create', { method: 'POST', body: data }) .then(response => response.json()) .then(f => { this.reqImport(f); - }) - .catch(e => { - os.dialog({ - type: 'error', - text: e - }); - }) - .finally(() => { - dialog.close(); }); + os.promiseDialog(promise); }, reqImport(file) { diff --git a/src/client/pages/test.vue b/src/client/pages/test.vue index 02b4d1614d..b053b859bb 100644 --- a/src/client/pages/test.vue +++ b/src/client/pages/test.vue @@ -106,6 +106,14 @@ </div> </div> + <div class="_card _vMargin"> + <div class="_title">Waiting dialog</div> + <div class="_content"> + <MkButton inline @click="openWaitingDialog()">icon only</MkButton> + <MkButton inline @click="openWaitingDialog('Doing')">with text</MkButton> + </div> + </div> + <div class="_card _vMargin"> <div class="_title">Messaging window</div> <div class="_content"> @@ -224,6 +232,13 @@ export default defineComponent({ os.pageWindow('/my/messaging', defineAsyncComponent(() => import('@/pages/messaging/index.vue'))); }, + openWaitingDialog(text?) { + const promise = new Promise((resolve, reject) => { + setTimeout(resolve, 2000); + }); + os.promiseDialog(promise, null, null, text); + }, + resetTutorial() { this.$store.dispatch('settings/set', { key: 'tutorial', value: 0 }); }, diff --git a/src/client/scripts/search.ts b/src/client/scripts/search.ts index fbdc32dfb1..540aba2a92 100644 --- a/src/client/scripts/search.ts +++ b/src/client/scripts/search.ts @@ -48,29 +48,18 @@ export async function search(q?: string | null | undefined) { } if (q.startsWith('https://')) { - /* - const dialog = os.dialog({ - type: 'waiting', - text: i18n.global.t('fetchingAsApObject') + '...', - showOkButton: false, - showCancelButton: false, - cancelableByBgClick: false + const promise = os.api('ap/show', { + uri: q }); - */ - try { - const res = await os.api('ap/show', { - uri: q - }); - //dialog.cancel(); - if (res.type === 'User') { - router.push(`/@${res.object.username}@${res.object.host}`); - } else if (res.type === 'Note') { - router.push(`/notes/${res.object.id}`); - } - } catch (e) { - //dialog.cancel(); - // TODO: Show error + os.promiseDialog(promise, null, null, i18n.global.t('fetchingAsApObject')); + + const res = await promise; + + if (res.type === 'User') { + router.push(`/@${res.object.username}@${res.object.host}`); + } else if (res.type === 'Note') { + router.push(`/notes/${res.object.id}`); } return;