This commit is contained in:
syuilo 2019-04-18 19:40:23 +09:00
parent f753451e92
commit 33d1ee922f
8 changed files with 128 additions and 71 deletions

View File

@ -35,6 +35,7 @@ common:
signup: "新規登録" signup: "新規登録"
signout: "ログアウト" signout: "ログアウト"
reload-to-apply-the-setting: "この設定を反映するにはページをリロードする必要があります。今すぐリロードしますか?" reload-to-apply-the-setting: "この設定を反映するにはページをリロードする必要があります。今すぐリロードしますか?"
fetching-as-ap-object: "連合に照会中"
got-it: "わかった" got-it: "わかった"
customization-tips: customization-tips:

View File

@ -0,0 +1,64 @@
import { faHistory } from '@fortawesome/free-solid-svg-icons';
export async function search(v: any, q: string) {
q = q.trim();
if (q.startsWith('@')) {
v.$router.push(`/${q}`);
return;
}
if (q.startsWith('#')) {
v.$router.push(`/tags/${encodeURIComponent(q.substr(1))}`);
return;
}
// like 2018/03/12
if (/^[0-9]{4}\/[0-9]{2}\/[0-9]{2}/.test(q.replace(/-/g, '/'))) {
const date = new Date(q.replace(/-/g, '/'));
// 日付しか指定されてない場合、例えば 2018/03/12 ならユーザーは
// 2018/03/12 のコンテンツを「含む」結果になることを期待するはずなので
// 23時間59分進める(そのままだと 2018/03/12 00:00:00 「まで」の
// 結果になってしまい、2018/03/12 のコンテンツは含まれない)
if (q.replace(/-/g, '/').match(/^[0-9]{4}\/[0-9]{2}\/[0-9]{2}$/)) {
date.setHours(23, 59, 59, 999);
}
v.$root.$emit('warp', date);
v.$root.dialog({
icon: faHistory,
splash: true,
});
return;
}
if (q.startsWith('https://')) {
const dialog = v.$root.dialog({
type: 'waiting',
text: v.$t('@.fetching-as-ap-object'),
showOkButton: false,
showCancelButton: false,
cancelableByBgClick: false
});
try {
const res = await v.$root.api('ap/show', {
uri: q
});
dialog.close();
if (res.type == 'User') {
v.$router.push(`/@${res.object.username}@${res.object.host}`);
} else if (res.type == 'Note') {
v.$router.push(`/notes/${res.object.id}`);
}
} catch (e) {
dialog.close();
// TODO: Show error
}
return;
}
v.$router.push(`/search?q=${encodeURIComponent(q)}`);
}

View File

@ -6,7 +6,17 @@
<mk-signin/> <mk-signin/>
</template> </template>
<template v-else> <template v-else>
<div class="icon" v-if="!input && !select && !user" :class="type"><fa :icon="icon"/></div> <div class="icon" v-if="icon">
<fa :icon="icon"/>
</div>
<div class="icon" v-else-if="!input && !select && !user" :class="type">
<fa icon="check" v-if="type === 'success'"/>
<fa :icon="faTimesCircle" v-if="type === 'error'"/>
<fa icon="exclamation-triangle" v-if="type === 'warning'"/>
<fa icon="info-circle" v-if="type === 'info'"/>
<fa :icon="faQuestionCircle" v-if="type === 'question'"/>
<fa icon="spinner" pulse v-if="type === 'waiting'"/>
</div>
<header v-if="title" v-html="title"></header> <header v-if="title" v-html="title"></header>
<div class="body" v-if="text" v-html="text"></div> <div class="body" v-if="text" v-html="text"></div>
<ui-input v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder" @keydown="onInputKeydown"></ui-input> <ui-input v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder" @keydown="onInputKeydown"></ui-input>
@ -14,8 +24,8 @@
<ui-select v-if="select" v-model="selectedValue" autofocus> <ui-select v-if="select" v-model="selectedValue" autofocus>
<option v-for="item in select.items" :value="item.value">{{ item.text }}</option> <option v-for="item in select.items" :value="item.value">{{ item.text }}</option>
</ui-select> </ui-select>
<ui-horizon-group no-grow class="buttons fit-bottom" v-if="!splash"> <ui-horizon-group no-grow class="buttons fit-bottom" v-if="!splash && (showOkButton || showCancelButton)">
<ui-button @click="ok" primary :autofocus="!input && !select && !user">{{ (showCancelButton || input || select || user) ? $t('@.ok') : $t('@.got-it') }}</ui-button> <ui-button @click="ok" v-if="showOkButton" primary :autofocus="!input && !select && !user">{{ (showCancelButton || input || select || user) ? $t('@.ok') : $t('@.got-it') }}</ui-button>
<ui-button @click="cancel" v-if="showCancelButton || input || select || user">{{ $t('@.cancel') }}</ui-button> <ui-button @click="cancel" v-if="showCancelButton || input || select || user">{{ $t('@.cancel') }}</ui-button>
</ui-horizon-group> </ui-horizon-group>
</template> </template>
@ -55,10 +65,21 @@ export default Vue.extend({
user: { user: {
required: false required: false
}, },
icon: {
required: false
},
showOkButton: {
type: Boolean,
default: true
},
showCancelButton: { showCancelButton: {
type: Boolean, type: Boolean,
default: false default: false
}, },
cancelableByBgClick: {
type: Boolean,
default: true
},
splash: { splash: {
type: Boolean, type: Boolean,
default: false default: false
@ -69,22 +90,11 @@ export default Vue.extend({
return { return {
inputValue: this.input && this.input.default ? this.input.default : null, inputValue: this.input && this.input.default ? this.input.default : null,
userInputValue: null, userInputValue: null,
selectedValue: null selectedValue: null,
faTimesCircle, faQuestionCircle
}; };
}, },
computed: {
icon(): any {
switch (this.type) {
case 'success': return 'check';
case 'error': return faTimesCircle;
case 'warning': return 'exclamation-triangle';
case 'info': return 'info-circle';
case 'question': return faQuestionCircle;
}
}
},
mounted() { mounted() {
this.$nextTick(() => { this.$nextTick(() => {
(this.$refs.bg as any).style.pointerEvents = 'auto'; (this.$refs.bg as any).style.pointerEvents = 'auto';
@ -113,6 +123,8 @@ export default Vue.extend({
methods: { methods: {
async ok() { async ok() {
if (!this.showOkButton) return;
if (this.user) { if (this.user) {
const user = await this.$root.api('users/show', parseAcct(this.userInputValue)); const user = await this.$root.api('users/show', parseAcct(this.userInputValue));
if (user) { if (user) {
@ -156,7 +168,9 @@ export default Vue.extend({
}, },
onBgClick() { onBgClick() {
this.cancel(); if (this.cancelableByBgClick) {
this.cancel();
}
}, },
onInputKeydown(e) { onInputKeydown(e) {
@ -240,7 +254,7 @@ export default Vue.extend({
margin-top 8px margin-top 8px
> .body > .body
margin 16px 0 margin 16px 0 0 0
> .buttons > .buttons
margin-top 16px margin-top 16px

View File

@ -9,6 +9,7 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n'; import i18n from '../../../i18n';
import { search } from '../../../common/scripts/search';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('desktop/views/components/ui.header.search.vue'), i18n: i18n('desktop/views/components/ui.header.search.vue'),
@ -22,29 +23,11 @@ export default Vue.extend({
async onSubmit() { async onSubmit() {
if (this.wait) return; if (this.wait) return;
const q = this.q.trim(); this.wait = true;
if (q.startsWith('@')) { search(this, this.q).finally(() => {
this.$router.push(`/${q}`);
} else if (q.startsWith('#')) {
this.$router.push(`/tags/${encodeURIComponent(q.substr(1))}`);
} else if (q.startsWith('https://')) {
this.wait = true;
try {
const res = await this.$root.api('ap/show', {
uri: q
});
if (res.type == 'User') {
this.$router.push(`/@${res.object.username}@${res.object.host}`);
} else if (res.type == 'Note') {
this.$router.push(`/notes/${res.object.id}`);
}
} catch (e) {
// TODO
}
this.wait = false; this.wait = false;
} else { this.q = '';
this.$router.push(`/search?q=${encodeURIComponent(q)}`); });
}
} }
} }
}); });

View File

@ -53,6 +53,12 @@ export default Vue.extend({
}, },
created() { created() {
this.$root.$on('warp', this.warp);
this.$once('hook:beforeDestroy', () => {
this.$root.$off('warp', this.warp);
this.connection.dispose();
});
const prepend = note => { const prepend = note => {
(this.$refs.timeline as any).prepend(note); (this.$refs.timeline as any).prepend(note);
}; };
@ -124,13 +130,14 @@ export default Vue.extend({
}); });
}, },
beforeDestroy() {
this.connection.dispose();
},
methods: { methods: {
focus() { focus() {
(this.$refs.timeline as any).focus(); (this.$refs.timeline as any).focus();
},
warp(date) {
this.date = date;
(this.$refs.timeline as any).reload();
} }
} }
}); });

View File

@ -458,10 +458,14 @@ export default (callback: (launch: (router: VueRouter) => [Vue, MiOS], os: MiOS)
}, },
dialog(opts) { dialog(opts) {
const vm = this.new(Dialog, opts); const vm = this.new(Dialog, opts);
return new Promise((res) => { const p: any = new Promise((res) => {
vm.$once('ok', result => res({ canceled: false, result })); vm.$once('ok', result => res({ canceled: false, result }));
vm.$once('cancel', () => res({ canceled: true })); vm.$once('cancel', () => res({ canceled: true }));
}); });
p.close = () => {
vm.close();
};
return p;
} }
}, },
router, router,

View File

@ -66,6 +66,7 @@ import i18n from '../../../i18n';
import { lang } from '../../../config'; import { lang } from '../../../config';
import { faNewspaper, faHashtag, faHome, faColumns } from '@fortawesome/free-solid-svg-icons'; import { faNewspaper, faHashtag, faHome, faColumns } from '@fortawesome/free-solid-svg-icons';
import { faMoon, faSun } from '@fortawesome/free-regular-svg-icons'; import { faMoon, faSun } from '@fortawesome/free-regular-svg-icons';
import { search } from '../../../common/scripts/search';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('mobile/views/components/ui.nav.vue'), i18n: i18n('mobile/views/components/ui.nav.vue'),
@ -133,29 +134,10 @@ export default Vue.extend({
}).then(async ({ canceled, result: query }) => { }).then(async ({ canceled, result: query }) => {
if (canceled) return; if (canceled) return;
const q = query.trim(); this.searching = true;
if (q.startsWith('@')) { search(this, query).finally(() => {
this.$router.push(`/${q}`);
} else if (q.startsWith('#')) {
this.$router.push(`/tags/${encodeURIComponent(q.substr(1))}`);
} else if (q.startsWith('https://')) {
this.searching = true;
try {
const res = await this.$root.api('ap/show', {
uri: q
});
if (res.type == 'User') {
this.$router.push(`/@${res.object.username}@${res.object.host}`);
} else if (res.type == 'Note') {
this.$router.push(`/notes/${res.object.id}`);
}
} catch (e) {
// TODO
}
this.searching = false; this.searching = false;
} else { });
this.$router.push(`/search?q=${encodeURIComponent(q)}`);
}
}); });
}, },

View File

@ -54,6 +54,12 @@ export default Vue.extend({
}, },
created() { created() {
this.$root.$on('warp', this.warp);
this.$once('hook:beforeDestroy', () => {
this.$root.$off('warp', this.warp);
this.connection.dispose();
});
const prepend = note => { const prepend = note => {
(this.$refs.timeline as any).prepend(note); (this.$refs.timeline as any).prepend(note);
}; };
@ -125,10 +131,6 @@ export default Vue.extend({
}); });
}, },
beforeDestroy() {
this.connection.dispose();
},
methods: { methods: {
focus() { focus() {
(this.$refs.timeline as any).focus(); (this.$refs.timeline as any).focus();