forked from potsda.mn/mobilizon
Event edit and participant fixes
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
e96dcc42b9
commit
b5a5de5c0c
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ export const COMMENT_FIELDS_FRAGMENT = gql`
|
||||||
summary
|
summary
|
||||||
}
|
}
|
||||||
totalReplies
|
totalReplies
|
||||||
|
insertedAt
|
||||||
updatedAt
|
updatedAt
|
||||||
deletedAt
|
deletedAt
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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 ->
|
||||||
|
[]
|
||||||
|
|
||||||
"" ->
|
"" ->
|
||||||
[]
|
[]
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue