fix(front): fix fetching and rendering profile mentions and fetching tags
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
a8ddc90ea2
commit
895378a96b
|
@ -2,37 +2,32 @@ import { SEARCH_PERSONS } from "@/graphql/search";
|
|||
import { VueRenderer } from "@tiptap/vue-3";
|
||||
import tippy from "tippy.js";
|
||||
import MentionList from "./MentionList.vue";
|
||||
import { apolloClient } from "@/vue-apollo";
|
||||
import { apolloClient, waitApolloQuery } from "@/vue-apollo";
|
||||
import { IPerson } from "@/types/actor";
|
||||
import pDebounce from "p-debounce";
|
||||
import { MentionOptions } from "@tiptap/extension-mention";
|
||||
import { Editor } from "@tiptap/core";
|
||||
import { provideApolloClient, useQuery } from "@vue/apollo-composable";
|
||||
import { Paginate } from "@/types/paginate";
|
||||
import { onError } from "@apollo/client/link/error";
|
||||
|
||||
const fetchItems = (query: string): Promise<IPerson[]> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { onResult } = provideApolloClient(apolloClient)(() => {
|
||||
return useQuery<{ searchPersons: Paginate<IPerson> }>(
|
||||
SEARCH_PERSONS,
|
||||
() => ({
|
||||
variables: {
|
||||
searchText: query,
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
onResult(({ data }) => {
|
||||
resolve(data.searchPersons.elements);
|
||||
});
|
||||
|
||||
onError(reject);
|
||||
});
|
||||
|
||||
// // TipTap doesn't handle async for onFilter, hence the following line.
|
||||
// return result.data.searchPersons.elements;
|
||||
const fetchItems = async (query: string): Promise<IPerson[]> => {
|
||||
try {
|
||||
if (query === "") return [];
|
||||
const res = await waitApolloQuery(
|
||||
provideApolloClient(apolloClient)(() => {
|
||||
return useQuery<
|
||||
{ searchPersons: Paginate<IPerson> },
|
||||
{ searchText: string }
|
||||
>(SEARCH_PERSONS, () => ({
|
||||
searchText: query,
|
||||
}));
|
||||
})
|
||||
);
|
||||
return res.data.searchPersons.elements;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const debouncedFetchItems = pDebounce(fetchItems, 200);
|
||||
|
@ -68,6 +63,10 @@ const mentionOptions: MentionOptions = {
|
|||
editor: props.editor,
|
||||
});
|
||||
|
||||
if (!props.clientRect) {
|
||||
return;
|
||||
}
|
||||
|
||||
popup = tippy("body", {
|
||||
getReferenceClientRect: props.clientRect,
|
||||
appendTo: () => document.body,
|
||||
|
@ -86,6 +85,12 @@ const mentionOptions: MentionOptions = {
|
|||
});
|
||||
},
|
||||
onKeyDown(props: any) {
|
||||
if (props.event.key === "Escape") {
|
||||
popup[0].hide();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
return component.ref?.onKeyDown(props);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<template>
|
||||
<div class="items">
|
||||
<div class="relative border overflow-hidden dark:border-transparent">
|
||||
<button
|
||||
class="item"
|
||||
:class="{ 'is-selected': index === selectedIndex }"
|
||||
class="block w-full text-start bg-white dark:bg-violet-1 border py-1 px-2 rounded dark:border-transparent"
|
||||
:class="{ 'border-black dark:!border-white': index === selectedIndex }"
|
||||
v-for="(item, index) in items"
|
||||
:key="index"
|
||||
@click="selectItem(index)"
|
||||
|
@ -34,37 +34,37 @@ watch(
|
|||
}
|
||||
);
|
||||
|
||||
// const onKeyDown = ({ event }: { event: KeyboardEvent }): boolean => {
|
||||
// if (event.key === "ArrowUp") {
|
||||
// upHandler();
|
||||
// return true;
|
||||
// }
|
||||
const onKeyDown = ({ event }: { event: KeyboardEvent }): boolean => {
|
||||
if (event.key === "ArrowUp") {
|
||||
upHandler();
|
||||
return true;
|
||||
}
|
||||
|
||||
// if (event.key === "ArrowDown") {
|
||||
// downHandler();
|
||||
// return true;
|
||||
// }
|
||||
if (event.key === "ArrowDown") {
|
||||
downHandler();
|
||||
return true;
|
||||
}
|
||||
|
||||
// if (event.key === "Enter") {
|
||||
// enterHandler();
|
||||
// return true;
|
||||
// }
|
||||
if (event.key === "Enter") {
|
||||
enterHandler();
|
||||
return true;
|
||||
}
|
||||
|
||||
// return false;
|
||||
// };
|
||||
return false;
|
||||
};
|
||||
|
||||
// const upHandler = (): void => {
|
||||
// selectedIndex.value =
|
||||
// (selectedIndex.value + props.items.length - 1) % props.items.length;
|
||||
// };
|
||||
const upHandler = (): void => {
|
||||
selectedIndex.value =
|
||||
(selectedIndex.value + props.items.length - 1) % props.items.length;
|
||||
};
|
||||
|
||||
// const downHandler = (): void => {
|
||||
// selectedIndex.value = (selectedIndex.value + 1) % props.items.length;
|
||||
// };
|
||||
const downHandler = (): void => {
|
||||
selectedIndex.value = (selectedIndex.value + 1) % props.items.length;
|
||||
};
|
||||
|
||||
// const enterHandler = (): void => {
|
||||
// selectItem(selectedIndex.value);
|
||||
// };
|
||||
const enterHandler = (): void => {
|
||||
selectItem(selectedIndex.value);
|
||||
};
|
||||
|
||||
const selectItem = (index: number): void => {
|
||||
const item = props.items[index];
|
||||
|
@ -73,27 +73,8 @@ const selectItem = (index: number): void => {
|
|||
props.command({ id: usernameWithDomain(item) });
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
onKeyDown,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.items {
|
||||
position: relative;
|
||||
border-radius: 0.25rem;
|
||||
background: white;
|
||||
color: rgba(black, 0.8);
|
||||
overflow: hidden;
|
||||
font-size: 0.9rem;
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(0, 0, 0, 0.1),
|
||||
0px 10px 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.item {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -657,4 +657,8 @@ onBeforeUnmount(() => {
|
|||
.menubar__button.is-active {
|
||||
@apply bg-zinc-300 dark:bg-zinc-500;
|
||||
}
|
||||
|
||||
.mention[data-id] {
|
||||
@apply inline-block border border-zinc-600 dark:border-zinc-300 rounded py-0.5 px-1;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,23 +1,20 @@
|
|||
import { FILTER_TAGS } from "@/graphql/tags";
|
||||
import { ITag } from "@/types/tag.model";
|
||||
import { apolloClient } from "@/vue-apollo";
|
||||
import { apolloClient, waitApolloQuery } from "@/vue-apollo";
|
||||
import { provideApolloClient, useQuery } from "@vue/apollo-composable";
|
||||
|
||||
export function fetchTags(text: string): Promise<ITag[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { onResult, onError } = provideApolloClient(apolloClient)(() =>
|
||||
useQuery<{ tags: ITag[] }, { filter: string }>(FILTER_TAGS, {
|
||||
filter: text,
|
||||
})
|
||||
export async function fetchTags(text: string): Promise<ITag[]> {
|
||||
try {
|
||||
const res = await waitApolloQuery(
|
||||
provideApolloClient(apolloClient)(() =>
|
||||
useQuery<{ tags: ITag[] }, { filter: string }>(FILTER_TAGS, {
|
||||
filter: text,
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
onResult((result) => {
|
||||
if (result.loading) {
|
||||
return;
|
||||
}
|
||||
return resolve(result.data.tags);
|
||||
});
|
||||
|
||||
onError((error) => reject(error));
|
||||
});
|
||||
return res.data.tags;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -620,4 +620,8 @@ useHead({
|
|||
.event-description a {
|
||||
@apply inline-block p-1 bg-mbz-yellow-alt-200 text-black;
|
||||
}
|
||||
|
||||
.event-description .mention.h-card {
|
||||
@apply inline-block border border-zinc-600 dark:border-zinc-300 rounded py-0.5 px-1;
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Reference in a new issue