app/models/Template.ts
Rama main · réplica de lectura del repositorio original.
import { addDays, differenceInSeconds } from "date-fns";
import i18n from "i18next";
import { computed, observable, override } from "mobx";
import type { ProsemirrorData } from "@shared/types";
import { ProsemirrorDataHelper } from "@shared/utils/ProsemirrorDataHelper";
import { isRTL } from "@shared/utils/rtl";
import type TemplatesStore from "~/stores/TemplatesStore";
import User from "~/models/User";
import Collection from "./Collection";
import ParanoidModel from "./base/ParanoidModel";
import Field from "./decorators/Field";
import Relation from "./decorators/Relation";
import type { Searchable } from "./interfaces/Searchable";
export default class Template extends ParanoidModel implements Searchable {
static modelName = "Template";
constructor(fields: Record<string, unknown>, store: TemplatesStore) {
super(fields, store);
this.initialize(fields);
}
store: TemplatesStore;
@Field
@observable.shallow
data: ProsemirrorData;
@computed
get searchContent(): string {
return this.title;
}
@computed
get searchSuppressed(): boolean {
return this.isDeleted;
}
/**
* The id of the collection that this template belongs to, if any.
*/
@Field
@observable
collectionId?: string | null = undefined;
/**
* The collection that this template belongs to.
*/
@Relation(() => Collection, { onDelete: "cascade" })
collection?: Collection;
/**
* The title of the template.
*/
@Field
@observable
title: string;
/**
* An icon (or) emoji to use as the template icon.
*/
@Field
@observable
icon?: string | null = undefined;
/**
* The color to use for the template icon.
*/
@Field
@observable
color?: string | null = undefined;
/**
* Whether the template layout is displayed full page width.
*/
@Field
@observable
fullWidth: boolean;
/**
* The likely language of the template, in ISO 639-1 format.
*/
@Field
@observable
language: string | undefined = undefined;
@Relation(() => User)
createdBy: User | undefined;
@Relation(() => User)
updatedBy: User | undefined;
/** The path to the template. The server makes the slug from the title. */
@observable
path: string;
@observable
urlId: string;
/**
* The date the template was published, and so made available to other members
* of the workspace.
*/
@observable
publishedAt: string | undefined = undefined;
/**
* Publishes the template, making it available to other members of the
* workspace.
*
* @returns a promise that resolves when the template has been published.
*/
publish = () => this.save(undefined, { publish: true });
/**
* Whether the template was created moments ago.
*
* @returns true if the template was created within the last ten seconds.
*/
get isJustCreated(): boolean {
return differenceInSeconds(new Date(), new Date(this.createdAt)) < 10;
}
/**
* Returns the direction of the template text, either "rtl" or "ltr"
*/
@computed
get dir(): "rtl" | "ltr" {
return this.rtl ? "rtl" : "ltr";
}
/**
* Returns true if the template text is right-to-left
*/
@computed
get rtl() {
return isRTL(this.title);
}
@override
get isDeleted(): boolean {
return !!this.deletedAt;
}
get hasEmptyTitle(): boolean {
return this.title === "";
}
/**
* Whether the template has neither a title nor any content.
*/
@computed
get isEmpty(): boolean {
return (
!this.title?.trim() &&
(!this.data || ProsemirrorDataHelper.isEmpty(this.data))
);
}
@computed
get isWorkspaceTemplate(): boolean {
return !this.collectionId;
}
/**
* Whether the template is an unpublished draft, only visible to the user that
* created it.
*/
@computed
get isDraft(): boolean {
return !this.publishedAt;
}
@computed
get permanentlyDeletedAt(): string | undefined {
if (!this.deletedAt) {
return undefined;
}
return addDays(new Date(this.deletedAt), 30).toString();
}
get displayTitle(): string {
return this.title || i18n.t("Untitled");
}
@computed
get initial(): string {
return (this.displayTitle?.charAt(0) ?? "?").toUpperCase();
}
@computed
get isActive(): boolean {
return !this.isDeleted;
}
}