Ctrl K
ComunidadGuías…

app/models/Template.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
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;
  }
}