Event edit and participant fixes

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2021-05-17 09:38:04 +02:00
parent e96dcc42b9
commit b5a5de5c0c
No known key found for this signature in database
GPG key ID: A061B9DDE0CA0773
6 changed files with 110 additions and 90 deletions

View file

@ -1,6 +1,7 @@
import { AUTH_ACCESS_TOKEN, AUTH_REFRESH_TOKEN } from "@/constants"; import { AUTH_ACCESS_TOKEN, AUTH_REFRESH_TOKEN } from "@/constants";
import { REFRESH_TOKEN } from "@/graphql/auth"; import { REFRESH_TOKEN } from "@/graphql/auth";
import { IFollower } from "@/types/actor/follower.model"; import { IFollower } from "@/types/actor/follower.model";
import { IParticipant } from "@/types/participant.model";
import { Paginate } from "@/types/paginate"; import { Paginate } from "@/types/paginate";
import { saveTokenData } from "@/utils/auth"; import { saveTokenData } from "@/utils/auth";
import { import {
@ -11,6 +12,9 @@ import {
TypePolicies, TypePolicies,
} from "@apollo/client/core"; } from "@apollo/client/core";
import introspectionQueryResultData from "../../fragmentTypes.json"; import introspectionQueryResultData from "../../fragmentTypes.json";
import { IMember } from "@/types/actor/member.model";
import { IComment } from "@/types/comment.model";
import { IEvent } from "@/types/event.model";
type possibleTypes = { name: string }; type possibleTypes = { name: string };
type schemaType = { type schemaType = {
@ -31,19 +35,34 @@ export const possibleTypes = types.reduce((acc, type) => {
export const typePolicies: TypePolicies = { export const typePolicies: TypePolicies = {
Discussion: { Discussion: {
fields: { fields: {
comments: pageLimitPagination(), comments: paginatedLimitPagination(),
}, },
}, },
Group: { Group: {
fields: { fields: {
organizedEvents: pageLimitPagination(["afterDatetime", "beforeDatetime"]), organizedEvents: paginatedLimitPagination([
"afterDatetime",
"beforeDatetime",
]),
}, },
}, },
Person: { Person: {
fields: { fields: {
organizedEvents: pageLimitPagination(), organizedEvents: pageLimitPagination(),
participations: pageLimitPagination(["eventId"]), participations: paginatedLimitPagination<IParticipant>(["eventId"]),
memberships: pageLimitPagination(["group"]), memberships: paginatedLimitPagination<IMember>(["group"]),
},
},
Event: {
fields: {
participants: paginatedLimitPagination<IParticipant>(["roles"]),
commnents: pageLimitPagination<IComment>(),
relatedEvents: pageLimitPagination<IEvent>(),
},
},
Comment: {
fields: {
replies: pageLimitPagination<IComment>(),
}, },
}, },
RootQueryType: { RootQueryType: {
@ -53,15 +72,15 @@ export const typePolicies: TypePolicies = {
"orderBy", "orderBy",
"direction", "direction",
]), ]),
events: pageLimitPagination(), events: paginatedLimitPagination(),
groups: pageLimitPagination([ groups: paginatedLimitPagination([
"preferredUsername", "preferredUsername",
"name", "name",
"domain", "domain",
"local", "local",
"suspended", "suspended",
]), ]),
persons: pageLimitPagination([ persons: paginatedLimitPagination([
"preferredUsername", "preferredUsername",
"name", "name",
"domain", "domain",
@ -103,12 +122,12 @@ type KeyArgs = FieldPolicy<any>["keyArgs"];
export function pageLimitPagination<T = Reference>( export function pageLimitPagination<T = Reference>(
keyArgs: KeyArgs = false keyArgs: KeyArgs = false
): FieldPolicy<T[]> { ): FieldPolicy<T[]> {
console.log("pageLimitPagination");
return { return {
keyArgs, keyArgs,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
merge(existing, incoming, { args }) { merge(existing, incoming, { args }) {
console.log("pageLimitPagination");
console.log("existing", existing); console.log("existing", existing);
console.log("incoming", incoming); console.log("incoming", incoming);
// console.log("args", args); // console.log("args", args);
@ -123,12 +142,14 @@ export function pageLimitPagination<T = Reference>(
export function paginatedLimitPagination<T = Paginate<any>>( export function paginatedLimitPagination<T = Paginate<any>>(
keyArgs: KeyArgs = false keyArgs: KeyArgs = false
): FieldPolicy<Paginate<T>> { ): FieldPolicy<Paginate<T>> {
console.log("paginatedLimitPagination");
return { return {
keyArgs, keyArgs,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
merge(existing, incoming, { args }) { merge(existing, incoming, { args }) {
console.log("paginatedLimitPagination");
console.log("existing", existing);
console.log("incoming", incoming);
if (!incoming) return existing; if (!incoming) return existing;
if (!existing) return incoming; // existing will be empty the first time if (!existing) return incoming; // existing will be empty the first time

View file

@ -21,6 +21,7 @@ export const COMMENT_FIELDS_FRAGMENT = gql`
summary summary
} }
totalReplies totalReplies
insertedAt
updatedAt updatedAt
deletedAt deletedAt
} }

View file

@ -42,6 +42,7 @@ interface IEventEditJSON {
draft: boolean; draft: boolean;
picture?: IMedia | { mediaId: string } | null; picture?: IMedia | { mediaId: string } | null;
attributedToId: string | null; attributedToId: string | null;
organizerActorId?: string;
onlineAddress?: string; onlineAddress?: string;
phoneAddress?: string; phoneAddress?: string;
physicalAddress?: IAddress; physicalAddress?: IAddress;
@ -209,8 +210,8 @@ export class EventModel implements IEvent {
tags: this.tags.map((t) => t.title), tags: this.tags.map((t) => t.title),
onlineAddress: this.onlineAddress, onlineAddress: this.onlineAddress,
phoneAddress: this.phoneAddress, phoneAddress: this.phoneAddress,
physicalAddress: this.physicalAddress, physicalAddress: this.removeTypeName(this.physicalAddress),
options: this.options, options: this.removeTypeName(this.options),
attributedToId: attributedToId:
this.attributedTo && this.attributedTo.id ? this.attributedTo.id : null, this.attributedTo && this.attributedTo.id ? this.attributedTo.id : null,
contacts: this.contacts.map(({ id }) => ({ contacts: this.contacts.map(({ id }) => ({
@ -218,4 +219,13 @@ export class EventModel implements IEvent {
})), })),
}; };
} }
private removeTypeName(entity: any): any {
if (entity?.__typename) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { __typename, ...purgedEntity } = entity;
return purgedEntity;
}
return entity;
}
} }

View file

@ -486,6 +486,7 @@ import "intersection-observer";
import { CONFIG } from "../../graphql/config"; import { CONFIG } from "../../graphql/config";
import { IConfig } from "../../types/config.model"; import { IConfig } from "../../types/config.model";
import { ApolloCache, FetchResult, InMemoryCache } from "@apollo/client/core"; import { ApolloCache, FetchResult, InMemoryCache } from "@apollo/client/core";
import { cloneDeep } from "@apollo/client/utilities";
const DEFAULT_LIMIT_NUMBER_OF_PLACES = 10; const DEFAULT_LIMIT_NUMBER_OF_PLACES = 10;
@ -512,7 +513,7 @@ const DEFAULT_LIMIT_NUMBER_OF_PLACES = 10;
}; };
}, },
update(data) { update(data) {
return new EventModel(data.event); return new EventModel(cloneDeep(data.event));
}, },
skip() { skip() {
return !this.eventId; return !this.eventId;
@ -547,8 +548,6 @@ export default class EditEvent extends Vue {
config!: IConfig; config!: IConfig;
unmodifiedEvent!: IEvent;
pictureFile: File | null = null; pictureFile: File | null = null;
EventStatus = EventStatus; EventStatus = EventStatus;
@ -646,7 +645,11 @@ export default class EditEvent extends Vue {
if (!(this.isUpdate || this.isDuplicate)) { if (!(this.isUpdate || this.isDuplicate)) {
this.initializeEvent(); this.initializeEvent();
} else { } else {
this.event.description = this.event.description || ""; this.event = {
...this.event,
options: this.event.options,
description: this.event.description || "",
};
} }
} }
@ -669,27 +672,6 @@ export default class EditEvent extends Vue {
} }
} }
@Watch("event")
setInitialData(): void {
if (
this.isUpdate &&
this.unmodifiedEvent === undefined &&
this.event &&
this.event.uuid
) {
this.unmodifiedEvent = JSON.parse(
JSON.stringify(this.event.toEditJSON())
);
}
}
// @Watch('event.attributedTo', { deep: true })
// updateHideOrganizerWhenGroupEventOption(attributedTo) {
// if (!attributedTo.preferredUsername) {
// this.event.options.hideOrganizerWhenGroupEvent = false;
// }
// }
private validateForm() { private validateForm() {
const form = this.$refs.form as HTMLFormElement; const form = this.$refs.form as HTMLFormElement;
if (form.checkValidity()) { if (form.checkValidity()) {
@ -777,8 +759,8 @@ export default class EditEvent extends Vue {
} }
get updateEventMessage(): string { get updateEventMessage(): string {
if (this.unmodifiedEvent.draft && !this.event.draft) // if (this.unmodifiedEvent.draft && !this.event.draft)
return this.$i18n.t("The event has been updated and published") as string; // return this.$i18n.t("The event has been updated and published") as string;
return ( return (
this.event.draft this.event.draft
? this.$i18n.t("The draft event has been updated") ? this.$i18n.t("The draft event has been updated")
@ -884,24 +866,12 @@ export default class EditEvent extends Vue {
? this.event.organizerActor ? this.event.organizerActor
: this.organizerActor; : this.organizerActor;
if (organizerActor) { if (organizerActor) {
res = Object.assign(res, { res = { ...res, organizerActorId: organizerActor?.id };
organizerActorId: organizerActor.id,
});
} }
const attributedToId = this.event.attributedTo?.id const attributedToId = this.event.attributedTo?.id
? this.event.attributedTo.id ? this.event.attributedTo.id
: null; : null;
res = Object.assign(res, { attributedToId }); res = { ...res, attributedToId };
// eslint-disable-next-line
// @ts-ignore
delete this.event.options.__typename;
if (this.event.physicalAddress) {
// eslint-disable-next-line
// @ts-ignore
delete this.event.physicalAddress.__typename;
}
if (this.endsOnNull) { if (this.endsOnNull) {
res.endsOn = null; res.endsOn = null;
@ -931,25 +901,6 @@ export default class EditEvent extends Vue {
return res; return res;
} }
private async getEvent() {
const result = await this.$apollo.query({
query: FETCH_EVENT,
variables: {
uuid: this.eventId,
},
});
if (result.data.event.endsOn === null) {
this.endsOnNull = true;
}
// as stated here : https://github.com/elixir-ecto/ecto/issues/1684
// "Ecto currently silently transforms empty strings into nil"
if (result.data.event.description === null) {
result.data.event.description = "";
}
return new EventModel(result.data.event);
}
@Watch("limitedPlaces") @Watch("limitedPlaces")
updatedEventCapacityOptions(limitedPlaces: boolean): void { updatedEventCapacityOptions(limitedPlaces: boolean): void {
if (!limitedPlaces) { if (!limitedPlaces) {
@ -1023,10 +974,11 @@ export default class EditEvent extends Vue {
} }
get isEventModified(): boolean { get isEventModified(): boolean {
return ( // return (
JSON.stringify(this.event.toEditJSON()) !== // JSON.stringify(this.event.toEditJSON()) !==
JSON.stringify(this.unmodifiedEvent) // JSON.stringify(this.unmodifiedEvent)
); // );
return false;
} }
get beginsOn(): Date { get beginsOn(): Date {

View file

@ -30,8 +30,8 @@
</nav> </nav>
<h2 class="title">{{ $t("Participants") }}</h2> <h2 class="title">{{ $t("Participants") }}</h2>
<b-field :label="$t('Status')" horizontal> <b-field :label="$t('Status')" horizontal>
<b-select v-model="roles"> <b-select v-model="role">
<option value=""> <option :value="null">
{{ $t("Everything") }} {{ $t("Everything") }}
</option> </option>
<option :value="ParticipantRole.CREATOR"> <option :value="ParticipantRole.CREATOR">
@ -230,6 +230,8 @@ import { IConfig } from "../../types/config.model";
import { nl2br } from "../../utils/html"; import { nl2br } from "../../utils/html";
import { asyncForEach } from "../../utils/asyncForEach"; import { asyncForEach } from "../../utils/asyncForEach";
import RouteName from "../../router/name"; import RouteName from "../../router/name";
import VueRouter from "vue-router";
const { isNavigationFailure, NavigationFailureType } = VueRouter;
const PARTICIPANTS_PER_PAGE = 10; const PARTICIPANTS_PER_PAGE = 10;
const MESSAGE_ELLIPSIS_LENGTH = 130; const MESSAGE_ELLIPSIS_LENGTH = 130;
@ -242,13 +244,12 @@ const MESSAGE_ELLIPSIS_LENGTH = 130;
config: CONFIG, config: CONFIG,
event: { event: {
query: PARTICIPANTS, query: PARTICIPANTS,
fetchPolicy: "cache-and-network",
variables() { variables() {
return { return {
uuid: this.eventId, uuid: this.eventId,
page: 1, page: this.page,
limit: PARTICIPANTS_PER_PAGE, limit: PARTICIPANTS_PER_PAGE,
roles: this.roles, roles: this.role,
}; };
}, },
skip() { skip() {
@ -264,7 +265,32 @@ const MESSAGE_ELLIPSIS_LENGTH = 130;
export default class Participants extends Vue { export default class Participants extends Vue {
@Prop({ required: true }) eventId!: string; @Prop({ required: true }) eventId!: string;
page = 1; get page(): number {
return parseInt((this.$route.query.page as string) || "1", 10);
}
set page(page: number) {
this.pushRouter(RouteName.RELAY_FOLLOWINGS, {
page: page.toString(),
});
}
get role(): ParticipantRole | null {
if (
Object.values(ParticipantRole).includes(
this.$route.query.role as ParticipantRole
)
) {
return this.$route.query.role as ParticipantRole;
}
return null;
}
set role(role: ParticipantRole | null) {
this.pushRouter(RouteName.PARTICIPATIONS, {
role: role || "",
});
}
limit = PARTICIPANTS_PER_PAGE; limit = PARTICIPANTS_PER_PAGE;
@ -280,21 +306,12 @@ export default class Participants extends Vue {
checkedRows: IParticipant[] = []; checkedRows: IParticipant[] = [];
roles: ParticipantRole = ParticipantRole.PARTICIPANT;
RouteName = RouteName; RouteName = RouteName;
usernameWithDomain = usernameWithDomain; usernameWithDomain = usernameWithDomain;
@Ref("queueTable") readonly queueTable!: any; @Ref("queueTable") readonly queueTable!: any;
mounted(): void {
const roleQuery = this.$route.query.role as string;
if (Object.values(ParticipantRole).includes(roleQuery as ParticipantRole)) {
this.roles = roleQuery as ParticipantRole;
}
}
get participantStats(): IEventParticipantStats | null { get participantStats(): IEventParticipantStats | null {
if (!this.event) return null; if (!this.event) return null;
return this.event.participantStats; return this.event.participantStats;
@ -386,6 +403,22 @@ export default class Participants extends Vue {
return; return;
this.queueTable.toggleDetails(row); this.queueTable.toggleDetails(row);
} }
async pushRouter(
routeName: string,
args: Record<string, string>
): Promise<void> {
try {
await this.$router.push({
name: routeName,
query: { ...this.$route.query, ...args },
});
} catch (e) {
if (isNavigationFailure(e, NavigationFailureType.redirected)) {
throw Error(e.toString());
}
}
}
} }
</script> </script>

View file

@ -127,6 +127,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Event do
{:actor_approve_permission, Events.moderator_for_event?(event_id, actor_id)} do {:actor_approve_permission, Events.moderator_for_event?(event_id, actor_id)} do
roles = roles =
case roles do case roles do
nil ->
[]
"" -> "" ->
[] []