Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,24 @@
}
<div class="apply-draft-button-container">
@if (featureFlags.usfmFormat.enabled) {
<span
[matTooltip]="t(doesLatestHaveDraft ? 'format_draft_can' : 'format_draft_cannot')"
[style.cursor]="doesLatestHaveDraft ? 'pointer' : 'not-allowed'"
>
<button mat-button (click)="navigateToFormatting()" [disabled]="!doesLatestHaveDraft">
@if (mustChooseFormattingOptions) {
<button mat-flat-button (click)="navigateToFormatting()">
<mat-icon>build</mat-icon>
<transloco key="editor_draft_tab.format_draft"></transloco>
<transloco class="hide-lt-md" key="editor_draft_tab.format_draft"></transloco>
<transloco class="hide-gt-md" key="editor_draft_tab.formatting"></transloco>
</button>
</span>
} @else {
<span
[matTooltip]="t(doesLatestHaveDraft ? 'format_draft_can' : 'format_draft_cannot')"
[style.cursor]="doesLatestHaveDraft ? 'pointer' : 'not-allowed'"
>
<button mat-button (click)="navigateToFormatting()" [disabled]="!doesLatestHaveDraft">
<mat-icon>build</mat-icon>
<transloco class="hide-lt-md" key="editor_draft_tab.format_draft"></transloco>
<transloco class="hide-gt-md" key="editor_draft_tab.formatting"></transloco>
</button>
</span>
}
}
@if (userAppliedDraft) {
<span class="draft-indicator">
Expand All @@ -61,16 +70,20 @@
</span>
}
@if (canApplyDraft) {
<button mat-flat-button color="primary" (click)="applyDraft()">
<mat-icon>auto_awesome</mat-icon>
@if (isDraftApplied) {
<transloco key="editor_draft_tab.reapply_to_project"></transloco>
} @else {
<transloco key="editor_draft_tab.apply_to_project"></transloco>
}
</button>
}
@if (!canApplyDraft) {
<span
[matTooltip]="t(mustChooseFormattingOptions ? 'format_draft_before' : 'add_chapter_to_project')"
[style.cursor]="mustChooseFormattingOptions ? 'not-allowed' : 'pointer'"
>
<button mat-flat-button color="primary" (click)="applyDraft()" [disabled]="mustChooseFormattingOptions">
<mat-icon>auto_awesome</mat-icon>
@if (isDraftApplied) {
<transloco key="editor_draft_tab.reapply_to_project"></transloco>
} @else {
<transloco key="editor_draft_tab.apply_to_project"></transloco>
}
</button>
</span>
} @else {
<app-notice icon="warning" type="warning" mode="fill-dark">
{{ "editor_draft_tab.cannot_import" | transloco }}
</app-notice>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ app-notice {
flex-grow: 1;
display: flex;
justify-content: flex-end;
column-gap: 4px;
gap: 4px;
align-items: center;
flex-wrap: wrap;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { cloneDeep } from 'lodash-es';
import { TranslocoMarkupModule } from 'ngx-transloco-markup';
import { Delta } from 'quill';
import { SFProjectRole } from 'realtime-server/lib/esm/scriptureforge/models/sf-project-role';
import { createTestProjectProfile } from 'realtime-server/lib/esm/scriptureforge/models/sf-project-test-data';
import { ParagraphBreakFormat, QuoteFormat } from 'realtime-server/lib/esm/scriptureforge/models/translate-config';
import { of } from 'rxjs';
import { anything, mock, verify, when } from 'ts-mockito';
import { ActivatedProjectService } from 'xforge-common/activated-project.service';
Expand Down Expand Up @@ -351,6 +353,67 @@ describe('EditorDraftComponent', () => {
}));

describe('applyDraft', () => {
it('should allow user to apply draft when formatting selected', fakeAsync(() => {
const testProjectDoc: SFProjectProfileDoc = {
data: createTestProjectProfile({
texts: [
{
bookNum: 1,
chapters: [{ number: 1, permissions: { user01: SFProjectRole.ParatextAdministrator }, hasDraft: true }]
}
],
translateConfig: {
draftConfig: {
usfmConfig: { paragraphFormat: ParagraphBreakFormat.BestGuess, quoteFormat: QuoteFormat.Denormalized }
}
}
})
} as SFProjectProfileDoc;
when(mockDraftGenerationService.draftExists(anything(), anything(), anything())).thenReturn(of(true));
when(mockDraftGenerationService.getGeneratedDraftHistory(anything(), anything(), anything())).thenReturn(
of(draftHistory)
);
when(mockActivatedProjectService.changes$).thenReturn(of(testProjectDoc));
when(mockDialogService.confirm(anything(), anything())).thenResolve(true);
spyOn<any>(component, 'getTargetOps').and.returnValue(of(targetDelta.ops));
when(mockDraftHandlingService.getDraft(anything(), anything())).thenReturn(of(draftDelta.ops!));
when(mockDraftHandlingService.draftDataToOps(anything(), anything())).thenReturn(draftDelta.ops!);

fixture.detectChanges();
tick(EDITOR_READY_TIMEOUT);

expect(component.mustChooseFormattingOptions).toBe(false);
flush();
}));

it('should guide user to select formatting options when formatting not selected', fakeAsync(() => {
const testProjectDoc: SFProjectProfileDoc = {
data: createTestProjectProfile({
texts: [
{
bookNum: 1,
chapters: [{ number: 1, permissions: { user01: SFProjectRole.ParatextAdministrator }, hasDraft: true }]
}
]
})
} as SFProjectProfileDoc;
when(mockDraftGenerationService.draftExists(anything(), anything(), anything())).thenReturn(of(true));
when(mockDraftGenerationService.getGeneratedDraftHistory(anything(), anything(), anything())).thenReturn(
of(draftHistory)
);
when(mockActivatedProjectService.changes$).thenReturn(of(testProjectDoc));
when(mockDialogService.confirm(anything(), anything())).thenResolve(true);
spyOn<any>(component, 'getTargetOps').and.returnValue(of(targetDelta.ops));
when(mockDraftHandlingService.getDraft(anything(), anything())).thenReturn(of(draftDelta.ops!));
when(mockDraftHandlingService.draftDataToOps(anything(), anything())).thenReturn(draftDelta.ops!);

fixture.detectChanges();
tick(EDITOR_READY_TIMEOUT);

expect(component.mustChooseFormattingOptions).toBe(true);
flush();
}));

it('should show a prompt when applying if the target has content', fakeAsync(() => {
const testProjectDoc: SFProjectProfileDoc = {
data: createTestProjectProfile()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export class EditorDraftComponent implements AfterViewInit, OnChanges {
isDraftReady = false;
isDraftApplied = false;
userAppliedDraft = false;
hasFormattingSelected = true;

private selectedRevisionSubject = new BehaviorSubject<Revision | undefined>(undefined);
private selectedRevision$ = this.selectedRevisionSubject.asObservable();
Expand Down Expand Up @@ -99,6 +100,28 @@ export class EditorDraftComponent implements AfterViewInit, OnChanges {
private readonly router: Router
) {}

get bookId(): string {
return this.bookNum !== undefined ? Canon.bookNumberToId(this.bookNum) : '';
}

get canApplyDraft(): boolean {
if (this.targetProject == null || this.bookNum == null || this.chapter == null || this.draftDelta?.ops == null) {
return false;
}
return this.draftHandlingService.canApplyDraft(this.targetProject, this.bookNum, this.chapter, this.draftDelta.ops);
}

get doesLatestHaveDraft(): boolean {
return (
this.targetProject?.texts.find(t => t.bookNum === this.bookNum)?.chapters.find(c => c.number === this.chapter)
?.hasDraft ?? false
);
}

get mustChooseFormattingOptions(): boolean {
return this.featureFlags.usfmFormat.enabled && !this.hasFormattingSelected && this.doesLatestHaveDraft;
}

ngOnChanges(): void {
if (this.projectId == null || this.bookNum == null || this.chapter == null) {
throw new Error('projectId, bookNum, or chapter is null');
Expand All @@ -118,10 +141,6 @@ export class EditorDraftComponent implements AfterViewInit, OnChanges {
this.selectedRevisionSubject.next(this.selectedRevision);
}

get bookId(): string {
return this.bookNum !== undefined ? Canon.bookNumberToId(this.bookNum) : '';
}

populateDraftTextInit(): void {
combineLatest([
this.onlineStatusService.onlineStatus$,
Expand Down Expand Up @@ -178,6 +197,7 @@ export class EditorDraftComponent implements AfterViewInit, OnChanges {
filterNullish(),
tap(projectDoc => {
this.targetProject = projectDoc.data;
this.hasFormattingSelected = projectDoc.data?.translateConfig.draftConfig.usfmConfig != null;
}),
distinctUntilChanged(),
map(() => initialTimestamp)
Expand Down Expand Up @@ -238,20 +258,6 @@ export class EditorDraftComponent implements AfterViewInit, OnChanges {
});
}

get canApplyDraft(): boolean {
if (this.targetProject == null || this.bookNum == null || this.chapter == null || this.draftDelta?.ops == null) {
return false;
}
return this.draftHandlingService.canApplyDraft(this.targetProject, this.bookNum, this.chapter, this.draftDelta.ops);
}

get doesLatestHaveDraft(): boolean {
return (
this.targetProject?.texts.find(t => t.bookNum === this.bookNum)?.chapters.find(c => c.number === this.chapter)
?.hasDraft ?? false
);
}

navigateToFormatting(): void {
this.router.navigateByUrl(`/projects/${this.projectId}/draft-generation/format/${this.bookId}/${this.chapter}`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -402,13 +402,16 @@
"your_comment": "Your comment"
},
"editor_draft_tab": {
"add_chapter_to_project": "Add this chapter to the project",
"apply_to_project": "Add to project",
"cannot_import": "You cannot import this draft because you do not have permission to edit this chapter. Permissions can be updated in Paratext.",
"click_book_to_preview": "Click a book below to preview the draft and add it to your project.",
"draft_indicator_applied": "Added",
"draft_legacy_warning": "We have updated our drafting functionality. You can take advantage of this by [link:generateDraftUrl]generating a new draft[/link].",
"error_applying_draft": "Failed to add the draft to the project. Try again later.",
"formatting": "Formatting",
"format_draft": "Formatting options",
"format_draft_before": "Select formatting options before adding it to your project.",
"format_draft_can": "Customize formatting options for the draft",
"format_draft_cannot": "You can only change formatting for books from the latest draft",
"no_draft_notice": "{{ bookChapterName }} has no draft.",
Expand Down
Loading