mirror of
https://iceshrimp.dev/crimekillz/trashposs
synced 2024-11-23 17:29:05 +01:00
Unified Content Discovery
This commit is contained in:
parent
6f57bce3aa
commit
77a9a49ca1
@ -278,6 +278,8 @@ uploadFromUrlRequested: "Upload angefordert"
|
||||
uploadFromUrlMayTakeTime: "Es kann einige Zeit dauern, bis das Hochladen abgeschlossen
|
||||
ist."
|
||||
explore: "Erkunden"
|
||||
discover: "Entdecken"
|
||||
reel: "Reel"
|
||||
messageRead: "Gelesen"
|
||||
noMoreHistory: "Es gibt keine weitere Historie"
|
||||
startMessaging: "Einen neuen Chat beginnen"
|
||||
|
@ -302,6 +302,8 @@ uploadFromUrlDescription: "URL of the file you want to upload"
|
||||
uploadFromUrlRequested: "Upload requested"
|
||||
uploadFromUrlMayTakeTime: "It may take some time until the upload is complete."
|
||||
explore: "Explore"
|
||||
discover: "Discover"
|
||||
reel: "Reel"
|
||||
messageRead: "Read"
|
||||
noMoreHistory: "There is no further history"
|
||||
startMessaging: "Start a new chat"
|
||||
|
@ -264,6 +264,8 @@ uploadFromUrlDescription: "アップロードしたいファイルのURL"
|
||||
uploadFromUrlRequested: "アップロードをリクエストしました"
|
||||
uploadFromUrlMayTakeTime: "アップロードが完了するまで時間がかかる場合があります。"
|
||||
explore: "みつける"
|
||||
discover: "Discover"
|
||||
reel: "Reel"
|
||||
messageRead: "既読"
|
||||
noMoreHistory: "これより過去の履歴はありません"
|
||||
startMessaging: "チャットを開始"
|
||||
|
@ -210,7 +210,7 @@ import { reactive, computed } from "vue";
|
||||
import XSettings from "@/pages/settings/profile.vue";
|
||||
import XModalWindow from "@/components/MkModalWindow.vue";
|
||||
import MkButton from "@/components/MkButton.vue";
|
||||
import XFeaturedUsers from "@/pages/explore.users.vue";
|
||||
import XFeaturedUsers from "@/pages/discover.users.vue";
|
||||
import XPostForm from "@/components/MkPostForm.vue";
|
||||
import MkSparkle from "@/components/MkSparkle.vue";
|
||||
import MkPushNotificationAllowButton from "@/components/MkPushNotificationAllowButton.vue";
|
||||
|
@ -38,15 +38,10 @@ export const navbarItemDef = reactive({
|
||||
indicated: computed(() => $i?.hasPendingReceivedFollowRequest),
|
||||
to: "/my/follow-requests",
|
||||
},
|
||||
explore: {
|
||||
title: "explore",
|
||||
icon: "ph-hash ph-bold ph-lg",
|
||||
to: "/explore",
|
||||
},
|
||||
search: {
|
||||
title: "search",
|
||||
icon: "ph-magnifying-glass ph-bold ph-lg",
|
||||
to: "/search",
|
||||
discover: {
|
||||
title: "discover",
|
||||
icon: "ph-lightning ph-bold ph-lg",
|
||||
to: "/discover",
|
||||
},
|
||||
lists: {
|
||||
title: "lists",
|
||||
@ -81,7 +76,7 @@ export const navbarItemDef = reactive({
|
||||
},
|
||||
gallery: {
|
||||
title: "gallery",
|
||||
icon: "ph-image-square ph-bold ph-lg",
|
||||
icon: "ph-film-strip ph-bold ph-lg",
|
||||
to: "/gallery",
|
||||
},
|
||||
channels: {
|
||||
|
342
packages/client/src/pages/discover.vue
Normal file
342
packages/client/src/pages/discover.vue
Normal file
@ -0,0 +1,342 @@
|
||||
<template>
|
||||
<MkStickyContainer>
|
||||
<template #header
|
||||
><MkPageHeader
|
||||
v-model:tab="tab"
|
||||
:actions="headerActions"
|
||||
:tabs="headerTabs"
|
||||
/></template>
|
||||
<div class="lznhrdub">
|
||||
<MkSpacer :content-max="1200">
|
||||
<MkSearch :query="searchQuery" :hideFilters="!$i || tab !== 'featured'" @query="search"/>
|
||||
<swiper
|
||||
:round-lengths="true"
|
||||
:touch-angle="25"
|
||||
:threshold="10"
|
||||
:centeredSlides="true"
|
||||
:modules="[Virtual]"
|
||||
:space-between="20"
|
||||
:virtual="true"
|
||||
:allow-touch-move="
|
||||
defaultStore.state.swipeOnMobile &&
|
||||
(deviceKind !== 'desktop' ||
|
||||
defaultStore.state.swipeOnDesktop)
|
||||
"
|
||||
@swiper="setSwiperRef"
|
||||
@slide-change="onSlideChange"
|
||||
>
|
||||
<swiper-slide>
|
||||
<template v-if="searchQuery == null || searchQuery.trim().length < 1">
|
||||
<XUsers />
|
||||
</template>
|
||||
<template v-else-if="tabs[swiperRef!.activeIndex] == 'users'">
|
||||
<XUserList
|
||||
ref="users"
|
||||
class="_gap"
|
||||
:pagination="usersSearchPagination"
|
||||
/>
|
||||
</template>
|
||||
</swiper-slide>
|
||||
<swiper-slide>
|
||||
<template v-if="$i">
|
||||
<template v-if="searchQuery == null || searchQuery.trim().length < 1">
|
||||
<XFeatured />
|
||||
</template>
|
||||
<template v-else-if="tabs[swiperRef!.activeIndex] == 'notes'">
|
||||
<XNotes ref="notes" :pagination="notesSearchPagination" />
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<XFeatured />
|
||||
</template>
|
||||
</swiper-slide>
|
||||
<swiper-slide>
|
||||
<div class="_content grwlizim featured">
|
||||
<MkChannelList
|
||||
key="featured"
|
||||
:pagination="chanTrendPagination"
|
||||
/>
|
||||
</div>
|
||||
</swiper-slide>
|
||||
<swiper-slide>
|
||||
<div class="rknalgpo">
|
||||
<MkPagination
|
||||
v-slot="{ items }"
|
||||
:pagination="pagesTrendPagination"
|
||||
>
|
||||
<MkPagePreview
|
||||
v-for="page in items"
|
||||
:key="page.id"
|
||||
class="ckltabjg"
|
||||
:page="page"
|
||||
/>
|
||||
</MkPagination>
|
||||
</div>
|
||||
</swiper-slide>
|
||||
<swiper-slide>
|
||||
<MkFolder class="_gap">
|
||||
<template #header
|
||||
><i class="ph-clock ph-bold ph-lg"></i>
|
||||
{{ i18n.ts.recentPosts }}</template
|
||||
>
|
||||
<MkPagination
|
||||
v-slot="{ items }"
|
||||
:pagination="recentReelsPagination"
|
||||
:disable-auto-load="true"
|
||||
>
|
||||
<div class="vfpdbgtk">
|
||||
<MkGalleryPostPreview
|
||||
v-for="post in items"
|
||||
:key="post.id"
|
||||
:post="post"
|
||||
class="post"
|
||||
/>
|
||||
</div>
|
||||
</MkPagination>
|
||||
</MkFolder>
|
||||
<MkFolder class="_gap">
|
||||
<template #header
|
||||
><i class="ph-fire-simple ph-bold ph-lg"></i>
|
||||
{{ i18n.ts.popularPosts }}</template
|
||||
>
|
||||
<MkPagination
|
||||
v-slot="{ items }"
|
||||
:pagination="popularReelsPagination"
|
||||
:disable-auto-load="true"
|
||||
>
|
||||
<div class="vfpdbgtk">
|
||||
<MkGalleryPostPreview
|
||||
v-for="post in items"
|
||||
:key="post.id"
|
||||
:post="post"
|
||||
class="post"
|
||||
/>
|
||||
</div>
|
||||
</MkPagination>
|
||||
</MkFolder>
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
</MkSpacer>
|
||||
</div>
|
||||
</MkStickyContainer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, watch, onMounted, onActivated } from "vue";
|
||||
import { Virtual } from "swiper/modules";
|
||||
import { Swiper, SwiperSlide } from "swiper/vue";
|
||||
import MkChannelPreview from "@/components/MkChannelPreview.vue";
|
||||
import MkChannelList from "@/components/MkChannelList.vue";
|
||||
import MkPagePreview from "@/components/MkPagePreview.vue";
|
||||
import MkGalleryPostPreview from "@/components/MkGalleryPostPreview.vue";
|
||||
import MkPagination from "@/components/MkPagination.vue";
|
||||
import XFeatured from "./discover.featured.vue";
|
||||
import XUsers from "./discover.users.vue";
|
||||
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||
import { deviceKind } from "@/scripts/device-kind";
|
||||
import { i18n } from "@/i18n";
|
||||
import { $i } from "@/account";
|
||||
import MkSearch from "@/components/MkSearch.vue";
|
||||
import XNotes from "@/components/MkNotes.vue";
|
||||
import XUserList from "@/components/MkUserList.vue";
|
||||
import { defaultStore } from "@/store";
|
||||
import "swiper/scss";
|
||||
import "swiper/scss/virtual";
|
||||
import { useRouter } from "@/router.js";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const tabs = ["users", "featured", "channels", "pages", "reel"];
|
||||
let tab = $ref(tabs[0]);
|
||||
watch($$(tab), () => syncSlide(tabs.indexOf(tab)));
|
||||
|
||||
const getUrlParams = () =>
|
||||
window.location.search
|
||||
.substring(1)
|
||||
.split("&")
|
||||
.reduce((result, query) => {
|
||||
const [k, v] = query.split("=");
|
||||
result[k] = decodeURIComponent(v?.replace('+', '%20'));
|
||||
return result;
|
||||
}, {});
|
||||
|
||||
let searchQuery = $ref<string>(getUrlParams()['q'] ?? "");
|
||||
let channel = $ref<string|undefined>(getUrlParams()['channel'] ?? undefined);
|
||||
|
||||
const headerActions = $computed(() => []);
|
||||
|
||||
const headerTabs = $computed(() => [
|
||||
{
|
||||
key: "users",
|
||||
icon: "ph-users ph-bold ph-lg",
|
||||
title: i18n.ts.users,
|
||||
},
|
||||
{
|
||||
key: "featured",
|
||||
icon: "ph-fire ph-bold ph-lg",
|
||||
title: i18n.ts.notes,
|
||||
},
|
||||
{
|
||||
key: "channels",
|
||||
icon: "ph-megaphone-simple ph-bold ph-lg",
|
||||
title: i18n.ts.channel,
|
||||
},
|
||||
{
|
||||
key: "pages",
|
||||
icon: "ph-file-text ph-bold ph-lg",
|
||||
title: i18n.ts.pages,
|
||||
},
|
||||
{
|
||||
key: "reel",
|
||||
icon: "ph-film-strip ph-bold ph-lg",
|
||||
title: i18n.ts.reel,
|
||||
},
|
||||
]);
|
||||
|
||||
const chanTrendPagination = {
|
||||
endpoint: "channels/featured" as const,
|
||||
limit: 10,
|
||||
noPaging: false,
|
||||
};
|
||||
|
||||
const pagesTrendPagination = {
|
||||
endpoint: "pages/featured" as const,
|
||||
limit: 10,
|
||||
};
|
||||
|
||||
const recentReelsPagination = {
|
||||
endpoint: "gallery/posts" as const,
|
||||
limit: 6,
|
||||
};
|
||||
|
||||
const popularReelsPagination = {
|
||||
endpoint: "gallery/featured" as const,
|
||||
limit: 5,
|
||||
};
|
||||
|
||||
const notesSearchPagination = {
|
||||
endpoint: "notes/search" as const,
|
||||
limit: 10,
|
||||
params: computed(() => ({
|
||||
query: searchQuery,
|
||||
channelId: channel,
|
||||
})),
|
||||
};
|
||||
|
||||
const usersSearchPagination = {
|
||||
endpoint: "users/search" as const,
|
||||
limit: 10,
|
||||
params: computed(() => ({
|
||||
query: searchQuery,
|
||||
origin: "combined",
|
||||
})),
|
||||
};
|
||||
|
||||
definePageMetadata(
|
||||
computed(() => ({
|
||||
title: i18n.ts.discover,
|
||||
icon: "ph-lightning ph-bold ph-lg",
|
||||
})),
|
||||
);
|
||||
|
||||
let swiperRef = null;
|
||||
|
||||
function setSwiperRef(swiper) {
|
||||
swiperRef = swiper;
|
||||
syncSlide(tabs.indexOf(tab));
|
||||
}
|
||||
|
||||
function onSlideChange() {
|
||||
tab = tabs[swiperRef.activeIndex];
|
||||
}
|
||||
|
||||
function syncSlide(index) {
|
||||
swiperRef.slideTo(index);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
syncSlide(tabs.indexOf(swiperRef.activeIndex));
|
||||
});
|
||||
|
||||
onActivated(() => {
|
||||
searchQuery = getUrlParams()['q'];
|
||||
channel = getUrlParams()['channel'] ?? undefined;
|
||||
|
||||
syncSlide(tabs.indexOf(tab));
|
||||
});
|
||||
|
||||
async function search(query: string) {
|
||||
const q = query.trim();
|
||||
|
||||
if (q.startsWith("@") && !q.includes(" ")) {
|
||||
router.push(`/${q}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (q.startsWith("#")) {
|
||||
router.push(`/tags/${encodeURIComponent(q.slice(1))}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (q.startsWith("http://") || 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") {
|
||||
router.push(`/@${res.object.username}@${res.object.host}`);
|
||||
} else if (res.type === "Note") {
|
||||
router.push(`/notes/${res.object.id}`);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
searchQuery = q;
|
||||
router.push(`/discover?q=${encodeURIComponent(q)}`);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.buttoncontainer {
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.vfpdbgtk {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||
grid-gap: 12px;
|
||||
margin: 0 var(--margin);
|
||||
|
||||
> .post {
|
||||
}
|
||||
}
|
||||
|
||||
.rknalgpo {
|
||||
> .buttoncontainer {
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
&.my .ckltabjg:first-child {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.ckltabjg:not(:last-child) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
@media (min-width: 500px) {
|
||||
.ckltabjg:not(:last-child) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,96 +0,0 @@
|
||||
<template>
|
||||
<MkStickyContainer>
|
||||
<template #header
|
||||
><MkPageHeader
|
||||
v-model:tab="tab"
|
||||
:actions="headerActions"
|
||||
:tabs="headerTabs"
|
||||
/></template>
|
||||
<div class="lznhrdub">
|
||||
<MkSpacer :content-max="1200">
|
||||
<swiper
|
||||
:round-lengths="true"
|
||||
:touch-angle="25"
|
||||
:threshold="10"
|
||||
:centeredSlides="true"
|
||||
:modules="[Virtual]"
|
||||
:space-between="20"
|
||||
:virtual="true"
|
||||
:allow-touch-move="
|
||||
defaultStore.state.swipeOnMobile &&
|
||||
(deviceKind !== 'desktop' ||
|
||||
defaultStore.state.swipeOnDesktop)
|
||||
"
|
||||
@swiper="setSwiperRef"
|
||||
@slide-change="onSlideChange"
|
||||
>
|
||||
<swiper-slide>
|
||||
<XUsers />
|
||||
</swiper-slide>
|
||||
<swiper-slide>
|
||||
<XFeatured />
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
</MkSpacer>
|
||||
</div>
|
||||
</MkStickyContainer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, watch, onMounted } from "vue";
|
||||
import { Virtual } from "swiper/modules";
|
||||
import { Swiper, SwiperSlide } from "swiper/vue";
|
||||
import XFeatured from "./explore.featured.vue";
|
||||
import XUsers from "./explore.users.vue";
|
||||
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||
import { deviceKind } from "@/scripts/device-kind";
|
||||
import { i18n } from "@/i18n";
|
||||
import { defaultStore } from "@/store";
|
||||
import "swiper/scss";
|
||||
import "swiper/scss/virtual";
|
||||
|
||||
const tabs = ["users", "featured"];
|
||||
let tab = $ref(tabs[0]);
|
||||
watch($$(tab), () => syncSlide(tabs.indexOf(tab)));
|
||||
|
||||
const headerActions = $computed(() => []);
|
||||
|
||||
const headerTabs = $computed(() => [
|
||||
{
|
||||
key: "users",
|
||||
icon: "ph-users ph-bold ph-lg",
|
||||
title: i18n.ts.users,
|
||||
},
|
||||
{
|
||||
key: "featured",
|
||||
icon: "ph-lightning ph-bold ph-lg",
|
||||
title: i18n.ts.featured,
|
||||
},
|
||||
]);
|
||||
|
||||
definePageMetadata(
|
||||
computed(() => ({
|
||||
title: i18n.ts.explore,
|
||||
icon: "ph-hash ph-bold ph-lg",
|
||||
})),
|
||||
);
|
||||
|
||||
let swiperRef = null;
|
||||
|
||||
function setSwiperRef(swiper) {
|
||||
swiperRef = swiper;
|
||||
syncSlide(tabs.indexOf(tab));
|
||||
}
|
||||
|
||||
function onSlideChange() {
|
||||
tab = tabs[swiperRef.activeIndex];
|
||||
}
|
||||
|
||||
function syncSlide(index) {
|
||||
swiperRef.slideTo(index);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
syncSlide(tabs.indexOf(swiperRef.activeIndex));
|
||||
});
|
||||
</script>
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<MkStickyContainer>
|
||||
<template #header
|
||||
><MkPageHeader :actions="headerActions" :tabs="headerTabs"
|
||||
><MkPageHeader :actions="headerActions" :tabs="headerTabs" :display-back-button="true"
|
||||
/></template>
|
||||
<MkSpacer :content-max="800" :margin-min="16" :margin-max="32">
|
||||
<FormSuspense :p="init" class="_formRoot">
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<MkStickyContainer>
|
||||
<template #header
|
||||
><MkPageHeader :actions="headerActions" :tabs="headerTabs"
|
||||
><MkPageHeader :actions="headerActions" :tabs="headerTabs" :display-back-button="true"
|
||||
/></template>
|
||||
<MkSpacer :content-max="1000" :margin-min="16" :margin-max="32">
|
||||
<div class="_root">
|
||||
|
@ -1,228 +0,0 @@
|
||||
<template>
|
||||
<MkStickyContainer>
|
||||
<template #header
|
||||
><MkPageHeader
|
||||
v-model:tab="tab"
|
||||
:actions="headerActions"
|
||||
:tabs="headerTabs"
|
||||
/></template>
|
||||
<MkSpacer :content-max="800">
|
||||
<MkSearch :query="searchQuery" :hideFilters="!$i || tab === 'users'" @query="search"/>
|
||||
<swiper
|
||||
:round-lengths="true"
|
||||
:touch-angle="25"
|
||||
:threshold="10"
|
||||
:centeredSlides="true"
|
||||
:modules="[Virtual]"
|
||||
:space-between="20"
|
||||
:virtual="true"
|
||||
:allow-touch-move="
|
||||
defaultStore.state.swipeOnMobile &&
|
||||
(deviceKind !== 'desktop' ||
|
||||
defaultStore.state.swipeOnDesktop)
|
||||
"
|
||||
@swiper="setSwiperRef"
|
||||
@slide-change="onSlideChange"
|
||||
>
|
||||
<swiper-slide>
|
||||
<template v-if="$i">
|
||||
<template v-if="searchQuery == null || searchQuery.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-if="tabs[swiperRef!.activeIndex] == 'notes'">
|
||||
<XNotes ref="notes" :pagination="notesPagination" />
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<transition :name="$store.state.animation ? 'zoom' : ''" appear>
|
||||
<div class="_fullinfo" ref="notes">
|
||||
<img
|
||||
:src="instance.images.info"
|
||||
class="_ghost"
|
||||
alt="Info"
|
||||
/>
|
||||
<div>
|
||||
{{ i18n.ts.searchNotLoggedIn_1 }}<br>
|
||||
{{ i18n.ts.searchNotLoggedIn_2 }}
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
</swiper-slide>
|
||||
<swiper-slide>
|
||||
<template v-if="searchQuery == null || searchQuery.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-if="tabs[swiperRef!.activeIndex] == 'users'">
|
||||
<XUserList
|
||||
ref="users"
|
||||
class="_gap"
|
||||
:pagination="usersPagination"
|
||||
/>
|
||||
</template>
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
</MkSpacer>
|
||||
</MkStickyContainer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, watch, onMounted, onActivated, ref } from "vue";
|
||||
import { Virtual } from "swiper/modules";
|
||||
import { Swiper, SwiperSlide } from "swiper/vue";
|
||||
import XNotes from "@/components/MkNotes.vue";
|
||||
import XUserList from "@/components/MkUserList.vue";
|
||||
import { i18n } from "@/i18n";
|
||||
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||
import { defaultStore } from "@/store";
|
||||
import { deviceKind } from "@/scripts/device-kind";
|
||||
import { $i } from "@/account";
|
||||
import "swiper/scss";
|
||||
import "swiper/scss/virtual";
|
||||
import {instance} from "@/instance";
|
||||
import MkSearch from "@/components/MkSearch.vue";
|
||||
import { useRouter } from "@/router.js";
|
||||
import * as os from "@/os.js";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const getUrlParams = () =>
|
||||
window.location.search
|
||||
.substring(1)
|
||||
.split("&")
|
||||
.reduce((result, query) => {
|
||||
const [k, v] = query.split("=");
|
||||
result[k] = decodeURIComponent(v?.replace('+', '%20'));
|
||||
return result;
|
||||
}, {});
|
||||
|
||||
let searchQuery = $ref<string>(getUrlParams()['q'] ?? "");
|
||||
let channel = $ref<string|undefined>(getUrlParams()['channel'] ?? undefined);
|
||||
|
||||
const notesPagination = {
|
||||
endpoint: "notes/search" as const,
|
||||
limit: 10,
|
||||
params: computed(() => ({
|
||||
query: searchQuery,
|
||||
channelId: channel,
|
||||
})),
|
||||
};
|
||||
|
||||
const usersPagination = {
|
||||
endpoint: "users/search" as const,
|
||||
limit: 10,
|
||||
params: computed(() => ({
|
||||
query: searchQuery,
|
||||
origin: "combined",
|
||||
})),
|
||||
};
|
||||
|
||||
const tabs = ["notes", "users"];
|
||||
let tab = $ref(tabs[0]);
|
||||
watch($$(tab), () => syncSlide(tabs.indexOf(tab)));
|
||||
|
||||
const headerActions = $computed(() => []);
|
||||
|
||||
const headerTabs = $computed(() => [
|
||||
{
|
||||
key: "notes",
|
||||
icon: "ph-magnifying-glass ph-bold ph-lg",
|
||||
title: i18n.ts.notes,
|
||||
},
|
||||
{
|
||||
key: "users",
|
||||
icon: "ph-users ph-bold ph-lg",
|
||||
title: i18n.ts.users,
|
||||
},
|
||||
]);
|
||||
|
||||
let swiperRef = null;
|
||||
|
||||
function setSwiperRef(swiper) {
|
||||
swiperRef = swiper;
|
||||
syncSlide(tabs.indexOf(tab));
|
||||
}
|
||||
|
||||
function onSlideChange() {
|
||||
tab = tabs[swiperRef.activeIndex];
|
||||
}
|
||||
|
||||
function syncSlide(index) {
|
||||
swiperRef.slideTo(index);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
syncSlide(tabs.indexOf(tab));
|
||||
});
|
||||
|
||||
onActivated(() => {
|
||||
searchQuery = getUrlParams()['q'];
|
||||
channel = getUrlParams()['channel'] ?? undefined;
|
||||
|
||||
syncSlide(tabs.indexOf(tab));
|
||||
});
|
||||
|
||||
definePageMetadata(
|
||||
computed(() => ({
|
||||
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(" ")) {
|
||||
router.push(`/${q}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (q.startsWith("#")) {
|
||||
router.push(`/tags/${encodeURIComponent(q.slice(1))}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (q.startsWith("http://") || 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") {
|
||||
router.push(`/@${res.object.username}@${res.object.host}`);
|
||||
} else if (res.type === "Note") {
|
||||
router.push(`/notes/${res.object.id}`);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
searchQuery = q;
|
||||
router.push(`/search?q=${encodeURIComponent(q)}`);
|
||||
}
|
||||
</script>
|
@ -337,16 +337,12 @@ export const routes = [
|
||||
loginRequired: true,
|
||||
},
|
||||
{
|
||||
path: "/explore/tags/:tag",
|
||||
component: page(() => import("./pages/explore.vue")),
|
||||
path: "/discover/tags/:tag",
|
||||
component: page(() => import("./pages/discover.vue")),
|
||||
},
|
||||
{
|
||||
path: "/explore",
|
||||
component: page(() => import("./pages/explore.vue")),
|
||||
},
|
||||
{
|
||||
path: "/search",
|
||||
component: page(() => import("./pages/search.vue")),
|
||||
path: "/discover",
|
||||
component: page(() => import("./pages/discover.vue")),
|
||||
},
|
||||
{
|
||||
path: "/authorize-follow",
|
||||
|
@ -15,8 +15,7 @@ const menuOptions = [
|
||||
"-",
|
||||
"snippets",
|
||||
"antennas",
|
||||
"explore",
|
||||
"search",
|
||||
"discover",
|
||||
"-",
|
||||
"achievements",
|
||||
"drive"
|
||||
@ -153,7 +152,6 @@ export const defaultStore = markRaw(
|
||||
arg: null,
|
||||
},
|
||||
},
|
||||
|
||||
overridedDeviceKind: {
|
||||
where: "device",
|
||||
default: null as null | "smartphone" | "tablet" | "desktop",
|
||||
|
@ -41,9 +41,9 @@
|
||||
><i class="ph-house ph-bold ph-lg icon"></i
|
||||
>{{ i18n.ts.home }}</MkA
|
||||
>
|
||||
<MkA to="/explore" class="link" active-class="active"
|
||||
><i class="ph-compass ph-bold ph-lg icon"></i
|
||||
>{{ i18n.ts.explore }}</MkA
|
||||
<MkA to="/discover" class="link" active-class="active"
|
||||
><i class="ph-lightning ph-bold ph-lg icon"></i
|
||||
>{{ i18n.ts.discover }}</MkA
|
||||
>
|
||||
<MkA to="/pages" class="link" active-class="active"
|
||||
><i class="ph-file-text ph-bold ph-lg icon"></i
|
||||
@ -53,10 +53,6 @@
|
||||
><i class="ph-image-square ph-bold ph-lg icon"></i
|
||||
>{{ i18n.ts.gallery }}</MkA
|
||||
>
|
||||
<MkA to="/search" class="link" active-class="active">
|
||||
<i class="ph-magnifying-glass ph-bold ph-lg icon"></i
|
||||
><span>{{ i18n.ts.search }}</span>
|
||||
</MkA>
|
||||
<MkA to="/settings" class="link" active-class="active">
|
||||
<i class="ph-gear-six ph-bold ph-lg icon"></i
|
||||
><span>{{ i18n.ts.settings }}</span>
|
||||
|
@ -14,9 +14,9 @@
|
||||
><i class="ph-chats-circle ph-bold ph-lg icon"></i
|
||||
>{{ i18n.ts.timeline }}</MkA
|
||||
> -->
|
||||
<MkA to="/explore" class="link" active-class="active"
|
||||
><i class="ph-compass ph-bold ph-lg icon"></i
|
||||
>{{ i18n.ts.explore }}</MkA
|
||||
<MkA to="/discover" class="link" active-class="active"
|
||||
><i class="ph-lightning ph-bold ph-lg icon"></i
|
||||
>{{ i18n.ts.discover }}</MkA
|
||||
>
|
||||
<MkA to="/pages" class="link" active-class="active"
|
||||
><i class="ph-file-text ph-bold ph-lg icon"></i
|
||||
@ -26,10 +26,6 @@
|
||||
><i class="ph-image-square ph-bold ph-lg icon"></i
|
||||
>{{ i18n.ts.gallery }}</MkA
|
||||
>
|
||||
<MkA to="/search" class="link" active-class="active">
|
||||
<i class="ph-magnifying-glass ph-bold ph-lg icon"></i
|
||||
><span>{{ i18n.ts.search }}</span>
|
||||
</MkA>
|
||||
<div v-if="info" class="page active link">
|
||||
<div class="title">
|
||||
<i v-if="info.icon" class="icon" :class="info.icon"></i>
|
||||
|
Loading…
Reference in New Issue
Block a user