summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKhsmty <me@khsmty.com>2023-02-25 09:01:21 +0900
committerGitHub <noreply@github.com>2023-02-25 09:01:21 +0900
commit64be363adca6385104e4af268e2e57a06c4d83a9 (patch)
tree99dc43380b92b6729a1e1789e62df40f24cbc1cb
parentMerge branch 'develop' of https://github.com/misskey-dev/misskey into develop (diff)
downloadmisskey-64be363adca6385104e4af268e2e57a06c4d83a9.tar.gz
misskey-64be363adca6385104e4af268e2e57a06c4d83a9.tar.bz2
misskey-64be363adca6385104e4af268e2e57a06c4d83a9.zip
feat: 2つの検索画面の統合 (#9949) (#10038)
* feat: 検索画面の UI を統一 * fix: エラーの修正 * add: changelog --------- Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
-rw-r--r--CHANGELOG.md2
-rw-r--r--packages/frontend/src/init.ts6
-rw-r--r--packages/frontend/src/navbar.ts3
-rw-r--r--packages/frontend/src/pages/explore.vue35
-rw-r--r--packages/frontend/src/pages/search.vue128
-rw-r--r--packages/frontend/src/router.ts2
-rw-r--r--packages/frontend/src/scripts/search.ts63
-rw-r--r--packages/frontend/src/ui/classic.header.vue4
-rw-r--r--packages/frontend/src/ui/classic.sidebar.vue4
-rw-r--r--packages/frontend/src/ui/visitor/a.vue5
-rw-r--r--packages/frontend/src/ui/visitor/b.vue5
-rw-r--r--packages/frontend/src/ui/visitor/header.vue6
12 files changed, 137 insertions, 126 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f6e56e17e4..6869091fcb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,7 +2,7 @@
## 13.x.x (unreleased)
### Improvements
--
+- feat: 検索画面の統合 (Khsmty)
### Bugfixes
-
diff --git a/packages/frontend/src/init.ts b/packages/frontend/src/init.ts
index 8c657295f9..0a626b36c6 100644
--- a/packages/frontend/src/init.ts
+++ b/packages/frontend/src/init.ts
@@ -36,7 +36,6 @@ import { $i, refreshAccount, login, updateAccount, signout } from '@/account';
import { defaultStore, ColdDeviceStorage } from '@/store';
import { fetchInstance, instance } from '@/instance';
import { makeHotkey } from '@/scripts/hotkey';
-import { search } from '@/scripts/search';
import { deviceKind } from '@/scripts/device-kind';
import { initializeSw } from '@/scripts/initialize-sw';
import { reloadChannel } from '@/scripts/unison-reload';
@@ -47,6 +46,7 @@ import { deckStore } from './ui/deck/deck-store';
import { miLocalStorage } from './local-storage';
import { claimAchievement, claimedAchievements } from './scripts/achievements';
import { fetchCustomEmojis } from './custom-emojis';
+import { mainRouter } from './router';
console.info(`Misskey v${version}`);
@@ -352,7 +352,9 @@ const hotkeys = {
'd': (): void => {
defaultStore.set('darkMode', !defaultStore.state.darkMode);
},
- 's': search,
+ 's': (): void => {
+ mainRouter.push('/search');
+ }
};
if ($i) {
diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts
index 95bf6e8181..48bece05fa 100644
--- a/packages/frontend/src/navbar.ts
+++ b/packages/frontend/src/navbar.ts
@@ -1,7 +1,6 @@
import { computed, reactive } from 'vue';
import { $i } from './account';
import { miLocalStorage } from './local-storage';
-import { search } from '@/scripts/search';
import * as os from '@/os';
import { i18n } from '@/i18n';
import { ui } from '@/config';
@@ -42,7 +41,7 @@ export const navbarItemDef = reactive({
search: {
title: i18n.ts.search,
icon: 'ti ti-search',
- action: () => search(),
+ to: '/search',
},
lists: {
title: i18n.ts.lists,
diff --git a/packages/frontend/src/pages/explore.vue b/packages/frontend/src/pages/explore.vue
index 0ed0a7ebc2..2131188dde 100644
--- a/packages/frontend/src/pages/explore.vue
+++ b/packages/frontend/src/pages/explore.vue
@@ -11,23 +11,6 @@
<div v-else-if="tab === 'roles'">
<XRoles/>
</div>
- <div v-else-if="tab === 'search'">
- <MkSpacer :content-max="1200">
- <div>
- <MkInput v-model="searchQuery" :debounce="true" type="search">
- <template #prefix><i class="ti ti-search"></i></template>
- <template #label>{{ i18n.ts.searchUser }}</template>
- </MkInput>
- <MkRadios v-model="searchOrigin">
- <option value="combined">{{ i18n.ts.all }}</option>
- <option value="local">{{ i18n.ts.local }}</option>
- <option value="remote">{{ i18n.ts.remote }}</option>
- </MkRadios>
- </div>
-
- <MkUserList v-if="searchQuery" ref="searchEl" class="_margin" :pagination="searchPagination"/>
- </MkSpacer>
- </div>
</div>
</MkStickyContainer>
</template>
@@ -38,11 +21,8 @@ import XFeatured from './explore.featured.vue';
import XUsers from './explore.users.vue';
import XRoles from './explore.roles.vue';
import MkFoldableSection from '@/components/MkFoldableSection.vue';
-import MkInput from '@/components/MkInput.vue';
-import MkRadios from '@/components/MkRadios.vue';
import { definePageMetadata } from '@/scripts/page-metadata';
import { i18n } from '@/i18n';
-import MkUserList from '@/components/MkUserList.vue';
const props = withDefaults(defineProps<{
tag?: string;
@@ -53,22 +33,11 @@ const props = withDefaults(defineProps<{
let tab = $ref(props.initialTab);
let tagsEl = $shallowRef<InstanceType<typeof MkFoldableSection>>();
-let searchQuery = $ref(null);
-let searchOrigin = $ref('combined');
watch(() => props.tag, () => {
if (tagsEl) tagsEl.toggleContent(props.tag == null);
});
-const searchPagination = {
- endpoint: 'users/search' as const,
- limit: 10,
- params: computed(() => (searchQuery && searchQuery !== '') ? {
- query: searchQuery,
- origin: searchOrigin,
- } : null),
-};
-
const headerActions = $computed(() => []);
const headerTabs = $computed(() => [{
@@ -83,10 +52,6 @@ const headerTabs = $computed(() => [{
key: 'roles',
icon: 'ti ti-badges',
title: i18n.ts.roles,
-}, {
- key: 'search',
- icon: 'ti ti-search',
- title: i18n.ts.search,
}]);
definePageMetadata(computed(() => ({
diff --git a/packages/frontend/src/pages/search.vue b/packages/frontend/src/pages/search.vue
index e52c97b350..d32bdcd78e 100644
--- a/packages/frontend/src/pages/search.vue
+++ b/packages/frontend/src/pages/search.vue
@@ -2,18 +2,41 @@
<MkStickyContainer>
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
<MkSpacer :content-max="800">
- <MkNotes ref="notes" :pagination="pagination"/>
+ <MkInput v-model="searchQuery" :large="true" :autofocus="true" :debounce="true" type="search" style="margin-bottom: var(--margin);" @update:model-value="search()">
+ <template #prefix><i class="ti ti-search"></i></template>
+ </MkInput>
+ <MkTab v-model="searchType" style="margin-bottom: var(--margin);" @update:model-value="search()">
+ <option value="note">{{ i18n.ts.note }}</option>
+ <option value="user">{{ i18n.ts.user }}</option>
+ </MkTab>
+
+ <div v-if="searchType === 'note'">
+ <MkNotes v-if="searchQuery" ref="notes" :pagination="notePagination"/>
+ </div>
+ <div v-else>
+ <MkRadios v-model="searchOrigin" style="margin-bottom: var(--margin);" @update:model-value="search()">
+ <option value="combined">{{ i18n.ts.all }}</option>
+ <option value="local">{{ i18n.ts.local }}</option>
+ <option value="remote">{{ i18n.ts.remote }}</option>
+ </MkRadios>
+
+ <MkUserList v-if="searchQuery" ref="users" :pagination="userPagination"/>
+ </div>
</MkSpacer>
</MkStickyContainer>
</template>
<script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, onMounted } from 'vue';
import MkNotes from '@/components/MkNotes.vue';
+import MkUserList from '@/components/MkUserList.vue';
+import MkInput from '@/components/MkInput.vue';
+import MkTab from '@/components/MkTab.vue';
+import MkRadios from '@/components/MkRadios.vue';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
import * as os from '@/os';
-import { useRouter } from '@/router';
+import { useRouter, mainRouter } from '@/router';
import { $i } from '@/account';
const router = useRouter();
@@ -21,14 +44,63 @@ const router = useRouter();
const props = defineProps<{
query: string;
channel?: string;
+ type?: string;
+ origin?: string;
}>();
-const query = props.query;
+let searchQuery = $ref('');
+let searchType = $ref('note');
+let searchOrigin = $ref('combined');
+
+onMounted(() => {
+ searchQuery = props.query ?? '';
+ searchType = props.type ?? 'note';
+ searchOrigin = props.origin ?? 'combined';
+
+ if (searchQuery) {
+ search();
+ }
+});
+
+const search = async () => {
+ const query = searchQuery.toString().trim();
+
+ if (query == null || query === '') return;
+
+ if (query.startsWith('@') && !query.includes(' ')) {
+ mainRouter.push(`/${query}`);
+ return;
+ }
+
+ if (query.startsWith('#')) {
+ mainRouter.push(`/tags/${encodeURIComponent(query.substr(1))}`);
+ return;
+ }
+
+ // like 2018/03/12
+ if (/^[0-9]{4}\/[0-9]{2}\/[0-9]{2}/.test(query.replace(/-/g, '/'))) {
+ const date = new Date(query.replace(/-/g, '/'));
+
+ // 日付しか指定されてない場合、例えば 2018/03/12 ならユーザーは
+ // 2018/03/12 のコンテンツを「含む」結果になることを期待するはずなので
+ // 23時間59分進める(そのままだと 2018/03/12 00:00:00 「まで」の
+ // 結果になってしまい、2018/03/12 のコンテンツは含まれない)
+ if (query.replace(/-/g, '/').match(/^[0-9]{4}\/[0-9]{2}\/[0-9]{2}$/)) {
+ date.setHours(23, 59, 59, 999);
+ }
+
+ // TODO
+ //v.$root.$emit('warp', date);
+ os.alert({
+ icon: 'ti ti-history',
+ iconOnly: true, autoClose: true,
+ });
+ return;
+ }
-if ($i != null) {
- if (query.startsWith('https://') || (query.startsWith('@') && !query.includes(' '))) {
+ if (query.startsWith('https://')) {
const promise = os.api('ap/show', {
- uri: props.query,
+ uri: query,
});
os.promiseDialog(promise, null, null, i18n.ts.fetchingAsApObject);
@@ -36,28 +108,58 @@ if ($i != null) {
const res = await promise;
if (res.type === 'User') {
- router.replace(`/@${res.object.username}@${res.object.host}`);
+ mainRouter.push(`/@${res.object.username}@${res.object.host}`);
} else if (res.type === 'Note') {
- router.replace(`/notes/${res.object.id}`);
+ mainRouter.push(`/notes/${res.object.id}`);
}
+
+ return;
}
-}
-const pagination = {
+ if ($i != null) {
+ if (query.startsWith('https://') || (query.startsWith('@') && !query.includes(' '))) {
+ const promise = os.api('ap/show', {
+ uri: query,
+ });
+
+ os.promiseDialog(promise, null, null, i18n.ts.fetchingAsApObject);
+
+ const res = await promise;
+
+ if (res.type === 'User') {
+ router.replace(`/@${res.object.username}@${res.object.host}`);
+ } else if (res.type === 'Note') {
+ router.replace(`/notes/${res.object.id}`);
+ }
+ }
+ }
+
+ window.history.replaceState('', '', `/search?q=${encodeURIComponent(query)}&type=${searchType}${searchType === 'user' ? `&origin=${searchOrigin}` : ''}`);
+};
+
+const notePagination = {
endpoint: 'notes/search' as const,
limit: 10,
params: computed(() => ({
- query: props.query,
+ query: searchQuery,
channelId: props.channel,
})),
};
+const userPagination = {
+ endpoint: 'users/search' as const,
+ limit: 10,
+ params: computed(() => ({
+ query: searchQuery,
+ origin: searchOrigin,
+ })),
+};
const headerActions = $computed(() => []);
const headerTabs = $computed(() => []);
definePageMetadata(computed(() => ({
- title: i18n.t('searchWith', { q: props.query }),
+ title: searchQuery ? i18n.t('searchWith', { q: searchQuery }) : i18n.ts.search,
icon: 'ti ti-search',
})));
</script>
diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts
index 9521e01910..70576688b1 100644
--- a/packages/frontend/src/router.ts
+++ b/packages/frontend/src/router.ts
@@ -213,6 +213,8 @@ export const routes = [{
query: {
q: 'query',
channel: 'channel',
+ type: 'type',
+ origin: 'origin',
},
}, {
path: '/authorize-follow',
diff --git a/packages/frontend/src/scripts/search.ts b/packages/frontend/src/scripts/search.ts
deleted file mode 100644
index 69f1586b77..0000000000
--- a/packages/frontend/src/scripts/search.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { mainRouter } from '@/router';
-
-export async function search() {
- const { canceled, result: query } = await os.inputText({
- title: i18n.ts.search,
- });
- if (canceled || query == null || query === '') return;
-
- const q = query.trim();
-
- if (q.startsWith('@') && !q.includes(' ')) {
- mainRouter.push(`/${q}`);
- return;
- }
-
- if (q.startsWith('#')) {
- mainRouter.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);
- }
-
- // TODO
- //v.$root.$emit('warp', date);
- os.alert({
- icon: 'ti ti-history',
- iconOnly: true, autoClose: true,
- });
- return;
- }
-
- if (q.startsWith('https://')) {
- const promise = os.api('ap/show', {
- uri: q,
- });
-
- os.promiseDialog(promise, null, null, i18n.ts.fetchingAsApObject);
-
- const res = await promise;
-
- if (res.type === 'User') {
- mainRouter.push(`/@${res.object.username}@${res.object.host}`);
- } else if (res.type === 'Note') {
- mainRouter.push(`/notes/${res.object.id}`);
- }
-
- return;
- }
-
- mainRouter.push(`/search?q=${encodeURIComponent(q)}`);
-}
diff --git a/packages/frontend/src/ui/classic.header.vue b/packages/frontend/src/ui/classic.header.vue
index 34ddfa1d32..3dfb371d32 100644
--- a/packages/frontend/src/ui/classic.header.vue
+++ b/packages/frontend/src/ui/classic.header.vue
@@ -45,11 +45,11 @@
import { defineAsyncComponent, defineComponent } from 'vue';
import { openInstanceMenu } from './_common_/common';
import { host } from '@/config';
-import { search } from '@/scripts/search';
import * as os from '@/os';
import { navbarItemDef } from '@/navbar';
import { openAccountMenu } from '@/account';
import MkButton from '@/components/MkButton.vue';
+import { mainRouter } from '@/router';
export default defineComponent({
components: {
@@ -103,7 +103,7 @@ export default defineComponent({
},
search() {
- search();
+ mainRouter.push('/search');
},
more(ev) {
diff --git a/packages/frontend/src/ui/classic.sidebar.vue b/packages/frontend/src/ui/classic.sidebar.vue
index a11c2ba10e..6fff233ac5 100644
--- a/packages/frontend/src/ui/classic.sidebar.vue
+++ b/packages/frontend/src/ui/classic.sidebar.vue
@@ -44,12 +44,12 @@
import { defineAsyncComponent, defineComponent } from 'vue';
import { openInstanceMenu } from './_common_/common';
import { host } from '@/config';
-import { search } from '@/scripts/search';
import * as os from '@/os';
import { navbarItemDef } from '@/navbar';
import { openAccountMenu } from '@/account';
import MkButton from '@/components/MkButton.vue';
import { StickySidebar } from '@/scripts/sticky-sidebar';
+import { mainRouter } from '@/router';
//import MisskeyLogo from '@assets/client/misskey.svg';
export default defineComponent({
@@ -120,7 +120,7 @@ export default defineComponent({
},
search() {
- search();
+ mainRouter.push('/search');
},
more(ev) {
diff --git a/packages/frontend/src/ui/visitor/a.vue b/packages/frontend/src/ui/visitor/a.vue
index 9494b1b705..023b7fdb94 100644
--- a/packages/frontend/src/ui/visitor/a.vue
+++ b/packages/frontend/src/ui/visitor/a.vue
@@ -40,7 +40,6 @@
import { defineComponent } from 'vue';
import XHeader from './header.vue';
import { host, instanceName } from '@/config';
-import { search } from '@/scripts/search';
import * as os from '@/os';
import MkButton from '@/components/MkButton.vue';
import { ColdDeviceStorage } from '@/store';
@@ -77,7 +76,9 @@ export default defineComponent({
if (ColdDeviceStorage.get('syncDeviceDarkMode')) return;
this.$store.set('darkMode', !this.$store.state.darkMode);
},
- 's': search,
+ 's': () => {
+ mainRouter.push('/search');
+ },
'h|/': this.help,
};
},
diff --git a/packages/frontend/src/ui/visitor/b.vue b/packages/frontend/src/ui/visitor/b.vue
index 163f038b43..e2168768e8 100644
--- a/packages/frontend/src/ui/visitor/b.vue
+++ b/packages/frontend/src/ui/visitor/b.vue
@@ -58,7 +58,6 @@ import { ComputedRef, onMounted, provide } from 'vue';
import XHeader from './header.vue';
import XKanban from './kanban.vue';
import { host, instanceName } from '@/config';
-import { search } from '@/scripts/search';
import * as os from '@/os';
import { instance } from '@/instance';
import XSigninDialog from '@/components/MkSigninDialog.vue';
@@ -97,7 +96,9 @@ const keymap = $computed(() => {
if (ColdDeviceStorage.get('syncDeviceDarkMode')) return;
defaultStore.set('darkMode', !defaultStore.state.darkMode);
},
- 's': search,
+ 's': () => {
+ mainRouter.push('/search');
+ },
};
});
diff --git a/packages/frontend/src/ui/visitor/header.vue b/packages/frontend/src/ui/visitor/header.vue
index 2647d0e62a..aaa7e77e90 100644
--- a/packages/frontend/src/ui/visitor/header.vue
+++ b/packages/frontend/src/ui/visitor/header.vue
@@ -27,7 +27,7 @@ import XSigninDialog from '@/components/MkSigninDialog.vue';
import XSignupDialog from '@/components/MkSignupDialog.vue';
import * as os from '@/os';
import { instance } from '@/instance';
-import { search } from '@/scripts/search';
+import { mainRouter } from '@/router';
export default defineComponent({
data() {
@@ -55,7 +55,9 @@ export default defineComponent({
}, {}, 'closed');
},
- search,
+ search() {
+ mainRouter.push('/search');
+ },
},
});
</script>