Fix tags autocomplete

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2021-09-10 11:29:28 +02:00
parent 6bb0b6d08a
commit e9e12500dc
No known key found for this signature in database
GPG key ID: A061B9DDE0CA0773
7 changed files with 84 additions and 48 deletions

View file

@ -29,19 +29,28 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator"; import { Component, Prop, Vue } from "vue-property-decorator";
import get from "lodash/get";
import differenceBy from "lodash/differenceBy"; import differenceBy from "lodash/differenceBy";
import { ITag } from "../../types/tag.model"; import { ITag } from "../../types/tag.model";
import { FILTER_TAGS } from "@/graphql/tags";
@Component @Component({
apollo: {
tags: {
query: FILTER_TAGS,
variables() {
return {
filter: this.text,
};
},
},
},
})
export default class TagInput extends Vue { export default class TagInput extends Vue {
@Prop({ required: false, default: () => [] }) data!: ITag[];
@Prop({ required: true, default: "value" }) path!: string;
@Prop({ required: true }) value!: ITag[]; @Prop({ required: true }) value!: ITag[];
filteredTags: ITag[] = []; tags!: ITag[];
text = "";
private static componentId = 0; private static componentId = 0;
@ -53,13 +62,20 @@ export default class TagInput extends Vue {
return `tag-input-${TagInput.componentId}`; return `tag-input-${TagInput.componentId}`;
} }
getFilteredTags(text: string): void { async getFilteredTags(text: string): Promise<void> {
this.filteredTags = differenceBy(this.data, this.value, "id").filter( this.text = text;
await this.$apollo.queries.tags.refetch();
}
get filteredTags(): ITag[] {
return differenceBy(this.tags, this.value, "id").filter(
(option) => (option) =>
get(option, this.path) option.title
.toString() .toString()
.toLowerCase() .toLowerCase()
.indexOf(text.toLowerCase()) >= 0 .indexOf(this.text.toLowerCase()) >= 0 ||
option.slug.toString().toLowerCase().indexOf(this.text.toLowerCase()) >=
0
); );
} }

View file

@ -9,16 +9,22 @@ export const TAG_FRAGMENT = gql`
`; `;
export const TAGS = gql` export const TAGS = gql`
query { query Tags {
tags { tags {
id
related { related {
id ...TagFragment
slug
title
} }
slug ...TagFragment
title
} }
} }
${TAG_FRAGMENT}
`;
export const FILTER_TAGS = gql`
query FilterTags($filter: String) {
tags(filter: $filter) {
...TagFragment
}
}
${TAG_FRAGMENT}
`; `;

View file

@ -31,7 +31,7 @@
/> />
</b-field> </b-field>
<tag-input v-model="event.tags" :data="tags" path="title" /> <tag-input v-model="event.tags" />
<b-field <b-field
horizontal horizontal
@ -556,8 +556,6 @@ import {
IPerson, IPerson,
usernameWithDomain, usernameWithDomain,
} from "../../types/actor"; } from "../../types/actor";
import { TAGS } from "../../graphql/tags";
import { ITag } from "../../types/tag.model";
import { import {
buildFileFromIMedia, buildFileFromIMedia,
buildFileVariable, buildFileVariable,
@ -590,7 +588,6 @@ const DEFAULT_LIMIT_NUMBER_OF_PLACES = 10;
}, },
apollo: { apollo: {
currentActor: CURRENT_ACTOR_CLIENT, currentActor: CURRENT_ACTOR_CLIENT,
tags: TAGS,
config: CONFIG, config: CONFIG,
identities: IDENTITIES, identities: IDENTITIES,
event: { event: {
@ -643,8 +640,6 @@ export default class EditEvent extends Vue {
currentActor!: IActor; currentActor!: IActor;
tags: ITag[] = [];
event: IEvent = new EventModel(); event: IEvent = new EventModel();
unmodifiedEvent: IEvent = new EventModel(); unmodifiedEvent: IEvent = new EventModel();

View file

@ -67,7 +67,7 @@
/> />
</b-field> </b-field>
<tag-input v-model="editablePost.tags" :data="tags" path="title" /> <tag-input v-model="editablePost.tags" />
<div class="field"> <div class="field">
<label class="label">{{ $t("Post") }}</label> <label class="label">{{ $t("Post") }}</label>
@ -166,7 +166,6 @@ import {
} from "@/utils/image"; } from "@/utils/image";
import GroupMixin from "@/mixins/group"; import GroupMixin from "@/mixins/group";
import { PostVisibility } from "@/types/enums"; import { PostVisibility } from "@/types/enums";
import { TAGS } from "../../graphql/tags";
import { CONFIG } from "../../graphql/config"; import { CONFIG } from "../../graphql/config";
import { import {
FETCH_POST, FETCH_POST,
@ -187,7 +186,6 @@ import { FETCH_GROUP } from "@/graphql/group";
@Component({ @Component({
apollo: { apollo: {
tags: TAGS,
config: CONFIG, config: CONFIG,
group: { group: {
query: FETCH_GROUP, query: FETCH_GROUP,

View file

@ -7,8 +7,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Tag do
alias Mobilizon.Events.{Event, Tag} alias Mobilizon.Events.{Event, Tag}
alias Mobilizon.Posts.Post alias Mobilizon.Posts.Post
def list_tags(_parent, %{page: page, limit: limit}, _resolution) do def list_tags(_parent, %{page: page, limit: limit} = args, _resolution) do
tags = Mobilizon.Events.list_tags(page, limit) filter = Map.get(args, :filter)
tags = Mobilizon.Events.list_tags(filter, page, limit)
{:ok, tags} {:ok, tags}
end end

View file

@ -6,33 +6,33 @@ defmodule Mobilizon.GraphQL.Resolvers.TagTest do
alias Mobilizon.GraphQL.AbsintheHelpers alias Mobilizon.GraphQL.AbsintheHelpers
describe "Tag Resolver" do describe "Tag Resolver" do
test "list_tags/3 returns the list of tags", context do @tags_query """
query Tags($filter: String) {
tags(filter: $filter) {
id
related {
id
slug
title
}
slug
title
}
}
"""
test "list_tags/3 returns the list of tags", %{conn: conn} do
tag1 = insert(:tag) tag1 = insert(:tag)
tag2 = insert(:tag) tag2 = insert(:tag)
tag3 = insert(:tag) tag3 = insert(:tag)
insert(:tag_relation, tag: tag1, link: tag2) insert(:tag_relation, tag: tag1, link: tag2)
insert(:tag_relation, tag: tag3, link: tag1) insert(:tag_relation, tag: tag3, link: tag1)
query = """
{
tags {
id,
slug,
title,
related {
id,
title,
slug
}
}
}
"""
res = res =
context.conn conn
|> get("/api", AbsintheHelpers.query_skeleton(query, "tags")) |> AbsintheHelpers.graphql_query(query: @tags_query)
tags = json_response(res, 200)["data"]["tags"] tags = res["data"]["tags"]
assert tags |> length == 3 assert tags |> length == 3
assert tags assert tags
@ -45,5 +45,19 @@ defmodule Mobilizon.GraphQL.Resolvers.TagTest do
|> Enum.map(fn tag -> tag.slug end) |> Enum.map(fn tag -> tag.slug end)
|> MapSet.new() |> MapSet.new()
end end
test "list_tags/3 returns tags for a filter", %{conn: conn} do
tag1 = insert(:tag, title: "PineApple", slug: "pineapple")
tag2 = insert(:tag, title: "sexy pineapple", slug: "sexy-pineapple")
_tag3 = insert(:tag)
res =
conn
|> AbsintheHelpers.graphql_query(query: @tags_query, variables: %{filter: "apple"})
tags = res["data"]["tags"]
assert tags |> length == 2
assert [tag1.id, tag2.id] == tags |> Enum.map(&String.to_integer(&1["id"]))
end
end end
end end

View file

@ -242,6 +242,12 @@ defmodule Mobilizon.EventsTest do
assert [tag.id] == Events.list_tags() |> Enum.map(& &1.id) assert [tag.id] == Events.list_tags() |> Enum.map(& &1.id)
end end
test "list_tags/1 filters tags by title or slug" do
tag1 = insert(:tag, title: "PineApple", slug: "pineapple")
tag2 = insert(:tag, title: "sexy pineapple", slug: "sexy-pineapple")
assert [tag1.id, tag2.id] == Events.list_tags("apple") |> Enum.map(& &1.id)
end
test "get_tag!/1 returns the tag with given id" do test "get_tag!/1 returns the tag with given id" do
tag = insert(:tag) tag = insert(:tag)
assert Events.get_tag!(tag.id).id == tag.id assert Events.get_tag!(tag.id).id == tag.id