mirror of
https://iceshrimp.dev/limepotato/jormungandr-bite.git
synced 2024-11-25 03:17:38 -07:00
[client] Add proper search page
This commit is contained in:
parent
855409332b
commit
ee4b58dee8
10 changed files with 332 additions and 276 deletions
|
@ -1131,6 +1131,7 @@ verifiedLink: "Verified link"
|
|||
openInMainColumn: "Open in main column"
|
||||
searchNotLoggedIn_1: "You have to be authenticated in order to use full text search."
|
||||
searchNotLoggedIn_2: "However, you can search using hashtags, and search users."
|
||||
searchEmptyQuery: "Please enter a serch term."
|
||||
|
||||
_sensitiveMediaDetection:
|
||||
description: "Reduces the effort of server moderation through automatically recognizing
|
||||
|
|
|
@ -98,15 +98,6 @@
|
|||
"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="input.type === 'search'" #suffix>
|
||||
<button
|
||||
v-tooltip.noDelay="i18n.ts.filter"
|
||||
class="_buttonIcon"
|
||||
@click.stop="openSearchFilters"
|
||||
>
|
||||
<i class="ph-funnel ph-bold"></i>
|
||||
</button>
|
||||
</template>
|
||||
</MkInput>
|
||||
<MkTextarea
|
||||
v-if="input && input.type === 'paragraph'"
|
||||
|
@ -200,15 +191,12 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { onBeforeUnmount, onMounted, ref, shallowRef } from "vue";
|
||||
import * as Acct from "iceshrimp-js/built/acct";
|
||||
import MkModal from "@/components/MkModal.vue";
|
||||
import MkButton from "@/components/MkButton.vue";
|
||||
import MkInput from "@/components/form/input.vue";
|
||||
import MkTextarea from "@/components/form/textarea.vue";
|
||||
import MkSelect from "@/components/form/select.vue";
|
||||
import * as os from "@/os";
|
||||
import { i18n } from "@/i18n";
|
||||
import XSearchFilterDialog from "@/components/MkSearchFilterDialog.vue";
|
||||
|
||||
interface Input {
|
||||
type: HTMLInputElement["type"];
|
||||
|
@ -347,179 +335,6 @@ function onInputKeydown(evt: KeyboardEvent) {
|
|||
}
|
||||
}
|
||||
|
||||
function formatDateToYYYYMMDD(date) {
|
||||
const year = date.getFullYear();
|
||||
const month = ("0" + (date.getMonth() + 1)).slice(-2);
|
||||
const day = ("0" + (date.getDate() + 1)).slice(-2);
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
function appendSearchFilter(filter: string, trailingSpace: boolean = true) {
|
||||
if (typeof inputValue.value !== "string") inputValue.value = "";
|
||||
if (inputValue.value.length > 0 && inputValue.value.at(inputValue.value.length - 1) !== " ") inputValue.value += " ";
|
||||
inputValue.value += filter;
|
||||
if (trailingSpace) inputValue.value += " ";
|
||||
}
|
||||
|
||||
async function openSearchFilters(ev) {
|
||||
await os.popupMenu(
|
||||
[
|
||||
{
|
||||
icon: "ph-user ph-bold ph-lg",
|
||||
text: i18n.ts._filters.fromUser,
|
||||
action: () => {
|
||||
os.selectUser().then((user) => {
|
||||
appendSearchFilter(`from:${Acct.toString(user)}`);
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "ph-at ph-bold ph-lg",
|
||||
text: i18n.ts._filters.mentioning,
|
||||
action: () => {
|
||||
os.selectUser().then((user) => {
|
||||
appendSearchFilter(`mention:${Acct.toString(user)}`);
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "ph-arrow-u-up-left ph-bold ph-lg",
|
||||
text: i18n.ts._filters.replyTo,
|
||||
action: () => {
|
||||
os.selectUser().then((user) => {
|
||||
appendSearchFilter(`reply:${Acct.toString(user)}`);
|
||||
});
|
||||
},
|
||||
},
|
||||
null,
|
||||
{
|
||||
icon: "ph-eye ph-bold ph-lg",
|
||||
text: i18n.ts._filters.followingOnly,
|
||||
action: () => {
|
||||
appendSearchFilter("filter:following");
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "ph-users-three ph-bold ph-lg",
|
||||
text: i18n.ts._filters.followersOnly,
|
||||
action: () => {
|
||||
appendSearchFilter("filter:followers");
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "ph-link ph-bold ph-lg",
|
||||
text: i18n.ts._filters.fromDomain,
|
||||
action: () => {
|
||||
appendSearchFilter("instance:", false);
|
||||
},
|
||||
},
|
||||
null,
|
||||
{
|
||||
type: "parent",
|
||||
text: i18n.ts._filters.withFile,
|
||||
icon: "ph-paperclip ph-bold ph-lg",
|
||||
children: [
|
||||
{
|
||||
text: i18n.ts.image,
|
||||
icon: "ph-image-square ph-bold ph-lg",
|
||||
action: () => {
|
||||
appendSearchFilter("has:image");
|
||||
},
|
||||
},
|
||||
{
|
||||
text: i18n.ts.video,
|
||||
icon: "ph-video-camera ph-bold ph-lg",
|
||||
action: () => {
|
||||
appendSearchFilter("has:video");
|
||||
},
|
||||
},
|
||||
{
|
||||
text: i18n.ts.audio,
|
||||
icon: "ph-music-note ph-bold ph-lg",
|
||||
action: () => {
|
||||
appendSearchFilter("has:audio");
|
||||
},
|
||||
},
|
||||
{
|
||||
text: i18n.ts.file,
|
||||
icon: "ph-file ph-bold ph-lg",
|
||||
action: () => {
|
||||
appendSearchFilter("has:file");
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
null,
|
||||
{
|
||||
icon: "ph-calendar-blank ph-bold ph-lg",
|
||||
text: i18n.ts._filters.notesBefore,
|
||||
action: () => {
|
||||
os.inputDate({
|
||||
title: i18n.ts._filters.notesBefore,
|
||||
}).then((res) => {
|
||||
if (res.canceled) return;
|
||||
appendSearchFilter("before:" + formatDateToYYYYMMDD(res.result));
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "ph-calendar-blank ph-bold ph-lg",
|
||||
text: i18n.ts._filters.notesAfter,
|
||||
action: () => {
|
||||
os.inputDate({
|
||||
title: i18n.ts._filters.notesAfter,
|
||||
}).then((res) => {
|
||||
if (res.canceled) return;
|
||||
appendSearchFilter("after:" + formatDateToYYYYMMDD(res.result));
|
||||
});
|
||||
},
|
||||
},
|
||||
null,
|
||||
{
|
||||
icon: "ph-arrow-u-up-left ph-bold ph-lg",
|
||||
text: i18n.ts._filters.excludeReplies,
|
||||
action: () => {
|
||||
appendSearchFilter("-filter:replies");
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "ph-repeat ph-bold ph-lg",
|
||||
text: i18n.ts._filters.excludeRenotes,
|
||||
action: () => {
|
||||
appendSearchFilter("-filter:renotes");
|
||||
},
|
||||
},
|
||||
null,
|
||||
{
|
||||
icon: "ph-text-aa ph-bold ph-lg",
|
||||
text: i18n.ts._filters.caseSensitive,
|
||||
action: () => {
|
||||
appendSearchFilter("case:sensitive");
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "ph-brackets-angle ph-bold ph-lg",
|
||||
text: i18n.ts._filters.matchWords,
|
||||
action: () => {
|
||||
appendSearchFilter("match:words");
|
||||
},
|
||||
},
|
||||
null,
|
||||
{
|
||||
icon: "ph-question ph-bold ph-lg",
|
||||
text: i18n.ts._filters._dialog.learnMore,
|
||||
action: () => {
|
||||
os.popup(XSearchFilterDialog, {}, {}, "closed");
|
||||
},
|
||||
},
|
||||
],
|
||||
ev.target,
|
||||
{ noReturnFocus: true },
|
||||
);
|
||||
inputEl.value!.focus();
|
||||
inputEl.value!.selectRange((inputValue.value as string).length, (inputValue.value as string).length); // cursor at end
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener("keydown", onKeydown);
|
||||
});
|
||||
|
|
239
packages/client/src/components/MkSearch.vue
Normal file
239
packages/client/src/components/MkSearch.vue
Normal file
|
@ -0,0 +1,239 @@
|
|||
<script setup lang="ts">
|
||||
import { i18n } from "@/i18n.js";
|
||||
import MkInput from "@/components/form/input.vue";
|
||||
import * as os from "@/os.js";
|
||||
import XSearchFilterDialog from "@/components/MkSearchFilterDialog.vue";
|
||||
import { onActivated, onMounted, onUnmounted, ref, toRefs } from "vue";
|
||||
import * as Acct from "iceshrimp-js/built/acct";
|
||||
|
||||
const props = defineProps<{
|
||||
query: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: "query", v: string): void;
|
||||
}>();
|
||||
|
||||
const query = ref(props.query);
|
||||
const input = ref<typeof MkInput>();
|
||||
|
||||
onActivated(() => {
|
||||
query.value = props.query;
|
||||
input.value!.focus();
|
||||
});
|
||||
|
||||
function formatDateToYYYYMMDD(date) {
|
||||
const year = date.getFullYear();
|
||||
const month = ("0" + (date.getMonth() + 1)).slice(-2);
|
||||
const day = ("0" + (date.getDate() + 1)).slice(-2);
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
function appendSearchFilter(filter: string, trailingSpace: boolean = true) {
|
||||
if (query.value.length > 0 && query.value.at(query.value.length - 1) !== " ") query.value += " ";
|
||||
query.value += filter;
|
||||
if (trailingSpace) query.value += " ";
|
||||
}
|
||||
|
||||
async function openSearchFilters(ev) {
|
||||
await os.popupMenu(
|
||||
[
|
||||
{
|
||||
icon: "ph-user ph-bold ph-lg",
|
||||
text: i18n.ts._filters.fromUser,
|
||||
action: () => {
|
||||
os.selectUser().then((user) => {
|
||||
appendSearchFilter(`from:${Acct.toString(user)}`);
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "ph-at ph-bold ph-lg",
|
||||
text: i18n.ts._filters.mentioning,
|
||||
action: () => {
|
||||
os.selectUser().then((user) => {
|
||||
appendSearchFilter(`mention:${Acct.toString(user)}`);
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "ph-arrow-u-up-left ph-bold ph-lg",
|
||||
text: i18n.ts._filters.replyTo,
|
||||
action: () => {
|
||||
os.selectUser().then((user) => {
|
||||
appendSearchFilter(`reply:${Acct.toString(user)}`);
|
||||
});
|
||||
},
|
||||
},
|
||||
null,
|
||||
{
|
||||
icon: "ph-eye ph-bold ph-lg",
|
||||
text: i18n.ts._filters.followingOnly,
|
||||
action: () => {
|
||||
appendSearchFilter("filter:following");
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "ph-users-three ph-bold ph-lg",
|
||||
text: i18n.ts._filters.followersOnly,
|
||||
action: () => {
|
||||
appendSearchFilter("filter:followers");
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "ph-link ph-bold ph-lg",
|
||||
text: i18n.ts._filters.fromDomain,
|
||||
action: () => {
|
||||
appendSearchFilter("instance:", false);
|
||||
},
|
||||
},
|
||||
null,
|
||||
{
|
||||
type: "parent",
|
||||
text: i18n.ts._filters.withFile,
|
||||
icon: "ph-paperclip ph-bold ph-lg",
|
||||
children: [
|
||||
{
|
||||
text: i18n.ts.image,
|
||||
icon: "ph-image-square ph-bold ph-lg",
|
||||
action: () => {
|
||||
appendSearchFilter("has:image");
|
||||
},
|
||||
},
|
||||
{
|
||||
text: i18n.ts.video,
|
||||
icon: "ph-video-camera ph-bold ph-lg",
|
||||
action: () => {
|
||||
appendSearchFilter("has:video");
|
||||
},
|
||||
},
|
||||
{
|
||||
text: i18n.ts.audio,
|
||||
icon: "ph-music-note ph-bold ph-lg",
|
||||
action: () => {
|
||||
appendSearchFilter("has:audio");
|
||||
},
|
||||
},
|
||||
{
|
||||
text: i18n.ts.file,
|
||||
icon: "ph-file ph-bold ph-lg",
|
||||
action: () => {
|
||||
appendSearchFilter("has:file");
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
null,
|
||||
{
|
||||
icon: "ph-calendar-blank ph-bold ph-lg",
|
||||
text: i18n.ts._filters.notesBefore,
|
||||
action: () => {
|
||||
os.inputDate({
|
||||
title: i18n.ts._filters.notesBefore,
|
||||
}).then((res) => {
|
||||
if (res.canceled) return;
|
||||
appendSearchFilter("before:" + formatDateToYYYYMMDD(res.result));
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "ph-calendar-blank ph-bold ph-lg",
|
||||
text: i18n.ts._filters.notesAfter,
|
||||
action: () => {
|
||||
os.inputDate({
|
||||
title: i18n.ts._filters.notesAfter,
|
||||
}).then((res) => {
|
||||
if (res.canceled) return;
|
||||
appendSearchFilter("after:" + formatDateToYYYYMMDD(res.result));
|
||||
});
|
||||
},
|
||||
},
|
||||
null,
|
||||
{
|
||||
icon: "ph-arrow-u-up-left ph-bold ph-lg",
|
||||
text: i18n.ts._filters.excludeReplies,
|
||||
action: () => {
|
||||
appendSearchFilter("-filter:replies");
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "ph-repeat ph-bold ph-lg",
|
||||
text: i18n.ts._filters.excludeRenotes,
|
||||
action: () => {
|
||||
appendSearchFilter("-filter:renotes");
|
||||
},
|
||||
},
|
||||
null,
|
||||
{
|
||||
icon: "ph-text-aa ph-bold ph-lg",
|
||||
text: i18n.ts._filters.caseSensitive,
|
||||
action: () => {
|
||||
appendSearchFilter("case:sensitive");
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "ph-brackets-angle ph-bold ph-lg",
|
||||
text: i18n.ts._filters.matchWords,
|
||||
action: () => {
|
||||
appendSearchFilter("match:words");
|
||||
},
|
||||
},
|
||||
null,
|
||||
{
|
||||
icon: "ph-question ph-bold ph-lg",
|
||||
text: i18n.ts._filters._dialog.learnMore,
|
||||
action: () => {
|
||||
os.popup(XSearchFilterDialog, {}, {}, "closed");
|
||||
},
|
||||
},
|
||||
],
|
||||
ev.target,
|
||||
{ noReturnFocus: true },
|
||||
);
|
||||
input.value!.focus();
|
||||
input.value!.selectRange((query.value as string).length, (query.value as string).length); // cursor at end
|
||||
}
|
||||
|
||||
function onInputKeydown(evt: KeyboardEvent) {
|
||||
if (evt.key === "Enter") {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
emit('query', query.value);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<MkInput
|
||||
class="search"
|
||||
ref="input"
|
||||
v-model="query"
|
||||
style="flex: 1"
|
||||
type="text"
|
||||
autofocus
|
||||
:placeholder="i18n.ts.searchPlaceholder"
|
||||
:spellcheck="false"
|
||||
@keydown="onInputKeydown"
|
||||
>
|
||||
<template #prefix>
|
||||
<div>
|
||||
<i class="ph-magnifying-glass ph-bold"></i>
|
||||
</div>
|
||||
</template>
|
||||
<template #suffix>
|
||||
<button
|
||||
v-tooltip.noDelay="i18n.ts.filter"
|
||||
class="_buttonIcon"
|
||||
@click.stop="openSearchFilters"
|
||||
>
|
||||
<i class="ph-funnel ph-bold"></i>
|
||||
</button>
|
||||
</template>
|
||||
</MkInput>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.search {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
|
@ -43,7 +43,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";
|
||||
|
@ -428,7 +427,6 @@ function checkForSplash() {
|
|||
d: (): void => {
|
||||
defaultStore.set("darkMode", !defaultStore.state.darkMode);
|
||||
},
|
||||
s: search,
|
||||
};
|
||||
|
||||
if ($i) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { computed, ref, reactive } from "vue";
|
||||
import { $i } from "./account";
|
||||
import { search } from "@/scripts/search";
|
||||
import * as os from "@/os";
|
||||
import { i18n } from "@/i18n";
|
||||
import { ui } from "@/config";
|
||||
|
@ -53,7 +52,7 @@ export const navbarItemDef = reactive({
|
|||
search: {
|
||||
title: "search",
|
||||
icon: "ph-magnifying-glass ph-bold ph-lg",
|
||||
action: () => search(),
|
||||
to: "/search",
|
||||
},
|
||||
lists: {
|
||||
title: "lists",
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
:display-back-button="true"
|
||||
/></template>
|
||||
<MkSpacer :content-max="800">
|
||||
<MkSearch :query="query" @query="search"/>
|
||||
<swiper
|
||||
:round-lengths="true"
|
||||
:touch-angle="25"
|
||||
|
@ -26,7 +27,23 @@
|
|||
>
|
||||
<swiper-slide>
|
||||
<template v-if="$i">
|
||||
<XNotes ref="notes" :pagination="notesPagination" />
|
||||
<template v-if="query == null || query.trim().length < 1">
|
||||
<transition :name="$store.state.animation ? 'zoom' : ''" appear>
|
||||
<div class="_fullinfo" ref="notes">
|
||||
<img
|
||||
:src="instance.images.info"
|
||||
class="_ghost"
|
||||
alt="Info"
|
||||
/>
|
||||
<div>
|
||||
{{ i18n.ts.searchEmptyQuery }}
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
<template v-else>
|
||||
<XNotes ref="notes" :pagination="notesPagination" />
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<transition :name="$store.state.animation ? 'zoom' : ''" appear>
|
||||
|
@ -45,11 +62,27 @@
|
|||
</template>
|
||||
</swiper-slide>
|
||||
<swiper-slide>
|
||||
<XUserList
|
||||
ref="users"
|
||||
class="_gap"
|
||||
:pagination="usersPagination"
|
||||
/>
|
||||
<template v-if="query == null || query.trim().length < 1">
|
||||
<transition :name="$store.state.animation ? 'zoom' : ''" appear>
|
||||
<div class="_fullinfo" ref="notes">
|
||||
<img
|
||||
:src="instance.images.info"
|
||||
class="_ghost"
|
||||
alt="Info"
|
||||
/>
|
||||
<div>
|
||||
{{ i18n.ts.searchEmptyQuery }}
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
<template v-else>
|
||||
<XUserList
|
||||
ref="users"
|
||||
class="_gap"
|
||||
:pagination="usersPagination"
|
||||
/>
|
||||
</template>
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
</MkSpacer>
|
||||
|
@ -70,11 +103,19 @@ import { $i } from "@/account";
|
|||
import "swiper/scss";
|
||||
import "swiper/scss/virtual";
|
||||
import {instance} from "@/instance";
|
||||
import MkSearch from "@/components/MkSearch.vue";
|
||||
import { mainRouter } from "@/router.js";
|
||||
import * as os from "@/os.js";
|
||||
|
||||
const props = defineProps<{
|
||||
query: string;
|
||||
channel?: string;
|
||||
}>();
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
query: string;
|
||||
channel?: string;
|
||||
}>(),
|
||||
{
|
||||
query: ""
|
||||
}
|
||||
);
|
||||
|
||||
const notesPagination = {
|
||||
endpoint: "notes/search" as const,
|
||||
|
@ -134,8 +175,42 @@ onMounted(() => {
|
|||
|
||||
definePageMetadata(
|
||||
computed(() => ({
|
||||
title: i18n.t("searchWith", { q: props.query }),
|
||||
title: i18n.ts.search,
|
||||
icon: "ph-magnifying-glass ph-bold ph-lg",
|
||||
})),
|
||||
);
|
||||
|
||||
async function search(query: string) {
|
||||
const q = query.trim();
|
||||
|
||||
if (q.startsWith("@") && !q.includes(" ")) {
|
||||
mainRouter.push(`/${q}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (q.startsWith("#")) {
|
||||
mainRouter.push(`/tags/${encodeURIComponent(q.slice(1))}`);
|
||||
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)}`);
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,64 +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({
|
||||
type: "search",
|
||||
title: i18n.ts.search,
|
||||
placeholder: i18n.ts.searchPlaceholder,
|
||||
});
|
||||
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.slice(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({
|
||||
type: "waiting",
|
||||
});
|
||||
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)}`);
|
||||
}
|
|
@ -77,7 +77,6 @@
|
|||
import { defineAsyncComponent, defineComponent } from "vue";
|
||||
import XHeader from "./header.vue";
|
||||
import { host, instanceName } from "@/config";
|
||||
import { search } from "@/scripts/search";
|
||||
import * as os from "@/os";
|
||||
import MkPagination from "@/components/MkPagination.vue";
|
||||
import MkButton from "@/components/MkButton.vue";
|
||||
|
@ -118,7 +117,6 @@ export default defineComponent({
|
|||
if (ColdDeviceStorage.get("syncDeviceDarkMode")) return;
|
||||
this.$store.set("darkMode", !this.$store.state.darkMode);
|
||||
},
|
||||
s: search,
|
||||
"h|/": this.help,
|
||||
};
|
||||
},
|
||||
|
@ -168,7 +166,7 @@ export default defineComponent({
|
|||
help() {
|
||||
// TODO(thatonecalculator): popup with keybinds
|
||||
// window.open('https://misskey-hub.net/docs/keyboard-shortcut.md', '_blank');
|
||||
console.log("d = dark/light mode, s = search, p = post :3");
|
||||
console.log("d = dark/light mode, p = post :3");
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -53,10 +53,10 @@
|
|||
><i class="ph-image-square ph-bold ph-lg icon"></i
|
||||
>{{ i18n.ts.gallery }}</MkA
|
||||
>
|
||||
<button class="_button link" active-class="active" @click="search()">
|
||||
<MkA to="/search" class="link" active-class="active">
|
||||
<i class="ph-magnifying-glass ph-bold ph-lg icon"></i
|
||||
><span>{{ i18n.ts.search }}</span>
|
||||
</button>
|
||||
</MkA>
|
||||
<div class="action">
|
||||
<button class="_buttonPrimary" @click="signup()">
|
||||
{{ i18n.ts.signup }}
|
||||
|
@ -76,7 +76,6 @@ import { 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 MkPagination from "@/components/MkPagination.vue";
|
||||
|
@ -123,7 +122,6 @@ const keymap = $computed(() => {
|
|||
if (ColdDeviceStorage.get("syncDeviceDarkMode")) return;
|
||||
defaultStore.set("darkMode", !defaultStore.state.darkMode);
|
||||
},
|
||||
s: search,
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -26,10 +26,10 @@
|
|||
><i class="ph-image-square ph-bold ph-lg icon"></i
|
||||
>{{ i18n.ts.gallery }}</MkA
|
||||
>
|
||||
<button class="_button link" active-class="active" @click="search()">
|
||||
<MkA to="/search" class="link" active-class="active">
|
||||
<i class="ph-magnifying-glass ph-bold ph-lg icon"></i
|
||||
><span>{{ i18n.ts.search }}</span>
|
||||
</button>
|
||||
</MkA>
|
||||
<div v-if="info" class="page active link">
|
||||
<div class="title">
|
||||
<i v-if="info.icon" class="icon" :class="info.icon"></i>
|
||||
|
@ -106,7 +106,6 @@ 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 { i18n } from "@/i18n";
|
||||
|
||||
export default defineComponent({
|
||||
|
@ -154,8 +153,6 @@ export default defineComponent({
|
|||
"closed",
|
||||
);
|
||||
},
|
||||
|
||||
search,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
Loading…
Reference in a new issue