Merge c6555c2cf2
into d4654dd7bd
This commit is contained in:
commit
81d774ad10
|
@ -38,6 +38,7 @@ export type SearchOpts = {
|
||||||
userId?: MiNote['userId'] | null;
|
userId?: MiNote['userId'] | null;
|
||||||
channelId?: MiNote['channelId'] | null;
|
channelId?: MiNote['channelId'] | null;
|
||||||
host?: string | null;
|
host?: string | null;
|
||||||
|
searchFrom? : string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SearchPagination = {
|
export type SearchPagination = {
|
||||||
|
@ -184,6 +185,7 @@ export class SearchService {
|
||||||
case 'sqlPgroonga': {
|
case 'sqlPgroonga': {
|
||||||
// ほとんど内容に差がないのでsqlLikeとsqlPgroongaを同じ処理にしている.
|
// ほとんど内容に差がないのでsqlLikeとsqlPgroongaを同じ処理にしている.
|
||||||
// 今後の拡張で差が出る用であれば関数を分ける.
|
// 今後の拡張で差が出る用であれば関数を分ける.
|
||||||
|
|
||||||
return this.searchNoteByLike(q, me, opts, pagination);
|
return this.searchNoteByLike(q, me, opts, pagination);
|
||||||
}
|
}
|
||||||
case 'meilisearch': {
|
case 'meilisearch': {
|
||||||
|
@ -220,9 +222,26 @@ export class SearchService {
|
||||||
.leftJoinAndSelect('renote.user', 'renoteUser');
|
.leftJoinAndSelect('renote.user', 'renoteUser');
|
||||||
|
|
||||||
if (this.config.fulltextSearch?.provider === 'sqlPgroonga') {
|
if (this.config.fulltextSearch?.provider === 'sqlPgroonga') {
|
||||||
query.andWhere('note.text &@~ :q', { q });
|
// pgroonga
|
||||||
|
if(opts.searchFrom === 'textWithCw'){
|
||||||
|
// textWithCwオプション
|
||||||
|
query.andWhere('(coalesce(note.cw, \'\') || note.text) &@~ :q', { q });
|
||||||
|
}else {
|
||||||
|
// 通常検索
|
||||||
|
query.andWhere('note.text &@~ :q', { q });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
query.andWhere('LOWER(note.text) LIKE :q', { q: `%${ sqlLikeEscape(q.toLowerCase()) }%` });
|
// Postgresql 標準
|
||||||
|
if(opts.searchFrom === 'textWithCw'){
|
||||||
|
// textWithCwオプション
|
||||||
|
query.andWhere('LOWER((coalesce(note.cw, \'\') || note.text)) LIKE :q', { q: `%${ sqlLikeEscape(q.toLowerCase()) }%` });
|
||||||
|
}else {
|
||||||
|
// 通常検索
|
||||||
|
query.andWhere('LOWER(note.text) LIKE :q', { q: `%${ sqlLikeEscape(q.toLowerCase()) }%` });
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.host) {
|
if (opts.host) {
|
||||||
|
|
|
@ -51,6 +51,7 @@ export const paramDef = {
|
||||||
},
|
},
|
||||||
userId: { type: 'string', format: 'misskey:id', nullable: true, default: null },
|
userId: { type: 'string', format: 'misskey:id', nullable: true, default: null },
|
||||||
channelId: { type: 'string', format: 'misskey:id', nullable: true, default: null },
|
channelId: { type: 'string', format: 'misskey:id', nullable: true, default: null },
|
||||||
|
searchFrom: { type: 'string', nullable: true, default: null },
|
||||||
},
|
},
|
||||||
required: ['query'],
|
required: ['query'],
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -78,6 +79,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
userId: ps.userId,
|
userId: ps.userId,
|
||||||
channelId: ps.channelId,
|
channelId: ps.channelId,
|
||||||
host: ps.host,
|
host: ps.host,
|
||||||
|
searchFrom: ps.searchFrom
|
||||||
}, {
|
}, {
|
||||||
untilId: untilId,
|
untilId: untilId,
|
||||||
sinceId: sinceId,
|
sinceId: sinceId,
|
||||||
|
|
|
@ -15,8 +15,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
>
|
>
|
||||||
<template #prefix><i class="ti ti-search"></i></template>
|
<template #prefix><i class="ti ti-search"></i></template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkFoldableSection expanded>
|
<div >
|
||||||
<template #header>{{ i18n.ts.options }}</template>
|
<div >{{ i18n.ts.options }}</div>
|
||||||
|
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<MkRadios v-model="searchScope">
|
<MkRadios v-model="searchScope">
|
||||||
|
@ -26,6 +26,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<option value="user">{{ i18n.ts._search.searchScopeUser }}</option>
|
<option value="user">{{ i18n.ts._search.searchScopeUser }}</option>
|
||||||
</MkRadios>
|
</MkRadios>
|
||||||
|
|
||||||
|
<MkRadios v-model="searchFrom">
|
||||||
|
<option value="text">本文</option>
|
||||||
|
<option value="textWithCw">本文+CW</option>
|
||||||
|
</MkRadios>
|
||||||
|
|
||||||
<div v-if="instance.federation !== 'none' && searchScope === 'server'" :class="$style.subOptionRoot">
|
<div v-if="instance.federation !== 'none' && searchScope === 'server'" :class="$style.subOptionRoot">
|
||||||
<MkInput
|
<MkInput
|
||||||
v-model="hostInput"
|
v-model="hostInput"
|
||||||
|
@ -87,31 +92,37 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MkFoldableSection>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<MkButton
|
<MkButton
|
||||||
large
|
large
|
||||||
primary
|
primary
|
||||||
gradate
|
gradate
|
||||||
rounded
|
rounded
|
||||||
:disabled="searchParams == null"
|
:disabled="
|
||||||
|
searchParams == null
|
||||||
|
|| !(componentBlockSearchUntil && now? componentBlockSearchUntil < now : true) // disableの条件なので逆をとる
|
||||||
|
"
|
||||||
style="margin: 0 auto;"
|
style="margin: 0 auto;"
|
||||||
|
|
||||||
@click="search"
|
@click="search"
|
||||||
>
|
>
|
||||||
{{ i18n.ts.search }}
|
{{ i18n.ts.search }}
|
||||||
|
<MkTime v-if="componentBlockSearchUntil && now && componentBlockSearchUntil > now" :time="componentBlockSearchUntil" />
|
||||||
</MkButton>
|
</MkButton>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MkFoldableSection v-if="paginator">
|
<div v-if="paginator">
|
||||||
<template #header>{{ i18n.ts.searchResult }}</template>
|
<div >{{ i18n.ts.searchResult }}</div>
|
||||||
<MkNotesTimeline :key="`searchNotes:${key}`" :paginator="paginator"/>
|
<MkNotesTimeline :key="`searchNotes:${key}`" :paginator="paginator"/>
|
||||||
</MkFoldableSection>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, markRaw, ref, shallowRef, toRef } from 'vue';
|
import { computed, markRaw, ref, shallowRef, toRef} from 'vue';
|
||||||
import { host as localHost } from '@@/js/config.js';
|
import { host as localHost } from '@@/js/config.js';
|
||||||
import type * as Misskey from 'misskey-js';
|
import type * as Misskey from 'misskey-js';
|
||||||
import { $i } from '@/i.js';
|
import { $i } from '@/i.js';
|
||||||
|
@ -122,7 +133,6 @@ import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
import { apLookup } from '@/utility/lookup.js';
|
import { apLookup } from '@/utility/lookup.js';
|
||||||
import { useRouter } from '@/router.js';
|
import { useRouter } from '@/router.js';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkFoldableSection from '@/components/MkFoldableSection.vue';
|
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkNotesTimeline from '@/components/MkNotesTimeline.vue';
|
import MkNotesTimeline from '@/components/MkNotesTimeline.vue';
|
||||||
import MkRadios from '@/components/MkRadios.vue';
|
import MkRadios from '@/components/MkRadios.vue';
|
||||||
|
@ -179,15 +189,17 @@ if (fetchedUser != null) {
|
||||||
|
|
||||||
const searchScope = ref<'all' | 'local' | 'server' | 'user'>((() => {
|
const searchScope = ref<'all' | 'local' | 'server' | 'user'>((() => {
|
||||||
if (user.value != null) return 'user';
|
if (user.value != null) return 'user';
|
||||||
if (noteSearchableScope === 'local') return 'local';
|
|
||||||
if (hostInput.value) return 'server';
|
if (hostInput.value) return 'server';
|
||||||
return 'all';
|
return 'local';
|
||||||
})());
|
})());
|
||||||
|
|
||||||
|
const searchFrom = ref<'text' | 'textWithCw'>('textWithCw');
|
||||||
|
|
||||||
type SearchParams = {
|
type SearchParams = {
|
||||||
readonly query: string;
|
readonly query: string;
|
||||||
readonly host?: string;
|
readonly host?: string;
|
||||||
readonly userId?: string;
|
readonly userId?: string;
|
||||||
|
readonly searchFrom?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const fixHostIfLocal = (target: string | null | undefined) => {
|
const fixHostIfLocal = (target: string | null | undefined) => {
|
||||||
|
@ -205,6 +217,7 @@ const searchParams = computed<SearchParams | null>(() => {
|
||||||
query: trimmedQuery,
|
query: trimmedQuery,
|
||||||
host: fixHostIfLocal(user.value.host),
|
host: fixHostIfLocal(user.value.host),
|
||||||
userId: user.value.id,
|
userId: user.value.id,
|
||||||
|
searchFrom: searchFrom.value,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,6 +232,7 @@ const searchParams = computed<SearchParams | null>(() => {
|
||||||
return {
|
return {
|
||||||
query: trimmedQuery,
|
query: trimmedQuery,
|
||||||
host: fixHostIfLocal(trimmedHost),
|
host: fixHostIfLocal(trimmedHost),
|
||||||
|
searchFrom: searchFrom.value
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,11 +240,13 @@ const searchParams = computed<SearchParams | null>(() => {
|
||||||
return {
|
return {
|
||||||
query: trimmedQuery,
|
query: trimmedQuery,
|
||||||
host: '.',
|
host: '.',
|
||||||
|
searchFrom: searchFrom.value
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
query: trimmedQuery,
|
query: trimmedQuery,
|
||||||
|
searchFrom: searchFrom.value
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -251,9 +267,28 @@ function removeUser() {
|
||||||
user.value = null;
|
user.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const localBlockSearchUntil = localStorage.getItem('noteSearchedAt')
|
||||||
|
const componentBlockSearchUntil = ref(localBlockSearchUntil ? new Date(parseInt(localBlockSearchUntil)): null)
|
||||||
|
const now = ref<Date | null >(new Date())
|
||||||
|
|
||||||
|
|
||||||
async function search() {
|
async function search() {
|
||||||
if (searchParams.value == null) return;
|
if (searchParams.value == null) return;
|
||||||
|
|
||||||
|
// バブリング防止チェック
|
||||||
|
const noteSearchedAt = localStorage.getItem('noteSearchedAt');
|
||||||
|
const now = Date.now();
|
||||||
|
if (noteSearchedAt && now < parseInt(noteSearchedAt)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
localStorage.setItem('noteSearchedAt', now.toString());
|
||||||
|
componentBlockSearchUntil.value = new Date(now + 6 * 1000)
|
||||||
|
console.log(componentBlockSearchUntil.value)
|
||||||
|
setTimeout(() => {
|
||||||
|
localStorage.removeItem('noteSearchedAt')
|
||||||
|
componentBlockSearchUntil.value = null
|
||||||
|
}, 6 * 1000)
|
||||||
|
|
||||||
//#region AP lookup
|
//#region AP lookup
|
||||||
if (searchParams.value.query.startsWith('https://') && !searchParams.value.query.includes(' ')) {
|
if (searchParams.value.query.startsWith('https://') && !searchParams.value.query.includes(' ')) {
|
||||||
const confirm = await os.confirm({
|
const confirm = await os.confirm({
|
||||||
|
|
Loading…
Reference in New Issue