Skip to content
Open
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
10 changes: 6 additions & 4 deletions frontend/src/js/components/viewer/PreviewSwitcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import PropTypes from "prop-types";
import { resourcePropType } from "../../types/Resource";
import _ from "lodash";

import { hasTextContent } from "../../util/resourceUtils";
import { keyboardShortcuts } from "../../util/keyboardShortcuts";
import { KeyboardShortcut } from "../UtilComponents/KeyboardShortcut";

Expand Down Expand Up @@ -34,7 +35,7 @@ class PreviewSwitcher extends React.Component {
return false;
}

if (this.props.view === "text" && !resource.text) {
if (this.props.view === "text" && !hasTextContent(resource)) {
return false;
}

Expand Down Expand Up @@ -65,8 +66,8 @@ class PreviewSwitcher extends React.Component {
this.props.view &&
!this.currentViewModeIsValid(this.props.resource)
) {
// Automatically switch to a preview if you can preview but there's no extracted text or OCR
if (this.props.resource.text) {
// Automatically switch to a valid view when the current one is not available
if (hasTextContent(this.props.resource)) {
this.props.setResourceView("text");
} else if (this.props.resource.ocr) {
const languages = Object.keys(this.props.resource.ocr);
Expand Down Expand Up @@ -136,7 +137,8 @@ class PreviewSwitcher extends React.Component {
shortcut={keyboardShortcuts.showPreview}
func={this.showPreview}
/>
{this.props.resource.text && !this.props.resource.transcript ? (
{hasTextContent(this.props.resource) &&
!this.props.resource.transcript ? (
<PreviewLink
current={current}
text="Text"
Expand Down
1 change: 1 addition & 0 deletions frontend/src/js/types/Resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export type Resource = BasicResource & {
children: BasicResource[];
comments: CommentData[];
selection?: ResourceRange;
previewStatus: string;
text: HighlightableText;
ocr?: {
[lang: string]: HighlightableText;
Expand Down
129 changes: 129 additions & 0 deletions frontend/src/js/util/resourceUtils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { Resource, HighlightableText } from "../types/Resource";
import { getDefaultView, hasTextContent } from "./resourceUtils";

function makeHighlightableText(contents: string): HighlightableText {
return { contents, highlights: [] };
}

function makeResource(overrides?: Partial<Resource>): Resource {
return {
uri: "test/doc.pdf",
type: "blob",
isExpandable: false,
processingStage: { type: "processed" },
extracted: true,
mimeTypes: ["application/pdf"],
fileSize: 1024,
parents: [],
children: [],
comments: [],
previewStatus: "disabled",
text: makeHighlightableText(""),
...overrides,
} as Resource;
}

describe("hasTextContent", () => {
test("returns true when text has content", () => {
const resource = makeResource({
text: makeHighlightableText("Hello world"),
});
expect(hasTextContent(resource)).toBe(true);
});

test("returns false when text is empty", () => {
const resource = makeResource({
text: makeHighlightableText(""),
});
expect(hasTextContent(resource)).toBe(false);
});

test("returns false when text is only whitespace", () => {
const resource = makeResource({
text: makeHighlightableText(" \n\t "),
});
expect(hasTextContent(resource)).toBe(false);
});
});

describe("getDefaultView", () => {
test("returns undefined for non-blob resources", () => {
const resource = makeResource({
text: makeHighlightableText("content"),
type: "file",
});
expect(getDefaultView(resource)).toBeUndefined();
});

test("returns 'text' when document has text content", () => {
const resource = makeResource({
text: makeHighlightableText("Hello world"),
});
expect(getDefaultView(resource)).toBe("text");
});

test("returns transcript view when transcript exists", () => {
const resource = makeResource({
text: makeHighlightableText(""),
transcript: {
english: makeHighlightableText("transcript content"),
},
});
expect(getDefaultView(resource)).toBe("transcript.english");
});

test("returns first OCR language when text is empty but OCR has content", () => {
const resource = makeResource({
text: makeHighlightableText(""),
ocr: {
english: makeHighlightableText("OCR text"),
},
});
expect(getDefaultView(resource)).toBe("ocr.english");
});

test("skips empty OCR entries and returns first non-empty one", () => {
const resource = makeResource({
text: makeHighlightableText(""),
ocr: {
french: makeHighlightableText(""),
english: makeHighlightableText("OCR text"),
},
});
expect(getDefaultView(resource)).toBe("ocr.english");
});

test("returns 'preview' when text is empty, no usable OCR, but preview is available", () => {
const resource = makeResource({
text: makeHighlightableText(""),
previewStatus: "pass_through",
});
expect(getDefaultView(resource)).toBe("preview");
});

test("returns 'preview' when text is empty and all OCR entries are also empty", () => {
const resource = makeResource({
text: makeHighlightableText(""),
ocr: {
english: makeHighlightableText(""),
},
previewStatus: "pdf_generated",
});
expect(getDefaultView(resource)).toBe("preview");
});

test("returns 'text' as last resort when text is empty and preview is disabled", () => {
const resource = makeResource({
text: makeHighlightableText(""),
previewStatus: "disabled",
});
expect(getDefaultView(resource)).toBe("text");
});

test("returns undefined when resource.text is undefined", () => {
const resource = makeResource({
text: undefined as unknown as HighlightableText,
});
expect(getDefaultView(resource)).toBeUndefined();
});
});
13 changes: 11 additions & 2 deletions frontend/src/js/util/resourceUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ export function getCurrentResource(prefix: string): string {
return window.location.pathname.split("/").slice(2).join("/");
}

export function hasTextContent(resource: Resource): boolean {
return resource.text.contents.trim() !== "";
}

export function getDefaultView(resource: Resource): string | undefined {
if (resource.type !== "blob") {
return undefined;
Expand All @@ -93,17 +97,22 @@ export function getDefaultView(resource: Resource): string | undefined {
return undefined;
}

if (resource.text.contents.trim().length === 0 && resource.ocr) {
if (!hasTextContent(resource) && resource.ocr) {
const ocrEntries = Object.entries(resource.ocr);
const firstNonEmptyEntry = ocrEntries.find(
([_, { contents }]) => contents.trim().length > 0,
([_, { contents }]) => contents.trim() !== "",
);

if (firstNonEmptyEntry) {
return `ocr.${firstNonEmptyEntry[0]}`;
}
}

// If text is empty and there's no usable OCR, prefer preview over an empty text view
if (!hasTextContent(resource) && resource.previewStatus !== "disabled") {
return "preview";
}

return "text";
}

Expand Down
Loading