(core) Add support for auto-copying docs on signup

Summary:
The new "copyDoc" query parameter on the login page sets a short-lived cookie, which is
then read when welcoming a new user to copy that document to their Home workspace, and
redirect to it. Currently, only templates and bare forks set this parameter.

A new API endpoint for copying a document to a workspace was also added.

Test Plan: Browser tests.

Reviewers: paulfitz

Reviewed By: paulfitz

Differential Revision: https://phab.getgrist.com/D3992
This commit is contained in:
George Gevoian
2023-09-06 14:35:46 -04:00
parent 90fb4434cc
commit 3dadf93c98
26 changed files with 1057 additions and 276 deletions

View File

@@ -340,6 +340,11 @@ export interface DocStateComparisonDetails {
rightChanges: ActionSummary;
}
export interface CopyDocOptions {
documentName: string;
asTemplate?: boolean;
}
export interface UserAPI {
getSessionActive(): Promise<ActiveSessionInfo>;
setSessionActive(email: string, org?: string): Promise<void>;
@@ -355,6 +360,7 @@ export interface UserAPI {
newWorkspace(props: Partial<WorkspaceProperties>, orgId: number|string): Promise<number>;
newDoc(props: Partial<DocumentProperties>, workspaceId: number): Promise<string>;
newUnsavedDoc(options?: {timezone?: string}): Promise<string>;
copyDoc(sourceDocumentId: string, workspaceId: number, options: CopyDocOptions): Promise<string>;
renameOrg(orgId: number|string, name: string): Promise<void>;
renameWorkspace(workspaceId: number, name: string): Promise<void>;
renameDoc(docId: string, name: string): Promise<void>;
@@ -569,6 +575,21 @@ export class UserAPIImpl extends BaseAPI implements UserAPI {
});
}
public async copyDoc(
sourceDocumentId: string,
workspaceId: number,
options: CopyDocOptions
): Promise<string> {
return this.requestJson(`${this._url}/api/docs`, {
method: 'POST',
body: JSON.stringify({
sourceDocumentId,
workspaceId,
...options,
}),
});
}
public async renameOrg(orgId: number|string, name: string): Promise<void> {
await this.request(`${this._url}/api/orgs/${orgId}`, {
method: 'PATCH',
@@ -984,6 +1005,14 @@ export class DocAPIImpl extends BaseAPI implements DocAPI {
return this.requestJson(`${this._url}/compare/${remoteDocId}${q}`);
}
public async copyDoc(workspaceId: number, options: CopyDocOptions): Promise<string> {
const {documentName, asTemplate} = options;
return this.requestJson(`${this._url}/copy`, {
body: JSON.stringify({workspaceId, documentName, asTemplate}),
method: 'POST'
});
}
public async compareVersion(leftHash: string, rightHash: string): Promise<DocStateComparison> {
const url = new URL(`${this._url}/compare`);
url.searchParams.append('left', leftHash);

View File

@@ -124,6 +124,7 @@ export interface IGristUrlState {
billingTask?: BillingTask;
embed?: boolean;
state?: string;
srcDocId?: string;
style?: InterfaceStyle;
compare?: string;
linkParameters?: Record<string, string>; // Parameters to pass as 'user.Link' in granular ACLs.
@@ -395,7 +396,9 @@ export function decodeUrl(gristConfig: Partial<GristLoadConfig>, location: Locat
if (sp.has('state')) {
state.params!.state = sp.get('state')!;
}
if (sp.has('srcDocId')) {
state.params!.srcDocId = sp.get('srcDocId')!;
}
if (sp.has('style')) {
let style = sp.get('style');
if (style === 'light') {