build: switch from yarn to npm to manage js dependencies and move js contents to root

yarn v1 is being deprecated and starts to have some issues

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel
2023-11-14 17:24:42 +01:00
parent 32055122c3
commit 2e72f6faf4
595 changed files with 12078 additions and 7843 deletions

View File

@@ -0,0 +1,37 @@
import { IActor, IGroup } from "./actor";
import { IMember } from "./actor/member.model";
import {
ActivityDiscussionSubject,
ActivityEventCommentSubject,
ActivityEventParticipantSubject,
ActivityEventSubject,
ActivityGroupSubject,
ActivityMemberSubject,
ActivityPostSubject,
ActivityResourceSubject,
ActivityType,
} from "./enums";
import { IEvent } from "./event.model";
import { IPost } from "./post.model";
import { IResource } from "./resource";
export type ActivitySubject =
| ActivityEventSubject
| ActivityPostSubject
| ActivityMemberSubject
| ActivityResourceSubject
| ActivityDiscussionSubject
| ActivityGroupSubject
| ActivityEventCommentSubject
| ActivityEventParticipantSubject;
export interface IActivity {
id: string;
type: ActivityType;
subject: ActivitySubject;
subjectParams: { key: string; value: string }[];
author: IActor;
group: IGroup;
object: IEvent | IPost | IGroup | IMember | IResource;
insertedAt: string;
}

View File

@@ -0,0 +1,90 @@
import type { IMedia } from "@/types/media.model";
import { ActorType } from "../enums";
export interface IActor {
id?: string;
url: string;
name: string;
domain: string | null;
mediaSize?: number;
summary: string;
preferredUsername: string;
suspended: boolean;
avatar?: IMedia | null;
banner?: IMedia | null;
type: ActorType;
}
export type IMinimalActor = Pick<IActor, "preferredUsername" | "domain">;
export type IMinimalActorWithName = Pick<
IActor,
"preferredUsername" | "domain" | "name"
>;
export class Actor implements IActor {
id?: string;
avatar: IMedia | null = null;
banner: IMedia | null = null;
domain: string | null = null;
mediaSize = 0;
name = "";
preferredUsername = "";
summary = "";
suspended = false;
url = "";
type: ActorType = ActorType.PERSON;
constructor(hash: IActor | Record<any, unknown> = {}) {
Object.assign(this, hash);
}
get displayNameAndUsername(): string {
return `${this.name} (${this.usernameWithDomain})`;
}
usernameWithDomain(): string {
const domain = this.domain ? `@${this.domain}` : "";
return `@${this.preferredUsername}${domain}`;
}
public displayName(): string {
return displayName(this);
}
}
export function usernameWithDomain(
actor: IMinimalActor | undefined,
force = false
): string {
if (!actor) return "";
if (actor?.domain) {
return `${actor.preferredUsername}@${actor.domain}`;
}
if (force) {
return `${actor.preferredUsername}@${window.location.hostname}`;
}
return actor.preferredUsername;
}
export function displayName(actor: IMinimalActorWithName | undefined): string {
return actor && actor.name != null && actor.name !== ""
? actor.name
: usernameWithDomain(actor);
}
export function displayNameAndUsername(actor: IMinimalActorWithName): string {
if (actor.name) {
return `${actor.name} (@${usernameWithDomain(actor)})`;
}
return usernameWithDomain(actor);
}

View File

@@ -0,0 +1,9 @@
import type { IActor } from "@/types/actor/actor.model";
export interface IFollower {
id?: string;
actor: IActor;
targetActor: IActor;
approved: boolean;
notify?: boolean;
}

View File

@@ -0,0 +1,64 @@
import type { IActor } from "./actor.model";
import { Actor } from "./actor.model";
import type { Paginate } from "../paginate";
import type { IResource } from "../resource";
import type { IEvent } from "../event.model";
import type { IDiscussion } from "../discussions";
import type { IPost } from "../post.model";
import { Address, type IAddress } from "../address.model";
import { ActorType, GroupVisibility, Openness } from "../enums";
import type { IMember } from "./member.model";
import type { ITodoList } from "../todolist";
import { IActivity } from "../activity.model";
import { IFollower } from "./follower.model";
export interface IGroup extends IActor {
members: Paginate<IMember>;
resources: Paginate<IResource>;
todoLists: Paginate<ITodoList>;
discussions: Paginate<IDiscussion>;
organizedEvents: Paginate<IEvent>;
posts: Paginate<IPost>;
physicalAddress: IAddress;
openness: Openness;
visibility: GroupVisibility;
manuallyApprovesFollowers: boolean;
activity: Paginate<IActivity>;
followers: Paginate<IFollower>;
membersCount?: number;
followersCount?: number;
}
export class Group extends Actor implements IGroup {
members: Paginate<IMember> = { elements: [], total: 0 };
followers: Paginate<IFollower> = { elements: [], total: 0 };
resources: Paginate<IResource> = { elements: [], total: 0 };
todoLists: Paginate<ITodoList> = { elements: [], total: 0 };
discussions: Paginate<IDiscussion> = { elements: [], total: 0 };
organizedEvents: Paginate<IEvent> = { elements: [], total: 0 };
posts: Paginate<IPost> = { elements: [], total: 0 };
constructor(hash: IGroup | Record<string, unknown> = {}) {
super(hash);
this.type = ActorType.GROUP;
this.patch(hash);
}
visibility: GroupVisibility = GroupVisibility.PUBLIC;
activity: Paginate<IActivity> = { elements: [], total: 0 };
openness: Openness = Openness.MODERATED;
physicalAddress: IAddress = new Address();
manuallyApprovesFollowers = false;
patch(hash: IGroup | Record<string, unknown>): void {
Object.assign(this, hash);
}
}

4
src/types/actor/index.ts Normal file
View File

@@ -0,0 +1,4 @@
export * from "./actor.model";
export * from "./group.model";
export * from "./person.model";
export * from "./follower.model";

View File

@@ -0,0 +1,12 @@
import type { IActor, IGroup, IPerson } from ".";
import { MemberRole } from "../enums";
export interface IMember {
id?: string;
role: MemberRole;
parent: IGroup;
actor: IActor;
invitedBy?: IPerson;
insertedAt: string;
updatedAt: string;
}

View File

@@ -0,0 +1,48 @@
import type { ICurrentUser } from "../current-user.model";
import type { IEvent } from "../event.model";
import { Actor } from "./actor.model";
import type { IActor } from "./actor.model";
import type { Paginate } from "../paginate";
import type { IParticipant } from "../participant.model";
import type { IMember } from "./member.model";
import type { IFeedToken } from "../feedtoken.model";
import { IFollower } from "./follower.model";
import { IConversation } from "../conversation";
export interface IPerson extends IActor {
feedTokens: IFeedToken[];
goingToEvents?: IEvent[];
participations?: Paginate<IParticipant>;
memberships?: Paginate<IMember>;
follows?: Paginate<IFollower>;
user?: ICurrentUser;
organizedEvents?: Paginate<IEvent>;
conversations?: Paginate<IConversation>;
unreadConversationsCount?: number;
}
export class Person extends Actor implements IPerson {
feedTokens: IFeedToken[] = [];
goingToEvents: IEvent[] = [];
participations!: Paginate<IParticipant>;
memberships!: Paginate<IMember>;
organizedEvents!: Paginate<IEvent>;
conversations!: Paginate<IConversation>;
user!: ICurrentUser;
constructor(hash: IPerson | Record<string, unknown> = {}) {
super(hash);
this.patch(hash);
}
follows!: Paginate<IFollower>;
patch(hash: IPerson | Record<string, unknown>): void {
Object.assign(this, hash);
}
}

182
src/types/address.model.ts Normal file
View File

@@ -0,0 +1,182 @@
import { poiIcons } from "@/utils/poiIcons";
import type { IPOIIcon } from "@/utils/poiIcons";
import { PictureInformation } from "./picture";
export interface IAddress {
id?: string;
description: string;
street: string;
locality: string;
postalCode: string;
region: string;
country: string;
type: string;
geom?: string;
url?: string;
originId?: string;
timezone?: string;
pictureInfo?: PictureInformation;
poiInfos?: IPoiInfo;
}
export interface IPoiInfo {
name: string;
alternativeName: string;
poiIcon: IPOIIcon;
}
export class Address implements IAddress {
country = "";
description = "";
locality = "";
postalCode = "";
region = "";
street = "";
type = "";
id?: string = "";
originId?: string = "";
url?: string = "";
geom?: string = "";
timezone?: string = "";
constructor(hash?: IAddress) {
if (!hash) return;
this.id = hash.id;
this.description = hash.description?.trim();
this.street = hash.street?.trim();
this.locality = hash.locality?.trim();
this.postalCode = hash.postalCode?.trim();
this.region = hash.region?.trim();
this.country = hash.country?.trim();
this.type = hash.type;
this.geom = hash.geom;
this.url = hash.url;
this.originId = hash.originId;
this.timezone = hash.timezone;
}
get poiInfos(): IPoiInfo {
return addressToPoiInfos(this);
}
get fullName(): string {
return addressFullName(this);
}
get iconForPOI(): IPOIIcon {
return iconForAddress(this);
}
}
export function addressToPoiInfos(address: IAddress): IPoiInfo {
/* generate name corresponding to poi type */
let name = "";
let alternativeName = "";
let poiIcon: IPOIIcon = poiIcons.default;
let addressType = address.type;
// Google Maps doesn't have a type
if (address.type == null && address.description === address.street) {
addressType = "house";
}
switch (addressType) {
case "house":
name = address.description;
alternativeName = (
address.description !== address.street ? [address.street] : []
)
.concat([address.postalCode, address.locality, address.country])
.filter((zone) => zone)
.join(", ");
poiIcon = poiIcons.defaultAddress;
break;
case "street":
case "secondary":
name = address.description;
alternativeName = [address.postalCode, address.locality, address.country]
.filter((zone) => zone)
.join(", ");
poiIcon = poiIcons.defaultStreet;
break;
case "zone":
case "city":
case "administrative":
name = address.postalCode
? `${address.description} (${address.postalCode})`
: address.description;
alternativeName = [address.region, address.country]
.filter((zone) => zone)
.join(", ");
poiIcon = poiIcons.defaultAdministrative;
break;
default:
// POI
name = address.description;
alternativeName = "";
if (address.street && address.street.trim()) {
alternativeName = `${address.street}`;
if (address.postalCode) {
alternativeName += `, ${address.postalCode}`;
}
if (address.locality) {
alternativeName += `, ${address.locality}`;
}
} else if (address.locality && address.locality.trim()) {
alternativeName = `${address.locality}, ${address.region}, ${address.country}`;
} else if (address.region && address.region.trim()) {
alternativeName = `${address.region}, ${address.country}`;
} else if (address.country && address.country.trim()) {
alternativeName = address.country;
}
poiIcon = iconForAddress(address);
break;
}
return { name, alternativeName, poiIcon };
}
export function iconForAddress(address: IAddress): IPOIIcon {
if (address.type == null) {
return poiIcons.default;
}
const type = address.type.split(":").pop() || "";
if (poiIcons[type]) return poiIcons[type];
return poiIcons.default;
}
export function addressFullName(address: IAddress): string {
const { name, alternativeName } = addressToPoiInfos(address);
if (name && alternativeName) {
return `${name}, ${alternativeName}`;
}
if (name) {
return name;
}
return "";
}
export function resetAddress(address: IAddress): void {
address.id = undefined;
address.description = "";
address.street = "";
address.locality = "";
address.postalCode = "";
address.region = "";
address.country = "";
address.type = "";
address.geom = undefined;
address.url = undefined;
address.originId = undefined;
address.timezone = undefined;
address.pictureInfo = undefined;
}

37
src/types/admin.model.ts Normal file
View File

@@ -0,0 +1,37 @@
import type { IEvent } from "@/types/event.model";
import type { IGroup } from "./actor";
import { InstancePrivacyType, InstanceTermsType } from "./enums";
export interface IDashboard {
lastPublicEventPublished: IEvent;
lastGroupCreated: IGroup;
numberOfUsers: number;
numberOfEvents: number;
numberOfComments: number;
numberOfReports: number;
numberOfGroups: number;
numberOfFollowers: number;
numberOfFollowings: number;
numberOfConfirmedParticipationsToLocalEvents: number;
}
export interface ILanguage {
code: string;
name: string;
}
export interface IAdminSettings {
instanceName: string;
instanceDescription: string;
instanceSlogan: string;
instanceLongDescription: string;
contact: string;
instanceTerms: string;
instanceTermsType: InstanceTermsType;
instanceTermsUrl: string | null;
instancePrivacyPolicy: string;
instancePrivacyPolicyType: InstancePrivacyType;
instancePrivacyPolicyUrl: string | null;
instanceRules: string;
registrationsOpen: boolean;
instanceLanguages: string[];
}

View File

@@ -0,0 +1,7 @@
export interface ISentryConfiguration {
dsn: string;
organization?: string;
project?: string;
host?: string;
tracesSampleRate: number;
}

9
src/types/apollo.ts Normal file
View File

@@ -0,0 +1,9 @@
import { GraphQLError } from "graphql/error/GraphQLError";
export class AbsintheGraphQLError extends GraphQLError {
readonly field: string | undefined;
}
export type TypeNamed<T extends Record<string, any>> = T & {
__typename: string;
};

View File

@@ -0,0 +1,15 @@
export interface IApplication {
name: string;
clientId: string;
clientSecret?: string;
redirectUris?: string;
scope: string | null;
website: string | null;
}
export interface IApplicationToken {
id: string;
application: IApplication;
lastUsedAt: string;
insertedAt: string;
}

View File

@@ -0,0 +1,76 @@
import { IPerson, Person } from "@/types/actor";
import type { IEvent } from "@/types/event.model";
import { EventModel } from "@/types/event.model";
import { IConversation } from "./conversation";
export interface IComment {
id?: string;
uuid?: string;
url?: string;
text: string;
local: boolean;
actor: IPerson | null;
inReplyToComment?: IComment;
originComment?: IComment;
replies: IComment[];
event?: IEvent;
updatedAt?: string;
deletedAt?: string;
totalReplies: number;
insertedAt?: string;
publishedAt?: string;
isAnnouncement: boolean;
language?: string;
conversation?: IConversation;
}
export class CommentModel implements IComment {
actor: IPerson = new Person();
id?: string;
text = "";
local = true;
url?: string;
uuid?: string;
inReplyToComment?: IComment = undefined;
originComment?: IComment = undefined;
replies: IComment[] = [];
event?: IEvent = undefined;
updatedAt?: string = undefined;
deletedAt?: string = undefined;
insertedAt?: string = undefined;
totalReplies = 0;
isAnnouncement = false;
constructor(hash?: IComment) {
if (!hash) return;
this.id = hash.id;
this.uuid = hash.uuid;
this.url = hash.url;
this.text = hash.text;
this.inReplyToComment = hash.inReplyToComment;
this.originComment = hash.originComment;
this.actor = hash.actor ? new Person(hash.actor) : new Person();
this.event = new EventModel(hash.event);
this.replies = hash.replies;
this.updatedAt = new Date(hash.updatedAt as string).toISOString();
this.deletedAt = hash.deletedAt;
this.insertedAt = new Date(hash.insertedAt as string).toISOString();
this.totalReplies = hash.totalReplies;
this.isAnnouncement = hash.isAnnouncement;
}
}

136
src/types/config.model.ts Normal file
View File

@@ -0,0 +1,136 @@
import { InstancePrivacyType, InstanceTermsType, RoutingType } from "./enums";
import type { IProvider } from "./resource";
export interface IOAuthProvider {
id: string;
label?: string;
}
export interface IKeyValueConfig {
key: string;
value: string;
type: "boolean" | "integer" | "string";
}
export interface IAnalyticsConfig {
id: string;
enabled: boolean;
configuration: IKeyValueConfig[];
}
export interface IAnonymousParticipationConfig {
allowed: boolean;
validation: {
email: {
enabled: boolean;
confirmationRequired: boolean;
};
captcha: {
enabled: boolean;
};
};
}
export interface IConfig {
name: string;
description: string;
longDescription: string;
contact: string;
slogan: string;
registrationsOpen: boolean;
registrationsAllowlist: boolean;
demoMode: boolean;
countryCode: string;
eventCategories: { id: string; label: string }[];
languages: string[];
location: {
latitude: number;
longitude: number;
// accuracyRadius: number;
};
anonymous: {
participation: IAnonymousParticipationConfig;
eventCreation: {
allowed: boolean;
validation: {
email: {
enabled: boolean;
confirmationRequired: boolean;
};
captcha: {
enabled: boolean;
};
};
};
reports: {
allowed: boolean;
};
actorId: string;
};
maps: {
tiles: {
endpoint: string;
attribution: string | null;
};
routing: {
type: RoutingType;
};
};
geocoding: {
provider: string;
autocomplete: boolean;
};
terms: {
bodyHtml: string;
type: InstanceTermsType;
url: string;
};
privacy: {
bodyHtml: string;
type: InstancePrivacyType;
url: string;
};
rules: string;
resourceProviders: IProvider[];
timezones: string[];
features: {
eventCreation: boolean;
eventExternal: boolean;
groups: boolean;
antispam: boolean;
};
restrictions: {
onlyAdminCanCreateGroups: boolean;
onlyGroupsCanCreateEvents: boolean;
};
federating: boolean;
version: string;
auth: {
ldap: boolean;
databaseLogin: boolean;
oauthProviders: IOAuthProvider[];
};
uploadLimits: {
default: number;
avatar: number;
banner: number;
};
instanceFeeds: {
enabled: boolean;
};
webPush: {
enabled: boolean;
publicKey: string;
};
exportFormats: {
eventParticipants: string[];
};
analytics: IAnalyticsConfig[];
search: {
global: {
isEnabled: boolean;
isDefault: boolean;
};
};
}

17
src/types/conversation.ts Normal file
View File

@@ -0,0 +1,17 @@
import type { IActor } from "@/types/actor";
import type { IComment } from "@/types/comment.model";
import type { Paginate } from "@/types/paginate";
import { IEvent } from "./event.model";
export interface IConversation {
conversationParticipantId?: string;
id?: string;
actor?: IActor;
lastComment?: IComment;
comments: Paginate<IComment>;
participants: IActor[];
updatedAt: string;
insertedAt: string;
unread: boolean;
event?: IEvent;
}

View File

@@ -0,0 +1,74 @@
import type { IEvent } from "@/types/event.model";
import type { IPerson } from "@/types/actor/person.model";
import type { Paginate } from "./paginate";
import type { IParticipant } from "./participant.model";
import { ICurrentUserRole, INotificationPendingEnum } from "./enums";
import { IFollowedGroupEvent } from "./followedGroupEvent.model";
import { PictureInformation } from "./picture";
import { IMember } from "./actor/member.model";
import { IFeedToken } from "./feedtoken.model";
import { IApplicationToken } from "./application.model";
import { IConversation } from "./conversation";
export interface ICurrentUser {
id: string;
email: string;
isLoggedIn: boolean;
role: ICurrentUserRole;
defaultActor?: IPerson;
actors: IPerson[];
}
export interface IUserPreferredLocation {
range?: number | null;
name?: string | null;
geohash?: string | null;
}
export interface ExtendedIUserPreferredLocation extends IUserPreferredLocation {
lat: number | undefined;
lon: number | undefined;
picture?: PictureInformation;
}
export interface IUserSettings {
timezone?: string;
notificationOnDay?: boolean;
notificationEachWeek?: boolean;
notificationBeforeEvent?: boolean;
notificationPendingParticipation?: INotificationPendingEnum;
notificationPendingMembership?: INotificationPendingEnum;
groupNotifications?: INotificationPendingEnum;
location?: IUserPreferredLocation;
}
export type IActivitySettingMethod = "email" | "push";
export interface IActivitySetting {
key: string;
method: IActivitySettingMethod;
enabled: boolean;
}
export interface IUser extends ICurrentUser {
confirmedAt: string;
confirmationSendAt: string;
actors: IPerson[];
disabled: boolean;
participations: Paginate<IParticipant>;
mediaSize: number;
drafts: Paginate<IEvent>;
settings: IUserSettings;
activitySettings: IActivitySetting[];
followedGroupEvents: Paginate<IFollowedGroupEvent>;
locale: string;
provider?: string;
lastSignInAt: string;
lastSignInIp: string;
currentSignInIp: string;
currentSignInAt: string;
memberships: Paginate<IMember>;
feedTokens: IFeedToken[];
authAuthorizedApplications: IApplicationToken[];
conversations: Paginate<IConversation>;
}

55
src/types/discussions.ts Normal file
View File

@@ -0,0 +1,55 @@
import type { IActor, IPerson } from "@/types/actor";
import type { IComment } from "@/types/comment.model";
import { CommentModel } from "@/types/comment.model";
import type { Paginate } from "@/types/paginate";
export interface IDiscussion {
id?: string;
title: string;
slug?: string;
creator?: IPerson;
actor?: IActor;
lastComment?: IComment;
comments: Paginate<IComment>;
updatedAt: string;
insertedAt: string;
}
export class Discussion implements IDiscussion {
id?: string;
title = "";
comments: Paginate<IComment> = { total: 0, elements: [] };
slug?: string = undefined;
creator?: IPerson = undefined;
actor?: IActor = undefined;
lastComment?: IComment = undefined;
insertedAt = "";
updatedAt = "";
constructor(hash?: IDiscussion) {
if (!hash) return;
this.id = hash.id;
this.title = hash.title;
this.comments = {
total: hash.comments.total,
elements: hash.comments.elements.map(
(comment: IComment) => new CommentModel(comment)
),
};
this.slug = hash.slug;
this.creator = hash.creator;
this.actor = hash.actor;
this.lastComment = hash.lastComment;
this.insertedAt = hash.insertedAt;
this.updatedAt = hash.updatedAt;
}
}

303
src/types/enums.ts Normal file
View File

@@ -0,0 +1,303 @@
/* eslint-disable no-unused-vars */
export enum InstanceTermsType {
DEFAULT = "DEFAULT",
URL = "URL",
CUSTOM = "CUSTOM",
}
export enum InstancePrivacyType {
DEFAULT = "DEFAULT",
URL = "URL",
CUSTOM = "CUSTOM",
}
export enum ICurrentUserRole {
USER = "USER",
MODERATOR = "MODERATOR",
ADMINISTRATOR = "ADMINISTRATOR",
}
export enum INotificationPendingEnum {
NONE = "NONE",
DIRECT = "DIRECT",
ONE_DAY = "ONE_DAY",
ONE_HOUR = "ONE_HOUR",
ONE_WEEK = "ONE_WEEK",
}
export enum IAuthProvider {
LDAP = "ldap",
GOOGLE = "google",
DISCORD = "discord",
GITHUB = "github",
KEYCLOAK = "keycloak",
FACEBOOK = "facebook",
GITLAB = "gitlab",
TWITTER = "twitter",
}
export enum ErrorCode {
UNKNOWN = "unknown",
REGISTRATION_CLOSED = "registration_closed",
}
export enum CommentModeration {
ALLOW_ALL = "ALLOW_ALL",
MODERATED = "MODERATED",
CLOSED = "CLOSED",
}
export enum EventStatus {
TENTATIVE = "TENTATIVE",
CONFIRMED = "CONFIRMED",
CANCELLED = "CANCELLED",
}
export enum EventVisibility {
PUBLIC = "PUBLIC",
UNLISTED = "UNLISTED",
RESTRICTED = "RESTRICTED",
PRIVATE = "PRIVATE",
}
export enum EventJoinOptions {
FREE = "FREE",
RESTRICTED = "RESTRICTED",
INVITE = "INVITE",
EXTERNAL = "EXTERNAL",
}
export enum EventVisibilityJoinOptions {
PUBLIC = "PUBLIC",
LINK = "LINK",
LIMITED = "LIMITED",
}
export enum LoginErrorCode {
NEED_TO_LOGIN = "need_to_login",
}
export enum LoginError {
USER_NOT_CONFIRMED = "User account not confirmed",
USER_DOES_NOT_EXIST = "No user with this email was found",
USER_EMAIL_PASSWORD_INVALID = "Impossible to authenticate, either your email or password are invalid.",
LOGIN_PROVIDER_ERROR = "Error with Login Provider",
LOGIN_PROVIDER_NOT_FOUND = "Login Provider not found",
USER_DISABLED = "This user has been disabled",
}
export enum ResetError {
USER_IMPOSSIBLE_TO_RESET = "This user can't reset their password",
}
export enum ParticipantRole {
NOT_APPROVED = "NOT_APPROVED",
NOT_CONFIRMED = "NOT_CONFIRMED",
REJECTED = "REJECTED",
PARTICIPANT = "PARTICIPANT",
MODERATOR = "MODERATOR",
ADMINISTRATOR = "ADMINISTRATOR",
CREATOR = "CREATOR",
}
export enum PostVisibility {
PUBLIC = "PUBLIC",
UNLISTED = "UNLISTED",
RESTRICTED = "RESTRICTED",
PRIVATE = "PRIVATE",
}
export enum ReportStatusEnum {
OPEN = "OPEN",
CLOSED = "CLOSED",
RESOLVED = "RESOLVED",
}
export enum ActionLogAction {
NOTE_CREATION = "NOTE_CREATION",
NOTE_DELETION = "NOTE_DELETION",
REPORT_UPDATE_CLOSED = "REPORT_UPDATE_CLOSED",
REPORT_UPDATE_OPENED = "REPORT_UPDATE_OPENED",
REPORT_UPDATE_RESOLVED = "REPORT_UPDATE_RESOLVED",
EVENT_DELETION = "EVENT_DELETION",
COMMENT_DELETION = "COMMENT_DELETION",
ACTOR_SUSPENSION = "ACTOR_SUSPENSION",
ACTOR_UNSUSPENSION = "ACTOR_UNSUSPENSION",
USER_DELETION = "USER_DELETION",
}
export enum SearchTabs {
EVENTS = 0,
GROUPS = 1,
}
export enum ContentType {
ALL = "ALL",
EVENTS = "EVENTS",
GROUPS = "GROUPS",
}
export enum ActorType {
PERSON = "PERSON",
APPLICATION = "APPLICATION",
GROUP = "GROUP",
ORGANISATION = "ORGANISATION",
SERVICE = "SERVICE",
}
export enum MemberRole {
NOT_APPROVED = "NOT_APPROVED",
INVITED = "INVITED",
MEMBER = "MEMBER",
MODERATOR = "MODERATOR",
ADMINISTRATOR = "ADMINISTRATOR",
CREATOR = "CREATOR",
REJECTED = "REJECTED",
}
export enum Openness {
INVITE_ONLY = "INVITE_ONLY",
MODERATED = "MODERATED",
OPEN = "OPEN",
}
export enum RoutingType {
OPENSTREETMAP = "OPENSTREETMAP",
GOOGLE_MAPS = "GOOGLE_MAPS",
}
export enum RoutingTransportationType {
FOOT = "FOOT",
BIKE = "BIKE",
TRANSIT = "TRANSIT",
CAR = "CAR",
}
export enum GroupVisibility {
PUBLIC = "PUBLIC",
UNLISTED = "UNLISTED",
PRIVATE = "PRIVATE",
}
export enum AddressSearchType {
ADMINISTRATIVE = "ADMINISTRATIVE",
}
export enum ActivityType {
EVENT = "EVENT",
POST = "POST",
MEMBER = "MEMBER",
RESOURCE = "RESOURCE",
DISCUSSION = "DISCUSSION",
GROUP = "GROUP",
}
export enum ActivityEventSubject {
EVENT_CREATED = "event_created",
EVENT_UPDATED = "event_updated",
EVENT_DELETED = "event_deleted",
}
export enum ActivityEventCommentSubject {
COMMENT_POSTED = "comment_posted",
}
export enum ActivityEventParticipantSubject {
EVENT_NEW_PARTICIPATION = "event_new_participation",
}
export enum ActivityPostSubject {
POST_CREATED = "post_created",
POST_UPDATED = "post_updated",
POST_DELETED = "post_deleted",
}
export enum ActivityMemberSubject {
MEMBER_REQUEST = "member_request",
MEMBER_INVITED = "member_invited",
MEMBER_ACCEPTED_INVITATION = "member_accepted_invitation",
MEMBER_REJECTED_INVITATION = "member_rejected_invitation",
MEMBER_ADDED = "member_added",
MEMBER_JOINED = "member_joined",
MEMBER_APPROVED = "member_approved",
MEMBER_UPDATED = "member_updated",
MEMBER_REMOVED = "member_removed",
MEMBER_QUIT = "member_quit",
}
export enum ActivityResourceSubject {
RESOURCE_CREATED = "resource_created",
RESOURCE_UPDATED = "resource_renamed",
RESOURCE_MOVED = "resource_moved",
RESOURCE_DELETED = "resource_deleted",
}
export enum ActivityDiscussionSubject {
DISCUSSION_CREATED = "discussion_created",
DISCUSSION_REPLIED = "discussion_replied",
DISCUSSION_RENAMED = "discussion_renamed",
DISCUSSION_ARCHIVED = "discussion_archived",
DISCUSSION_DELETED = "discussion_deleted",
}
export enum ActivityGroupSubject {
GROUP_CREATED = "group_created",
GROUP_UPDATED = "group_updated",
}
export enum EventSortField {
BEGINS_ON = "BEGINS_ON",
INSERTED_AT = "INSERTED_AT",
UPDATED_AT = "UPDATED_AT",
}
export enum SortDirection {
ASC = "ASC",
DESC = "DESC",
}
export enum EventMetadataType {
STRING = "STRING",
INTEGER = "INTEGER",
FLOAT = "FLOAT",
BOOLEAN = "BOOLEAN",
}
export enum EventMetadataKeyType {
PLAIN = "PLAIN",
URL = "URL",
CHOICE = "CHOICE",
HANDLE = "HANDLE",
}
export enum EventMetadataCategories {
ACCESSIBILITY = "ACCESSIBILITY",
LIVE = "LIVE",
REPLAY = "REPLAY",
SOCIAL = "SOCIAL",
TOOLS = "TOOLS",
DETAILS = "DETAILS",
BOOKING = "BOOKING",
VIDEO_CONFERENCE = "VIDEO_CONFERENCE",
}
export enum InstanceFilterFollowStatus {
ALL = "ALL",
FOLLOWING = "FOLLOWING",
FOLLOWED = "FOLLOWED",
}
export enum InstanceFollowStatus {
APPROVED = "APPROVED",
PENDING = "PENDING",
NONE = "NONE",
}
export enum SearchTargets {
INTERNAL = "INTERNAL",
GLOBAL = "GLOBAL",
}
export enum AntiSpamFeedback {
HAM = "HAM",
SPAM = "SPAM",
}

18
src/types/errors.model.ts Normal file
View File

@@ -0,0 +1,18 @@
import { Operation, NextLink } from "@apollo/client/core";
import { NetworkError } from "@apollo/client/errors";
import { ExecutionResult, GraphQLError } from "graphql";
export declare class AbsintheGraphQLError extends GraphQLError {
field?: string;
code?: string;
status_code?: number;
}
export declare type AbsintheGraphQLErrors = ReadonlyArray<AbsintheGraphQLError>;
export interface ErrorResponse {
graphQLErrors?: AbsintheGraphQLErrors;
networkError?: NetworkError;
response?: ExecutionResult;
operation: Operation;
forward: NextLink;
}

View File

@@ -0,0 +1,23 @@
import {
EventMetadataCategories,
EventMetadataKeyType,
EventMetadataType,
} from "./enums";
export interface IEventMetadata {
key: string;
title?: string;
value: string;
type: EventMetadataType;
}
export interface IEventMetadataDescription extends IEventMetadata {
icon?: string;
placeholder?: string;
description?: string;
choices?: Record<string, string>;
keyType?: EventMetadataKeyType;
pattern?: RegExp;
label: string;
category: EventMetadataCategories;
}

View File

@@ -0,0 +1,63 @@
import { CommentModeration } from "./enums";
export interface IParticipationCondition {
title: string;
content: string;
url: string;
}
export interface IOffer {
price: number;
priceCurrency: string;
url: string;
}
export interface IEventOptions {
maximumAttendeeCapacity: number;
remainingAttendeeCapacity: number;
showRemainingAttendeeCapacity: boolean;
anonymousParticipation: boolean;
hideOrganizerWhenGroupEvent: boolean;
offers: IOffer[];
participationConditions: IParticipationCondition[];
attendees: string[];
program: string;
commentModeration: CommentModeration;
showParticipationPrice: boolean;
showStartTime: boolean;
showEndTime: boolean;
timezone: string | null;
isOnline: boolean;
}
export class EventOptions implements IEventOptions {
maximumAttendeeCapacity = 0;
remainingAttendeeCapacity = 0;
showRemainingAttendeeCapacity = false;
anonymousParticipation = false;
hideOrganizerWhenGroupEvent = false;
offers: IOffer[] = [];
participationConditions: IParticipationCondition[] = [];
attendees: string[] = [];
program = "";
commentModeration = CommentModeration.ALLOW_ALL;
showParticipationPrice = false;
showStartTime = true;
showEndTime = true;
timezone = null;
isOnline = false;
}

307
src/types/event.model.ts Normal file
View File

@@ -0,0 +1,307 @@
import { Address } from "@/types/address.model";
import type { IAddress } from "@/types/address.model";
import type { ITag } from "@/types/tag.model";
import type { IMedia } from "@/types/media.model";
import type { IComment } from "@/types/comment.model";
import type { Paginate } from "@/types/paginate";
import { Actor, displayName, Group } from "./actor";
import type { IActor, IGroup, IPerson } from "./actor";
import type { IParticipant } from "./participant.model";
import { EventOptions } from "./event-options.model";
import type { IEventOptions } from "./event-options.model";
import { EventJoinOptions, EventStatus, EventVisibility } from "./enums";
import { IEventMetadata, IEventMetadataDescription } from "./event-metadata";
import { IConversation } from "./conversation";
export interface IEventCardOptions {
hideDate?: boolean;
loggedPerson?: IPerson | boolean;
hideDetails?: boolean;
organizerActor?: IActor | null;
isRemoteEvent?: boolean;
isLoggedIn?: boolean;
}
export interface IEventParticipantStats {
notApproved: number;
notConfirmed: number;
rejected: number;
participant: number;
creator: number;
moderator: number;
administrator: number;
going: number;
}
export type EventType = "IN_PERSON" | "ONLINE" | null;
interface IEventEditJSON {
id?: string;
title: string;
description: string;
beginsOn: string | null;
endsOn: string | null;
status: EventStatus;
visibility: EventVisibility;
joinOptions: EventJoinOptions;
externalParticipationUrl: string | null;
draft: boolean;
picture?: IMedia | { mediaId: string } | null;
attributedToId: string | null;
organizerActorId?: string;
onlineAddress?: string;
phoneAddress?: string;
physicalAddress?: IAddress;
tags: string[];
options: IEventOptions;
contacts: { id?: string }[];
metadata: IEventMetadata[];
category: string;
}
export interface IEvent {
id?: string;
uuid: string;
url: string;
local: boolean;
title: string;
slug: string;
description: string;
beginsOn: string;
endsOn: string | null;
publishAt: string;
status: EventStatus;
visibility: EventVisibility;
joinOptions: EventJoinOptions;
externalParticipationUrl: string | null;
draft: boolean;
picture: IMedia | null;
organizerActor?: IActor;
attributedTo?: IGroup;
participantStats: IEventParticipantStats;
participants: Paginate<IParticipant>;
relatedEvents: IEvent[];
comments: IComment[];
conversations: Paginate<IConversation>;
onlineAddress?: string;
phoneAddress?: string;
physicalAddress: IAddress | null;
tags: ITag[];
options: IEventOptions;
metadata: IEventMetadataDescription[];
contacts: IActor[];
language: string;
category: string;
toEditJSON?(): IEventEditJSON;
}
export interface IEditableEvent extends Omit<IEvent, "beginsOn"> {
beginsOn: string | null;
}
export class EventModel implements IEvent {
id?: string;
beginsOn = new Date().toISOString();
endsOn: string | null = new Date().toISOString();
title = "";
url = "";
uuid = "";
slug = "";
description = "";
local = true;
onlineAddress: string | undefined = "";
phoneAddress: string | undefined = "";
physicalAddress: IAddress | null = null;
picture: IMedia | null = null;
visibility = EventVisibility.PUBLIC;
joinOptions = EventJoinOptions.FREE;
externalParticipationUrl: string | null = null;
status = EventStatus.CONFIRMED;
draft = true;
publishAt = new Date().toISOString();
language = "und";
participantStats = {
notApproved: 0,
notConfirmed: 0,
rejected: 0,
participant: 0,
moderator: 0,
administrator: 0,
creator: 0,
going: 0,
};
participants!: Paginate<IParticipant>;
relatedEvents: IEvent[] = [];
comments: IComment[] = [];
conversations!: Paginate<IConversation>;
attributedTo?: IGroup = new Group();
organizerActor?: IActor = new Actor();
tags: ITag[] = [];
contacts: IActor[] = [];
options: IEventOptions = new EventOptions();
metadata: IEventMetadataDescription[] = [];
category = "MEETING";
constructor(hash?: IEvent | IEditableEvent) {
if (!hash) return;
this.id = hash.id;
this.uuid = hash.uuid;
this.url = hash.url;
this.local = hash.local;
this.title = hash.title;
this.slug = hash.slug;
this.description = hash.description || "";
if (hash.beginsOn) {
this.beginsOn = new Date(hash.beginsOn).toISOString();
}
if (hash.endsOn) {
this.endsOn = new Date(hash.endsOn).toISOString();
} else {
this.endsOn = null;
}
this.publishAt = new Date(hash.publishAt).toISOString();
this.status = hash.status;
this.visibility = hash.visibility;
this.joinOptions = hash.joinOptions;
this.externalParticipationUrl = hash.externalParticipationUrl;
this.draft = hash.draft;
this.picture = hash.picture;
this.organizerActor = new Actor(hash.organizerActor);
this.attributedTo = new Group(hash.attributedTo);
this.participants = hash.participants;
this.relatedEvents = hash.relatedEvents;
this.onlineAddress = hash.onlineAddress;
this.phoneAddress = hash.phoneAddress;
this.physicalAddress = hash.physicalAddress
? new Address(hash.physicalAddress)
: null;
this.participantStats = hash.participantStats;
this.contacts = hash.contacts;
this.tags = hash.tags;
this.metadata = hash.metadata;
this.language = hash.language;
this.category = hash.category;
if (hash.options) this.options = hash.options;
}
toEditJSON(): IEventEditJSON {
return toEditJSON(this);
}
}
export function removeTypeName(entity: any): any {
if (entity?.__typename) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { __typename, ...purgedEntity } = entity;
return purgedEntity;
}
return entity;
}
export function toEditJSON(event: IEditableEvent): IEventEditJSON {
return {
id: event.id,
title: event.title,
description: event.description,
beginsOn: event.beginsOn ? event.beginsOn.toString() : null,
endsOn: event.endsOn ? event.endsOn.toString() : null,
status: event.status,
category: event.category,
visibility: event.visibility,
joinOptions: event.joinOptions,
externalParticipationUrl: event.externalParticipationUrl,
draft: event.draft,
tags: event.tags.map((t) => t.title),
onlineAddress: event.onlineAddress,
phoneAddress: event.phoneAddress,
physicalAddress: removeTypeName(event.physicalAddress),
options: removeTypeName(event.options),
metadata: event.metadata.map(({ key, value, type, title }) => ({
key,
value,
type,
title,
})),
attributedToId:
event.attributedTo && event.attributedTo.id
? event.attributedTo.id
: null,
contacts: event.contacts.map(({ id }) => ({
id,
})),
};
}
export function organizer(event: IEvent): IActor | null {
if (event.attributedTo?.id) {
return event.attributedTo;
}
if (event.organizerActor) {
return event.organizerActor;
}
return null;
}
export function organizerAvatarUrl(event: IEvent): string | null {
return organizer(event)?.avatar?.url ?? null;
}
export function organizerDisplayName(event: IEvent): string | null {
const organizerActor = organizer(event);
if (organizerActor) {
return displayName(organizerActor);
}
return null;
}
export function instanceOfIEvent(object: any): object is IEvent {
return "organizerActor" in object;
}

View File

@@ -0,0 +1,8 @@
import type { IPerson } from "./actor";
import type { ICurrentUser } from "./current-user.model";
export interface IFeedToken {
token: string;
actor?: IPerson;
user: ICurrentUser;
}

View File

@@ -0,0 +1,10 @@
import { IEvent } from "./event.model";
import { IPerson, IGroup } from "./actor";
import { IUser } from "./current-user.model";
export interface IFollowedGroupEvent {
profile: IPerson;
group: IGroup;
user: IUser;
event: IEvent;
}

View File

@@ -0,0 +1,16 @@
import { InstanceFollowStatus } from "./enums";
export interface IInstance {
domain: string;
hasRelay: boolean;
relayAddress: string | null;
followerStatus: InstanceFollowStatus;
followedStatus: InstanceFollowStatus;
personCount: number;
groupCount: number;
followersCount: number;
followingsCount: number;
reportsCount: number;
mediaSize: number;
eventCount: number;
}

10
src/types/login.model.ts Normal file
View File

@@ -0,0 +1,10 @@
import type { ICurrentUser } from "@/types/current-user.model";
export interface IToken {
accessToken: string;
refreshToken: string;
}
export interface ILogin extends IToken {
user: ICurrentUser;
}

23
src/types/media.model.ts Normal file
View File

@@ -0,0 +1,23 @@
export interface IMedia {
id: string;
url: string;
name: string;
alt: string;
metadata: IMediaMetadata;
}
export interface IMediaUpload {
file: File;
name: string;
alt: string | null;
}
export interface IMediaUploadWrapper {
media: IMediaUpload;
}
export interface IMediaMetadata {
width?: number;
height?: number;
blurhash?: string;
}

4
src/types/paginate.ts Normal file
View File

@@ -0,0 +1,4 @@
export interface Paginate<T> {
elements: T[];
total: number;
}

View File

@@ -0,0 +1,39 @@
import { Actor } from "./actor";
import type { IActor } from "./actor";
import { EventModel } from "./event.model";
import type { IEvent } from "./event.model";
import { ParticipantRole } from "./enums";
export interface IParticipant {
id?: string;
role: ParticipantRole;
actor: IActor;
event: IEvent;
metadata: { cancellationToken?: string; message?: string };
insertedAt?: Date;
}
export class Participant implements IParticipant {
id?: string;
event!: IEvent;
actor!: IActor;
role: ParticipantRole = ParticipantRole.NOT_APPROVED;
metadata = {};
insertedAt?: Date;
constructor(hash?: IParticipant) {
if (!hash) return;
this.id = hash.id;
this.event = new EventModel(hash.event);
this.actor = new Actor(hash.actor);
this.role = hash.role;
this.metadata = hash.metadata;
this.insertedAt = hash.insertedAt;
}
}

11
src/types/picture.ts Normal file
View File

@@ -0,0 +1,11 @@
export interface PictureInformation {
url: string;
author: {
name: string;
url: string;
};
source: {
name: string;
url: string;
};
}

23
src/types/post.model.ts Normal file
View File

@@ -0,0 +1,23 @@
import type { ITag } from "./tag.model";
import type { IMedia } from "./media.model";
import type { IActor } from "./actor";
import type { PostVisibility } from "./enums";
export interface IPost {
id?: string;
slug?: string;
url?: string;
local: boolean;
title: string;
body: string;
tags: ITag[];
picture?: IMedia | null;
draft: boolean;
visibility: PostVisibility;
author?: IActor;
attributedTo?: IActor;
publishAt?: string;
insertedAt?: string;
language?: string;
updatedAt?: string;
}

34
src/types/report.model.ts Normal file
View File

@@ -0,0 +1,34 @@
import type { IActor, IPerson } from "@/types/actor";
import type { IEvent } from "@/types/event.model";
import type { IComment } from "@/types/comment.model";
import { ActionLogAction, ReportStatusEnum } from "./enums";
export interface IActionLogObject {
id: string;
}
export interface IReportNote extends IActionLogObject {
id: string;
content: string;
moderator: IActor;
insertedAt: string;
}
export interface IReport extends IActionLogObject {
id: string;
reported: IActor | undefined;
reporter: IPerson;
events?: IEvent[];
comments: IComment[];
content: string;
notes: IReportNote[];
insertedAt: string;
updatedAt: string;
status: ReportStatusEnum;
}
export interface IActionLog {
id: string;
object: IReport | IReportNote | IEvent | IComment | IActor;
actor: IActor;
action: ActionLogAction;
insertedAt: Date;
}

46
src/types/resource.ts Normal file
View File

@@ -0,0 +1,46 @@
import type { Paginate } from "@/types/paginate";
import type { IActor, IGroup } from "@/types/actor";
export interface IResourceMetadata {
title?: string;
description?: string;
imageRemoteUrl?: string;
height?: number;
width?: number;
type?: string;
authorName?: string;
authorUrl?: string;
providerName?: string;
providerUrl?: string;
html?: string;
faviconUrl?: string;
}
export interface IResource {
id?: string;
title: string;
summary?: string;
actor?: IGroup;
url?: string;
resourceUrl: string;
path?: string;
children: Paginate<IResource>;
parent?: IResource;
metadata: IResourceMetadata;
insertedAt?: string;
updatedAt?: string;
publishedAt?: string;
creator?: IActor;
type?: string;
}
export const mapServiceTypeToIcon: Record<string, string> = {
pad: "file-document-outline",
calc: "google-spreadsheet",
visio: "webcam",
};
export interface IProvider {
type: string;
endpoint: string;
software: string;
}

17
src/types/search.model.ts Normal file
View File

@@ -0,0 +1,17 @@
import type { IGroup, IPerson } from "@/types/actor";
import type { IEvent } from "@/types/event.model";
export interface SearchEvent {
total: number;
elements: IEvent[];
}
export interface SearchGroup {
total: number;
elements: IGroup[];
}
export interface SearchPerson {
total: number;
elements: IPerson[];
}

View File

@@ -0,0 +1,11 @@
export interface IStatistics {
numberOfUsers: number;
numberOfEvents: number;
numberOfLocalEvents: number;
numberOfComments: number;
numberOfLocalComments: number;
numberOfGroups: number;
numberOfLocalGroups: number;
numberOfInstanceFollowings: number;
numberOfInstanceFollowers: number;
}

5
src/types/stats.model.ts Normal file
View File

@@ -0,0 +1,5 @@
export interface CategoryStatsModel {
key: string;
number: number;
label?: string;
}

4
src/types/tag.model.ts Normal file
View File

@@ -0,0 +1,4 @@
export interface ITag {
slug: string;
title: string;
}

10
src/types/todolist.ts Normal file
View File

@@ -0,0 +1,10 @@
import type { IActor } from "./actor";
import type { Paginate } from "./paginate";
import type { ITodo } from "./todos";
export interface ITodoList {
id: string;
title: string;
todos: Paginate<ITodo>;
actor?: IActor;
}

12
src/types/todos.ts Normal file
View File

@@ -0,0 +1,12 @@
import type { IActor, IPerson } from "@/types/actor";
import { ITodoList } from "./todolist";
export interface ITodo {
id?: string;
title: string;
status: boolean;
dueDate?: string;
creator?: IActor;
assignedTo?: IPerson;
todoList?: ITodoList;
}

View File

@@ -0,0 +1,10 @@
import { PictureInformation } from "./picture";
export type LocationType = {
lat: number | undefined;
lon: number | undefined;
name: string | undefined;
picture?: PictureInformation;
isIPLocation?: boolean;
accuracy?: number;
};