Merge branch 'fix-nav-search-field' into 'master'
Fix nav search field Closes #450 See merge request framasoft/mobilizon!753
This commit is contained in:
commit
5f3531cc18
|
@ -44,7 +44,7 @@ type RefreshedToken {
|
||||||
}
|
}
|
||||||
|
|
||||||
"Represents an application"
|
"Represents an application"
|
||||||
type Application {
|
type Application implements Actor {
|
||||||
"Internal ID for this application"
|
"Internal ID for this application"
|
||||||
id: ID
|
id: ID
|
||||||
|
|
||||||
|
@ -336,7 +336,7 @@ type PaginatedPostList {
|
||||||
}
|
}
|
||||||
|
|
||||||
"A comment"
|
"A comment"
|
||||||
type Comment {
|
type Comment implements ActionLogObject {
|
||||||
"Internal ID for this comment"
|
"Internal ID for this comment"
|
||||||
id: ID
|
id: ID
|
||||||
|
|
||||||
|
@ -893,7 +893,7 @@ enum EventCommentModeration {
|
||||||
}
|
}
|
||||||
|
|
||||||
"Represents a person identity"
|
"Represents a person identity"
|
||||||
type Person {
|
type Person implements ActionLogObject & Actor {
|
||||||
"Internal ID for this person"
|
"Internal ID for this person"
|
||||||
id: ID
|
id: ID
|
||||||
|
|
||||||
|
@ -1949,7 +1949,7 @@ type RootQueryType {
|
||||||
|
|
||||||
"The limit of events per page"
|
"The limit of events per page"
|
||||||
limit: Int
|
limit: Int
|
||||||
): [Event]
|
): PaginatedEventList
|
||||||
|
|
||||||
"Get an event by uuid"
|
"Get an event by uuid"
|
||||||
event("The event's UUID" uuid: UUID!): Event
|
event("The event's UUID" uuid: UUID!): Event
|
||||||
|
@ -2219,7 +2219,7 @@ input EventOptionsInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
"A report object"
|
"A report object"
|
||||||
type Report {
|
type Report implements ActionLogObject {
|
||||||
"The internal ID of the report"
|
"The internal ID of the report"
|
||||||
id: ID
|
id: ID
|
||||||
|
|
||||||
|
@ -2285,7 +2285,7 @@ type PaginatedTodoListList {
|
||||||
}
|
}
|
||||||
|
|
||||||
"An event"
|
"An event"
|
||||||
type Event {
|
type Event implements Interactable & ActionLogObject {
|
||||||
"Internal ID for this event"
|
"Internal ID for this event"
|
||||||
id: ID
|
id: ID
|
||||||
|
|
||||||
|
@ -2737,7 +2737,7 @@ type Geocoding {
|
||||||
}
|
}
|
||||||
|
|
||||||
"A report note object"
|
"A report note object"
|
||||||
type ReportNote {
|
type ReportNote implements ActionLogObject {
|
||||||
"The internal ID of the report note"
|
"The internal ID of the report note"
|
||||||
id: ID
|
id: ID
|
||||||
|
|
||||||
|
@ -3052,7 +3052,7 @@ type Member {
|
||||||
}
|
}
|
||||||
|
|
||||||
"A local user of Mobilizon"
|
"A local user of Mobilizon"
|
||||||
type User {
|
type User implements ActionLogObject {
|
||||||
"The user's ID"
|
"The user's ID"
|
||||||
id: ID
|
id: ID
|
||||||
|
|
||||||
|
@ -3157,7 +3157,7 @@ type User {
|
||||||
}
|
}
|
||||||
|
|
||||||
"Represents a group of actors"
|
"Represents a group of actors"
|
||||||
type Group {
|
type Group implements Interactable & Actor {
|
||||||
"Internal ID for this group"
|
"Internal ID for this group"
|
||||||
id: ID
|
id: ID
|
||||||
|
|
||||||
|
|
|
@ -196,54 +196,54 @@ export const FETCH_EVENT_BASIC = gql`
|
||||||
export const FETCH_EVENTS = gql`
|
export const FETCH_EVENTS = gql`
|
||||||
query {
|
query {
|
||||||
events {
|
events {
|
||||||
id,
|
total
|
||||||
uuid,
|
elements {
|
||||||
url,
|
|
||||||
local,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
beginsOn,
|
|
||||||
endsOn,
|
|
||||||
status,
|
|
||||||
visibility,
|
|
||||||
picture {
|
|
||||||
id
|
id
|
||||||
|
uuid
|
||||||
url
|
url
|
||||||
},
|
local
|
||||||
publishAt,
|
title
|
||||||
# online_address,
|
description
|
||||||
# phone_address,
|
beginsOn
|
||||||
physicalAddress {
|
endsOn
|
||||||
id,
|
status
|
||||||
description,
|
visibility
|
||||||
locality
|
picture {
|
||||||
},
|
|
||||||
organizerActor {
|
|
||||||
id,
|
|
||||||
avatar {
|
|
||||||
id
|
id
|
||||||
url
|
url
|
||||||
},
|
}
|
||||||
preferredUsername,
|
publishAt
|
||||||
domain,
|
# online_address,
|
||||||
name,
|
# phone_address,
|
||||||
},
|
physicalAddress {
|
||||||
# attributedTo {
|
id
|
||||||
# avatar {
|
description
|
||||||
# id
|
locality
|
||||||
# url
|
}
|
||||||
# },
|
organizerActor {
|
||||||
# preferredUsername,
|
id
|
||||||
# name,
|
avatar {
|
||||||
# },
|
id
|
||||||
category,
|
url
|
||||||
participants {
|
}
|
||||||
${participantsQuery}
|
preferredUsername
|
||||||
},
|
domain
|
||||||
tags {
|
name
|
||||||
slug,
|
}
|
||||||
title
|
# attributedTo {
|
||||||
},
|
# avatar {
|
||||||
|
# id
|
||||||
|
# url
|
||||||
|
# },
|
||||||
|
# preferredUsername,
|
||||||
|
# name,
|
||||||
|
# },
|
||||||
|
category
|
||||||
|
tags {
|
||||||
|
slug
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -220,6 +220,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue, Watch } from "vue-property-decorator";
|
import { Component, Vue, Watch } from "vue-property-decorator";
|
||||||
import { ParticipantRole } from "@/types/enums";
|
import { ParticipantRole } from "@/types/enums";
|
||||||
|
import { Paginate } from "@/types/paginate";
|
||||||
import { IParticipant, Participant } from "../types/participant.model";
|
import { IParticipant, Participant } from "../types/participant.model";
|
||||||
import { FETCH_EVENTS } from "../graphql/event";
|
import { FETCH_EVENTS } from "../graphql/event";
|
||||||
import EventListCard from "../components/Event/EventListCard.vue";
|
import EventListCard from "../components/Event/EventListCard.vue";
|
||||||
|
@ -295,7 +296,7 @@ import Subtitle from "../components/Utils/Subtitle.vue";
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
export default class Home extends Vue {
|
export default class Home extends Vue {
|
||||||
events: IEvent[] = [];
|
events!: Paginate<IEvent>;
|
||||||
|
|
||||||
locations = [];
|
locations = [];
|
||||||
|
|
||||||
|
@ -437,7 +438,7 @@ export default class Home extends Vue {
|
||||||
* Return all events from server excluding the ones shown as participating
|
* Return all events from server excluding the ones shown as participating
|
||||||
*/
|
*/
|
||||||
get filteredFeaturedEvents(): IEvent[] {
|
get filteredFeaturedEvents(): IEvent[] {
|
||||||
return this.events.filter(
|
return this.events.elements.filter(
|
||||||
({ id }) =>
|
({ id }) =>
|
||||||
!this.currentUserParticipations
|
!this.currentUserParticipations
|
||||||
.filter(
|
.filter(
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
<option
|
<option
|
||||||
v-for="(option, index) in options"
|
v-for="(option, index) in options"
|
||||||
:key="index"
|
:key="index"
|
||||||
:value="option"
|
:value="index"
|
||||||
>
|
>
|
||||||
{{ option.label }}
|
{{ option.label }}
|
||||||
</option>
|
</option>
|
||||||
|
@ -56,20 +56,23 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="events-featured" v-if="!tag && searchEvents.initial">
|
<section
|
||||||
|
class="events-featured"
|
||||||
|
v-if="!tag && !(search || location.geom || when !== 'any')"
|
||||||
|
>
|
||||||
<b-loading :active.sync="$apollo.loading"></b-loading>
|
<b-loading :active.sync="$apollo.loading"></b-loading>
|
||||||
<h2 class="title">{{ $t("Featured events") }}</h2>
|
<h2 class="title">{{ $t("Featured events") }}</h2>
|
||||||
<div v-if="events.length > 0" class="columns is-multiline">
|
<div v-if="events.elements.length > 0" class="columns is-multiline">
|
||||||
<div
|
<div
|
||||||
class="column is-one-third-desktop"
|
class="column is-one-third-desktop"
|
||||||
v-for="event in events"
|
v-for="event in events.elements"
|
||||||
:key="event.uuid"
|
:key="event.uuid"
|
||||||
>
|
>
|
||||||
<EventCard :event="event" />
|
<EventCard :event="event" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<b-message
|
<b-message
|
||||||
v-else-if="events.length === 0 && $apollo.loading === false"
|
v-else-if="events.elements.length === 0 && $apollo.loading === false"
|
||||||
type="is-danger"
|
type="is-danger"
|
||||||
>{{ $t("No events found") }}</b-message
|
>{{ $t("No events found") }}</b-message
|
||||||
>
|
>
|
||||||
|
@ -109,15 +112,24 @@
|
||||||
<b-message v-else-if="$apollo.loading === false" type="is-danger">{{
|
<b-message v-else-if="$apollo.loading === false" type="is-danger">{{
|
||||||
$t("No events found")
|
$t("No events found")
|
||||||
}}</b-message>
|
}}</b-message>
|
||||||
|
<b-loading
|
||||||
|
v-else-if="$apollo.loading"
|
||||||
|
:is-full-page="false"
|
||||||
|
v-model="$apollo.loading"
|
||||||
|
:can-cancel="false"
|
||||||
|
/>
|
||||||
</b-tab-item>
|
</b-tab-item>
|
||||||
<b-tab-item v-if="config && config.features.groups">
|
<b-tab-item v-if="!tag">
|
||||||
<template slot="header">
|
<template slot="header">
|
||||||
<b-icon icon="account-multiple"></b-icon>
|
<b-icon icon="account-multiple"></b-icon>
|
||||||
<span>
|
<span>
|
||||||
{{ $t("Groups") }} <b-tag rounded>{{ searchGroups.total }}</b-tag>
|
{{ $t("Groups") }} <b-tag rounded>{{ searchGroups.total }}</b-tag>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<div v-if="searchGroups.total > 0">
|
<b-message v-if="config && !config.features.groups" type="is-danger">
|
||||||
|
{{ $t("Groups are not enabled on your server.") }}
|
||||||
|
</b-message>
|
||||||
|
<div v-else-if="searchGroups.total > 0">
|
||||||
<div class="columns is-multiline">
|
<div class="columns is-multiline">
|
||||||
<div
|
<div
|
||||||
class="column is-one-third-desktop"
|
class="column is-one-third-desktop"
|
||||||
|
@ -143,13 +155,19 @@
|
||||||
<b-message v-else-if="$apollo.loading === false" type="is-danger">
|
<b-message v-else-if="$apollo.loading === false" type="is-danger">
|
||||||
{{ $t("No groups found") }}
|
{{ $t("No groups found") }}
|
||||||
</b-message>
|
</b-message>
|
||||||
|
<b-loading
|
||||||
|
v-else-if="$apollo.loading"
|
||||||
|
:is-full-page="false"
|
||||||
|
v-model="$apollo.loading"
|
||||||
|
:can-cancel="false"
|
||||||
|
/>
|
||||||
</b-tab-item>
|
</b-tab-item>
|
||||||
</b-tabs>
|
</b-tabs>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Prop, Vue, Watch } from "vue-property-decorator";
|
import { Component, Prop, Vue } from "vue-property-decorator";
|
||||||
import ngeohash from "ngeohash";
|
import ngeohash from "ngeohash";
|
||||||
import {
|
import {
|
||||||
endOfToday,
|
endOfToday,
|
||||||
|
@ -165,6 +183,7 @@ import {
|
||||||
eachWeekendOfInterval,
|
eachWeekendOfInterval,
|
||||||
} from "date-fns";
|
} from "date-fns";
|
||||||
import { SearchTabs } from "@/types/enums";
|
import { SearchTabs } from "@/types/enums";
|
||||||
|
import { RawLocation } from "vue-router";
|
||||||
import EventCard from "../components/Event/EventCard.vue";
|
import EventCard from "../components/Event/EventCard.vue";
|
||||||
import { FETCH_EVENTS } from "../graphql/event";
|
import { FETCH_EVENTS } from "../graphql/event";
|
||||||
import { IEvent } from "../types/event.model";
|
import { IEvent } from "../types/event.model";
|
||||||
|
@ -183,11 +202,6 @@ interface ISearchTimeOption {
|
||||||
end?: Date | null;
|
end?: Date | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tabsName: { events: number; groups: number } = {
|
|
||||||
events: SearchTabs.EVENTS,
|
|
||||||
groups: SearchTabs.GROUPS,
|
|
||||||
};
|
|
||||||
|
|
||||||
const EVENT_PAGE_LIMIT = 10;
|
const EVENT_PAGE_LIMIT = 10;
|
||||||
|
|
||||||
const GROUP_PAGE_LIMIT = 10;
|
const GROUP_PAGE_LIMIT = 10;
|
||||||
|
@ -218,7 +232,7 @@ const GROUP_PAGE_LIMIT = 10;
|
||||||
},
|
},
|
||||||
debounce: 300,
|
debounce: 300,
|
||||||
skip() {
|
skip() {
|
||||||
return !this.search && !this.tag && !this.geohash && this.end === null;
|
return !this.tag && !this.geohash && this.end === null;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
searchGroups: {
|
searchGroups: {
|
||||||
|
@ -250,12 +264,14 @@ const GROUP_PAGE_LIMIT = 10;
|
||||||
export default class Search extends Vue {
|
export default class Search extends Vue {
|
||||||
@Prop({ type: String, required: false }) tag!: string;
|
@Prop({ type: String, required: false }) tag!: string;
|
||||||
|
|
||||||
events: IEvent[] = [];
|
events: Paginate<IEvent> = {
|
||||||
|
total: 0,
|
||||||
searchEvents: Paginate<IEvent> & { initial: boolean } = {
|
elements: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
searchEvents: Paginate<IEvent> = {
|
||||||
total: 0,
|
total: 0,
|
||||||
elements: [],
|
elements: [],
|
||||||
initial: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
searchGroups: Paginate<IGroup> = { total: 0, elements: [] };
|
searchGroups: Paginate<IGroup> = { total: 0, elements: [] };
|
||||||
|
@ -264,62 +280,51 @@ export default class Search extends Vue {
|
||||||
|
|
||||||
groupPage = 1;
|
groupPage = 1;
|
||||||
|
|
||||||
search: string = (this.$route.query.term as string) || "";
|
|
||||||
|
|
||||||
activeTab: SearchTabs =
|
|
||||||
tabsName[this.$route.query.searchType as "events" | "groups"] || 0;
|
|
||||||
|
|
||||||
location: IAddress = new Address();
|
location: IAddress = new Address();
|
||||||
|
|
||||||
options: ISearchTimeOption[] = [
|
options: Record<string, ISearchTimeOption> = {
|
||||||
{
|
today: {
|
||||||
label: this.$t("Today") as string,
|
label: this.$t("Today") as string,
|
||||||
start: new Date(),
|
start: new Date(),
|
||||||
end: endOfToday(),
|
end: endOfToday(),
|
||||||
},
|
},
|
||||||
{
|
tomorrow: {
|
||||||
label: this.$t("Tomorrow") as string,
|
label: this.$t("Tomorrow") as string,
|
||||||
start: startOfDay(addDays(new Date(), 1)),
|
start: startOfDay(addDays(new Date(), 1)),
|
||||||
end: endOfDay(addDays(new Date(), 1)),
|
end: endOfDay(addDays(new Date(), 1)),
|
||||||
},
|
},
|
||||||
{
|
weekend: {
|
||||||
label: this.$t("This weekend") as string,
|
label: this.$t("This weekend") as string,
|
||||||
start: this.weekend.start,
|
start: this.weekend.start,
|
||||||
end: this.weekend.end,
|
end: this.weekend.end,
|
||||||
},
|
},
|
||||||
{
|
week: {
|
||||||
label: this.$t("This week") as string,
|
label: this.$t("This week") as string,
|
||||||
start: new Date(),
|
start: new Date(),
|
||||||
end: endOfWeek(new Date(), { locale: this.$dateFnsLocale }),
|
end: endOfWeek(new Date(), { locale: this.$dateFnsLocale }),
|
||||||
},
|
},
|
||||||
{
|
next_week: {
|
||||||
label: this.$t("Next week") as string,
|
label: this.$t("Next week") as string,
|
||||||
start: startOfWeek(addWeeks(new Date(), 1), {
|
start: startOfWeek(addWeeks(new Date(), 1), {
|
||||||
locale: this.$dateFnsLocale,
|
locale: this.$dateFnsLocale,
|
||||||
}),
|
}),
|
||||||
end: endOfWeek(addWeeks(new Date(), 1), { locale: this.$dateFnsLocale }),
|
end: endOfWeek(addWeeks(new Date(), 1), { locale: this.$dateFnsLocale }),
|
||||||
},
|
},
|
||||||
{
|
month: {
|
||||||
label: this.$t("This month") as string,
|
label: this.$t("This month") as string,
|
||||||
start: new Date(),
|
start: new Date(),
|
||||||
end: endOfMonth(new Date()),
|
end: endOfMonth(new Date()),
|
||||||
},
|
},
|
||||||
{
|
next_month: {
|
||||||
label: this.$t("Next month") as string,
|
label: this.$t("Next month") as string,
|
||||||
start: startOfMonth(addMonths(new Date(), 1)),
|
start: startOfMonth(addMonths(new Date(), 1)),
|
||||||
end: endOfMonth(addMonths(new Date(), 1)),
|
end: endOfMonth(addMonths(new Date(), 1)),
|
||||||
},
|
},
|
||||||
{
|
any: {
|
||||||
label: this.$t("Any day") as string,
|
label: this.$t("Any day") as string,
|
||||||
start: undefined,
|
start: undefined,
|
||||||
end: undefined,
|
end: undefined,
|
||||||
},
|
},
|
||||||
];
|
|
||||||
|
|
||||||
when: ISearchTimeOption = {
|
|
||||||
label: this.$t("Any day") as string,
|
|
||||||
start: undefined,
|
|
||||||
end: null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
EVENT_PAGE_LIMIT = EVENT_PAGE_LIMIT;
|
EVENT_PAGE_LIMIT = EVENT_PAGE_LIMIT;
|
||||||
|
@ -335,26 +340,60 @@ export default class Search extends Vue {
|
||||||
|
|
||||||
radiusOptions: (number | null)[] = [1, 5, 10, 25, 50, 100, 150, null];
|
radiusOptions: (number | null)[] = [1, 5, 10, 25, 50, 100, 150, null];
|
||||||
|
|
||||||
radius = 50;
|
|
||||||
|
|
||||||
submit(): void {
|
submit(): void {
|
||||||
this.$apollo.queries.searchEvents.refetch();
|
this.$apollo.queries.searchEvents.refetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Watch("search")
|
get search(): string | undefined {
|
||||||
updateSearchTerm(): void {
|
return this.$route.query.term as string;
|
||||||
this.$router.push({
|
}
|
||||||
|
|
||||||
|
set search(term: string | undefined) {
|
||||||
|
const route: RawLocation = {
|
||||||
name: RouteName.SEARCH,
|
name: RouteName.SEARCH,
|
||||||
query: { ...this.$route.query, term: this.search },
|
};
|
||||||
|
if (term !== "") {
|
||||||
|
route.query = { ...this.$route.query, term };
|
||||||
|
}
|
||||||
|
this.$router.replace(route);
|
||||||
|
}
|
||||||
|
|
||||||
|
get activeTab(): SearchTabs {
|
||||||
|
return (
|
||||||
|
parseInt(this.$route.query.searchType as string, 10) || SearchTabs.EVENTS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
set activeTab(value: SearchTabs) {
|
||||||
|
this.$router.replace({
|
||||||
|
name: RouteName.SEARCH,
|
||||||
|
query: { ...this.$route.query, searchType: value.toString() },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Watch("activeTab")
|
get radius(): number | null {
|
||||||
updateActiveTab(): void {
|
if (this.$route.query.radius === "any") {
|
||||||
const searchType = this.activeTab === tabsName.events ? "events" : "groups";
|
return null;
|
||||||
this.$router.push({
|
}
|
||||||
|
return parseInt(this.$route.query.radius as string, 10) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
set radius(value: number | null) {
|
||||||
|
const radius = value === null ? "any" : value.toString();
|
||||||
|
this.$router.replace({
|
||||||
name: RouteName.SEARCH,
|
name: RouteName.SEARCH,
|
||||||
query: { ...this.$route.query, searchType },
|
query: { ...this.$route.query, radius },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get when(): string {
|
||||||
|
return (this.$route.query.when as string) || "any";
|
||||||
|
}
|
||||||
|
|
||||||
|
set when(value: string) {
|
||||||
|
this.$router.replace({
|
||||||
|
name: RouteName.SEARCH,
|
||||||
|
query: { ...this.$route.query, when: value },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -378,11 +417,17 @@ export default class Search extends Vue {
|
||||||
}
|
}
|
||||||
|
|
||||||
get start(): Date | undefined {
|
get start(): Date | undefined {
|
||||||
return this.when.start;
|
if (this.options[this.when]) {
|
||||||
|
return this.options[this.when].start;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
get end(): Date | undefined | null {
|
get end(): Date | undefined | null {
|
||||||
return this.when.end;
|
if (this.options[this.when]) {
|
||||||
|
return this.options[this.when].end;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -190,7 +190,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Admin do
|
||||||
when is_admin(role) do
|
when is_admin(role) do
|
||||||
last_public_event_published =
|
last_public_event_published =
|
||||||
case Events.list_events(1, 1, :inserted_at, :desc) do
|
case Events.list_events(1, 1, :inserted_at, :desc) do
|
||||||
[event | _] -> event
|
%Page{elements: [event | _]} -> event
|
||||||
_ -> nil
|
_ -> nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -161,7 +161,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Event do
|
||||||
events =
|
events =
|
||||||
if @number_of_related_events - length(events) > 0 do
|
if @number_of_related_events - length(events) > 0 do
|
||||||
events
|
events
|
||||||
|> Enum.concat(Events.list_events(1, @number_of_related_events, :begins_on, :asc, true))
|
|> Enum.concat(
|
||||||
|
Events.list_events(1, @number_of_related_events, :begins_on, :asc, true).elements
|
||||||
|
)
|
||||||
|> uniq_events()
|
|> uniq_events()
|
||||||
else
|
else
|
||||||
events
|
events
|
||||||
|
|
|
@ -299,7 +299,7 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
|
||||||
|
|
||||||
object :event_queries do
|
object :event_queries do
|
||||||
@desc "Get all events"
|
@desc "Get all events"
|
||||||
field :events, list_of(:event) do
|
field :events, :paginated_event_list do
|
||||||
arg(:page, :integer, default_value: 1, description: "The page in the paginated event list")
|
arg(:page, :integer, default_value: 1, description: "The page in the paginated event list")
|
||||||
arg(:limit, :integer, default_value: 10, description: "The limit of events per page")
|
arg(:limit, :integer, default_value: 10, description: "The limit of events per page")
|
||||||
resolve(&Event.list_events/3)
|
resolve(&Event.list_events/3)
|
||||||
|
|
|
@ -357,7 +357,7 @@ defmodule Mobilizon.Events do
|
||||||
direction \\ :asc,
|
direction \\ :asc,
|
||||||
is_future \\ true
|
is_future \\ true
|
||||||
) do
|
) do
|
||||||
query = from(e in Event, distinct: true, preload: [:organizer_actor, :participants])
|
query = from(e in Event, preload: [:organizer_actor, :participants])
|
||||||
|
|
||||||
query
|
query
|
||||||
|> sort(sort, direction)
|
|> sort(sort, direction)
|
||||||
|
@ -365,8 +365,7 @@ defmodule Mobilizon.Events do
|
||||||
|> filter_public_visibility()
|
|> filter_public_visibility()
|
||||||
|> filter_draft()
|
|> filter_draft()
|
||||||
|> filter_local_or_from_followed_instances_events()
|
|> filter_local_or_from_followed_instances_events()
|
||||||
|> Page.paginate(page, limit)
|
|> Page.build_page(page, limit)
|
||||||
|> Repo.all()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec stream_events_for_sitemap :: Enum.t()
|
@spec stream_events_for_sitemap :: Enum.t()
|
||||||
|
|
|
@ -19,12 +19,15 @@ defmodule Mobilizon.Storage.Page do
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns a Page struct for a query.
|
Returns a Page struct for a query.
|
||||||
|
|
||||||
|
`field` is use to define the field that will be used for the count aggregate, which should be the same as the field used for order_by
|
||||||
|
See https://stackoverflow.com/q/12693089/10204399
|
||||||
"""
|
"""
|
||||||
@spec build_page(Ecto.Query.t(), integer | nil, integer | nil) :: t
|
@spec build_page(Ecto.Query.t(), integer | nil, integer | nil, atom()) :: t
|
||||||
def build_page(query, page, limit) do
|
def build_page(query, page, limit, field \\ :id) do
|
||||||
[total, elements] =
|
[total, elements] =
|
||||||
[
|
[
|
||||||
fn -> Repo.aggregate(query, :count, :id) end,
|
fn -> Repo.aggregate(query, :count, field) end,
|
||||||
fn -> Repo.all(paginate(query, page, limit)) end
|
fn -> Repo.all(paginate(query, page, limit)) end
|
||||||
]
|
]
|
||||||
|> Enum.map(&Task.async/1)
|
|> Enum.map(&Task.async/1)
|
||||||
|
|
|
@ -1144,120 +1144,89 @@ defmodule Mobilizon.Web.Resolvers.EventTest do
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "list_events/3 returns events", context do
|
@fetch_events_query """
|
||||||
event = insert(:event)
|
query Events($page: Int, $limit: Int) {
|
||||||
|
events(page: $page, limit: $limit) {
|
||||||
query = """
|
total
|
||||||
{
|
elements {
|
||||||
events {
|
uuid
|
||||||
uuid,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
test "list_events/3 returns events", %{conn: conn} do
|
||||||
|
event = insert(:event)
|
||||||
|
|
||||||
res =
|
res =
|
||||||
context.conn
|
conn
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "event"))
|
|> AbsintheHelpers.graphql_query(query: @fetch_events_query)
|
||||||
|
|
||||||
assert json_response(res, 200)["data"]["events"] |> Enum.map(& &1["uuid"]) == [event.uuid]
|
assert res["data"]["events"]["elements"] |> Enum.map(& &1["uuid"]) == [
|
||||||
|
event.uuid
|
||||||
|
]
|
||||||
|
|
||||||
Enum.each(0..15, fn _ ->
|
Enum.each(0..15, fn _ ->
|
||||||
insert(:event)
|
insert(:event)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
query = """
|
res =
|
||||||
{
|
conn
|
||||||
events {
|
|> AbsintheHelpers.graphql_query(query: @fetch_events_query)
|
||||||
uuid,
|
|
||||||
}
|
assert res["data"]["events"]["total"] == 17
|
||||||
}
|
assert res["data"]["events"]["elements"] |> length == 10
|
||||||
"""
|
|
||||||
|
|
||||||
res =
|
res =
|
||||||
context.conn
|
conn
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "event"))
|
|> AbsintheHelpers.graphql_query(query: @fetch_events_query, variables: %{page: 2})
|
||||||
|
|
||||||
assert json_response(res, 200)["data"]["events"] |> length == 10
|
assert res["data"]["events"]["total"] == 17
|
||||||
|
assert res["data"]["events"]["elements"] |> length == 7
|
||||||
query = """
|
|
||||||
{
|
|
||||||
events(page: 2) {
|
|
||||||
uuid,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
res =
|
res =
|
||||||
context.conn
|
conn
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "event"))
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @fetch_events_query,
|
||||||
|
variables: %{page: 2, limit: 15}
|
||||||
|
)
|
||||||
|
|
||||||
assert json_response(res, 200)["data"]["events"] |> length == 7
|
assert res["data"]["events"]["total"] == 17
|
||||||
|
assert res["data"]["events"]["elements"] |> length == 2
|
||||||
query = """
|
|
||||||
{
|
|
||||||
events(page: 2, limit: 15) {
|
|
||||||
uuid,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
res =
|
res =
|
||||||
context.conn
|
conn
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "event"))
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @fetch_events_query,
|
||||||
|
variables: %{page: 3, limit: 15}
|
||||||
|
)
|
||||||
|
|
||||||
assert json_response(res, 200)["data"]["events"] |> length == 2
|
assert res["data"]["events"]["total"] == 17
|
||||||
|
assert res["data"]["events"]["elements"] |> length == 0
|
||||||
query = """
|
|
||||||
{
|
|
||||||
events(page: 3, limit: 15) {
|
|
||||||
uuid,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
res =
|
|
||||||
context.conn
|
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "event"))
|
|
||||||
|
|
||||||
assert json_response(res, 200)["data"]["events"] |> length == 0
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "list_events/3 doesn't list private events", context do
|
test "list_events/3 doesn't list private events", %{conn: conn} do
|
||||||
insert(:event, visibility: :private)
|
insert(:event, visibility: :private)
|
||||||
insert(:event, visibility: :unlisted)
|
insert(:event, visibility: :unlisted)
|
||||||
insert(:event, visibility: :restricted)
|
insert(:event, visibility: :restricted)
|
||||||
|
|
||||||
query = """
|
|
||||||
{
|
|
||||||
events {
|
|
||||||
uuid,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
res =
|
res =
|
||||||
context.conn
|
conn
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "event"))
|
|> AbsintheHelpers.graphql_query(query: @fetch_events_query)
|
||||||
|
|
||||||
assert json_response(res, 200)["data"]["events"] |> Enum.map(& &1["uuid"]) == []
|
assert res["data"]["events"]["total"] == 0
|
||||||
|
assert res["data"]["events"]["elements"] |> Enum.map(& &1["uuid"]) == []
|
||||||
end
|
end
|
||||||
|
|
||||||
test "list_events/3 doesn't list draft events", context do
|
test "list_events/3 doesn't list draft events", %{conn: conn} do
|
||||||
insert(:event, visibility: :public, draft: true)
|
insert(:event, visibility: :public, draft: true)
|
||||||
|
|
||||||
query = """
|
|
||||||
{
|
|
||||||
events {
|
|
||||||
uuid,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
res =
|
res =
|
||||||
context.conn
|
conn
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "event"))
|
|> AbsintheHelpers.graphql_query(query: @fetch_events_query)
|
||||||
|
|
||||||
assert json_response(res, 200)["data"]["events"] |> Enum.map(& &1["uuid"]) == []
|
assert res["data"]["events"]["total"] == 0
|
||||||
|
assert res["data"]["events"]["elements"] |> Enum.map(& &1["uuid"]) == []
|
||||||
end
|
end
|
||||||
|
|
||||||
test "find_event/3 returns an unlisted event", context do
|
test "find_event/3 returns an unlisted event", context do
|
||||||
|
|
|
@ -29,12 +29,12 @@ defmodule Mobilizon.EventsTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "list_events/0 returns all events", %{event: event} do
|
test "list_events/0 returns all events", %{event: event} do
|
||||||
assert event.title == hd(Events.list_events()).title
|
assert event.title == hd(Events.list_events().elements).title
|
||||||
end
|
end
|
||||||
|
|
||||||
test "list_events/5 returns events from other instances if we follow them",
|
test "list_events/5 returns events from other instances if we follow them",
|
||||||
%{event: _event} do
|
%{event: _event} do
|
||||||
events = Events.list_events()
|
events = Events.list_events().elements
|
||||||
assert length(events) == 1
|
assert length(events) == 1
|
||||||
|
|
||||||
%Actor{id: remote_instance_actor_id} = remote_instance_actor = insert(:instance_actor)
|
%Actor{id: remote_instance_actor_id} = remote_instance_actor = insert(:instance_actor)
|
||||||
|
@ -46,7 +46,7 @@ defmodule Mobilizon.EventsTest do
|
||||||
|
|
||||||
insert(:follower, target_actor: remote_instance_actor, actor: own_instance_actor)
|
insert(:follower, target_actor: remote_instance_actor, actor: own_instance_actor)
|
||||||
|
|
||||||
events = Events.list_events()
|
events = Events.list_events().elements
|
||||||
assert length(events) == 2
|
assert length(events) == 2
|
||||||
assert events |> Enum.any?(fn event -> event.title == "My Remote event" end)
|
assert events |> Enum.any?(fn event -> event.title == "My Remote event" end)
|
||||||
end
|
end
|
||||||
|
@ -58,7 +58,7 @@ defmodule Mobilizon.EventsTest do
|
||||||
%Event{url: remote_event_url} = insert(:event, local: false, title: "My Remote event")
|
%Event{url: remote_event_url} = insert(:event, local: false, title: "My Remote event")
|
||||||
Mobilizon.Share.create(remote_event_url, remote_instance_actor_id, remote_actor_id)
|
Mobilizon.Share.create(remote_event_url, remote_instance_actor_id, remote_actor_id)
|
||||||
|
|
||||||
events = Events.list_events()
|
events = Events.list_events().elements
|
||||||
assert length(events) == 1
|
assert length(events) == 1
|
||||||
assert events |> Enum.all?(fn event -> event.title != "My Remote event" end)
|
assert events |> Enum.all?(fn event -> event.title != "My Remote event" end)
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue