(core) Add initial tutorials implementation

Summary:
Documents can now be flagged as tutorials, which causes them to display
Markdown-formatted slides from a special GristDocTutorial table. Tutorial
documents are forked on open, and remember the last slide a user was on.
They can be restarted too, which prepares a new fork of the tutorial.

Test Plan: Browser tests.

Reviewers: jarek

Reviewed By: jarek

Differential Revision: https://phab.getgrist.com/D3813
This commit is contained in:
George Gevoian
2023-03-22 09:48:50 -04:00
parent 210aa92eed
commit be8e13df64
31 changed files with 1621 additions and 174 deletions

View File

@@ -76,9 +76,6 @@ export class Document extends Resource {
@Column({name: 'trunk_id', type: 'text', nullable: true})
public trunkId: string|null;
// Property set for forks, containing the URL ID of the trunk.
public trunkUrlId?: string|null;
@ManyToOne(_type => Document, document => document.forks)
@JoinColumn({name: 'trunk_id'})
public trunk: Document|null;
@@ -123,6 +120,19 @@ export class Document extends Resource {
if (props.options.externalId !== undefined) {
this.options.externalId = props.options.externalId;
}
if (props.options.tutorial !== undefined) {
// Tutorial metadata is merged over the existing state - unless
// metadata is set to "null", in which case the state is wiped
// completely.
if (props.options.tutorial === null) {
this.options.tutorial = null;
} else {
this.options.tutorial = this.options.tutorial || {};
if (props.options.tutorial.lastSlideIndex !== undefined) {
this.options.tutorial.lastSlideIndex = props.options.tutorial.lastSlideIndex;
}
}
}
// Normalize so that null equates with absence.
for (const key of Object.keys(this.options) as Array<keyof DocumentOptions>) {
if (this.options[key] === null) {