diff --git a/.babelrc b/.babelrc
index df92d9a..04fd246 100644
--- a/.babelrc
+++ b/.babelrc
@@ -2,6 +2,8 @@
"presets": [
"@babel/preset-env",
"@babel/preset-typescript",
- "@babel/preset-react"
+ ["@babel/preset-react", {
+ "runtime": "automatic"
+ }]
]
}
diff --git a/package.json b/package.json
index 57cebc3..73ca203 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "consort-frontend",
- "version": "0.19.0",
+ "version": "0.20.0",
"description": "",
"engines": {
"npm": ">=10",
diff --git a/server/app.js b/server/app.js
index 3903803..88cd249 100644
--- a/server/app.js
+++ b/server/app.js
@@ -39,7 +39,14 @@ app.use(session({
secret: 'keyboard cat',
resave: false, // don't save session if unmodified
saveUninitialized: false, // don't create session until something stored
- store: new SQLiteStore({ db: 'sessions.db', dir: './var/db' })
+ store: new SQLiteStore({ db: 'sessions.db', dir: './var/db' }),
+ rolling: true, // Reset session expiration on each request
+ cookie: {
+ maxAge: 24 * 60 * 60 * 1000, // 1 day in milliseconds
+ httpOnly: true,
+ secure: process.env.NODE_ENV === 'production', // Use secure cookies in production
+ sameSite: 'lax'
+ }
}));
// CORS middleware for all /api/* routes - must be before authentication
diff --git a/src/actions/client.js b/src/actions/client.js
index cb64b8e..b3cead9 100644
--- a/src/actions/client.js
+++ b/src/actions/client.js
@@ -2,14 +2,11 @@
import {
createEmptyDatasetRequest,
- getDatasetMetadataLoop,
- getFileInDataset,
uploadFileToDatasetRequest
} from "../utils/dataset";
-import config from "../app.config";
import {wordPipeline} from "../utils/word_pipeline";
import {pdfPipeline} from "../utils/pdf_pipeline";
-import {SET_EXTRACTION_STATUS, setExtractionStatus} from "./file";
+import {setExtractionStatus} from "./file";
import {ADD_FILE_TO_DATASET, addFileToDataset, CREATE_DATASETS, createDataset} from "./dataset";
import {resetFileToDefault} from "./file";
import {resetDatasetToDefault} from "./dataset";
@@ -22,43 +19,44 @@ import {resetUserCategoryToDefault} from "./dashboard";
export function createUploadExtract(file, config) {
return async function createUploadExtractThunk(dispatch) {
// this function creates an empty dataset. uploads the file to the dataset and submits for extraction
- console.log("StatementType", config.statementType)
- console.log("UserCategory", config.userCategory)
+ // console.log("StatementType", config.statementType)
+ // console.log("UserCategory", config.userCategory)
+ console.log("Config", config);
// Clowder API call to create empty dataset
const file_name = file.name.replace(/\.[^/.]+$/, ""); // get filename without extension as dataset name
const file_description = file.type;
- console.log("Uploading file", file_name);
+ // console.log("Uploading file", file_name);
const dataset_json = await createEmptyDatasetRequest(file_name, file_description); // returns the dataset ID {id:xxx}
if (dataset_json !== undefined && dataset_json !== null) {
dispatch(createDataset(CREATE_DATASETS, dataset_json));
// upload input file to dataset
- let file_json = await uploadFileToDatasetRequest(dataset_json.id, file); // return file ID. {id:xxx} OR {ids:[{id:xxx}, {id:xxx}]}
+ const file_json = await uploadFileToDatasetRequest(dataset_json.id, file); // return file ID. {id:xxx} OR {ids:[{id:xxx}, {id:xxx}]}
if (file_json !== undefined){
file_json["filename"] = file.name;
// submit uploaded file for extraction
dispatch(setExtractionStatus("Analyzing file"));
- if (file.type == "application/vnd.openxmlformats-officedocument.wordprocessingml.document" || file.type =="application/msword"){
+ if (file.type === "application/vnd.openxmlformats-officedocument.wordprocessingml.document" || file.type === "application/msword"){
const word_pipeline_status = await wordPipeline(file_json, dataset_json, config, dispatch);
if (word_pipeline_status) {
- console.log("Analysis complete");
+ // console.log("Analysis complete");
dispatch(setExtractionStatus("Analysis complete"));
}
else {
- console.error("Analysis failed");
+ // console.error("Analysis failed");
dispatch(setExtractionStatus("Analysis failed"));
}
}
- else if (file.type == "application/pdf") {
+ else if (file.type === "application/pdf") {
const pdf_pipeline_status = await pdfPipeline(file_json, dataset_json, config, dispatch);
if (pdf_pipeline_status) {
- console.log("Analysis complete.");
+ // console.log("Analysis complete.");
dispatch(setExtractionStatus("Analysis complete"));
}
else {
- console.error("Analysis failed");
+ // console.error("Analysis failed");
dispatch(setExtractionStatus("Analysis failed"));
}
@@ -68,19 +66,18 @@ export function createUploadExtract(file, config) {
}
else {
// TODO add error action
- console.error("Error in file type");
+ // console.error("Error in file type");
dispatch(setExtractionStatus("Error in file type"));
}
// after submitting uploaded file for extraction, add the file to dataset state
dispatch(addFileToDataset(ADD_FILE_TO_DATASET, file_json));
}
else {
- console.error("Error in clowder upload of file ", file.name)
- dispatch(setExtractionStatus("Error in clowder upload of file " + file.name));
+ dispatch(setExtractionStatus(`Error in clowder upload of file ${file.name}`));
}
}
else {
- console.error("Error in dataset creation");
+ // console.error("Error in dataset creation");
dispatch(setExtractionStatus("Error in dataset creation"));
dispatch(resetFileToDefault());
dispatch(resetDatasetToDefault());
diff --git a/src/actions/dashboard.js b/src/actions/dashboard.js
index a4eb822..f51fdc6 100644
--- a/src/actions/dashboard.js
+++ b/src/actions/dashboard.js
@@ -64,9 +64,9 @@ export function checkAuthenticationStatus() {
return async (dispatch) => {
dispatch(setAuthenticationLoading(true));
try {
- const response = await fetch('/isAuthenticated', {
- method: 'GET',
- credentials: 'include',
+ const response = await fetch("/isAuthenticated", {
+ method: "GET",
+ credentials: "include",
});
const data = await response.json();
dispatch(setAuthenticationStatus(data.isAuthenticated));
@@ -78,7 +78,7 @@ export function checkAuthenticationStatus() {
dispatch(setUserCategory(SET_USER_CATEGORY, "author"));
}
} catch (error) {
- console.error('Error checking authentication status:', error);
+ // console.error("Error in checking authentication status", error);
dispatch(setAuthenticationStatus(false));
dispatch(setUserCategory(SET_USER_CATEGORY, "author"));
} finally {
diff --git a/src/actions/dataset.js b/src/actions/dataset.js
index e41a2d6..ddd1119 100644
--- a/src/actions/dataset.js
+++ b/src/actions/dataset.js
@@ -1,7 +1,7 @@
// dataset actions
import {getHeader} from "../utils/common";
-import {createEmptyDatasetRequest, getDatasetsRequest} from "../utils/dataset";
+import {getDatasetsRequest} from "../utils/dataset";
// receive datasets action
export const RECEIVE_DATASETS = "RECEIVE_DATASETS";
@@ -29,9 +29,9 @@ export const DELETE_DATASET = "DELETE_DATASET";
// update dataset status action
export const UPDATE_DATASET_STATUS = "UPDATE_DATASET_STATUS";
export const updateDatasetStatus = (datasetId, status) => ({
- type: UPDATE_DATASET_STATUS,
- datasetId,
- status
+ type: UPDATE_DATASET_STATUS,
+ datasetId,
+ status
});
// fetchDatasets thunk function
@@ -43,34 +43,34 @@ export const fetchDatasets = (title = null, limit="5") => async dispatch => {
};
export function fetchFilesInDataset(id) {
- let url = `/api/datasets/${id}/files`;
+ const url = `/api/datasets/${id}/files`;
return (dispatch) => {
return fetch(url, {mode: "cors"})
- .then((response) => {
- if (response.status === 200) {
- response.json().then(json => {
- dispatch(receiveFilesInDataset(RECEIVE_FILES_IN_DATASET, json));
- });
- } else {
- dispatch(receiveFilesInDataset(RECEIVE_FILES_IN_DATASET, []));
- }
- });
+ .then((response) => {
+ if (response.status === 200) {
+ response.json().then(json => {
+ dispatch(receiveFilesInDataset(RECEIVE_FILES_IN_DATASET, json));
+ });
+ } else {
+ dispatch(receiveFilesInDataset(RECEIVE_FILES_IN_DATASET, []));
+ }
+ });
};
}
export function fetchDatasetAbout(id) {
- let url = `/api/datasets/${id}`;
+ const url = `/api/datasets/${id}`;
return (dispatch) => {
return fetch(url, {mode: "cors"})
- .then((response) => {
- if (response.status === 200) {
- response.json().then(json => {
- dispatch(receiveDatasetAbout(RECEIVE_DATASET_ABOUT, json));
- });
- } else {
- dispatch(receiveDatasetAbout(RECEIVE_DATASET_ABOUT, []));
- }
- });
+ .then((response) => {
+ if (response.status === 200) {
+ response.json().then(json => {
+ dispatch(receiveDatasetAbout(RECEIVE_DATASET_ABOUT, json));
+ });
+ } else {
+ dispatch(receiveDatasetAbout(RECEIVE_DATASET_ABOUT, []));
+ }
+ });
};
}
@@ -86,10 +86,10 @@ export function setDatasetMetadata(type, json) {
}
export function postDatasetMetadata(id, metadata) {
- let url = `/api/datasets/${id}/metadata.jsonld`;
- let authHeader = getHeader();
- authHeader.append('Accept', 'application/json');
- authHeader.append('Content-Type', 'application/json');
+ const url = `/api/datasets/${id}/metadata.jsonld`;
+ const authHeader = getHeader();
+ authHeader.append("Accept", "application/json");
+ authHeader.append("Content-Type", "application/json");
const body = JSON.stringify(metadata);
return (dispatch) => {
return fetch(url, {method:"POST", mode: "cors", headers: authHeader, body:body})
@@ -104,24 +104,20 @@ export function postDatasetMetadata(id, metadata) {
}
export function deleteDataset(datasetId) {
- let url = `/api/datasets/${datasetId}?superAdmin=true`;
+ const url = `/api/datasets/${datasetId}?superAdmin=true`;
return (dispatch) => {
return fetch(url, {mode: "cors", method: "DELETE"})
- .then((response) => {
- if (response.status === 200) {
- response.json().then(json => {
- dispatch({
- type: DELETE_DATASET,
- dataset: {"id": datasetId},
- receivedAt: Date.now(),
+ .then((response) => {
+ if (response.status === 200) {
+ response.json().then(() => {
+ dispatch({
+ type: DELETE_DATASET,
+ dataset: {"id": datasetId},
+ receivedAt: Date.now(),
+ });
});
- });
- } else {
- response.json().then(json => {
- console.error("Failed to delete dataset:", json);
- });
- }
- });
+ }
+ });
};
}
diff --git a/src/actions/file.js b/src/actions/file.js
index 7d13ad1..d0787df 100644
--- a/src/actions/file.js
+++ b/src/actions/file.js
@@ -1,6 +1,5 @@
// file actions
-import config from "../app.config";
import {getPreviewsRequest} from "../utils/file";
@@ -16,18 +15,18 @@ export function receiveFileMetadata(type, json){
}
export function fetchFileMetadata(id) {
- let url = `/api/files/${id}/metadata`;
+ const url = `/api/files/${id}/metadata`;
return (dispatch) => {
return fetch(url, {mode: "cors"})
- .then((response) => {
- if (response.status === 200) {
- response.json().then(json => {
- dispatch(receiveFileMetadata(RECEIVE_FILE_METADATA, json));
- });
- } else {
- dispatch(receiveFileMetadata(RECEIVE_FILE_METADATA, []));
- }
- });
+ .then((response) => {
+ if (response.status === 200) {
+ response.json().then(json => {
+ dispatch(receiveFileMetadata(RECEIVE_FILE_METADATA, json));
+ });
+ } else {
+ dispatch(receiveFileMetadata(RECEIVE_FILE_METADATA, []));
+ }
+ });
};
}
@@ -44,18 +43,18 @@ export function receiveFileExtractedMetadata(type, json) {
}
export function fetchFileExtractedMetadata(id) {
- let url = `/api/files/${id}/extracted_metadata`;
+ const url = `/api/files/${id}/extracted_metadata`;
return (dispatch) => {
return fetch(url, {mode: "cors"})
- .then((response) => {
- if (response.status === 200) {
- response.json().then(json => {
- dispatch(receiveFileExtractedMetadata(RECEIVE_FILE_EXTRACTED_METADATA, json));
- });
- } else {
- dispatch(receiveFileExtractedMetadata(RECEIVE_FILE_EXTRACTED_METADATA, []));
- }
- });
+ .then((response) => {
+ if (response.status === 200) {
+ response.json().then(json => {
+ dispatch(receiveFileExtractedMetadata(RECEIVE_FILE_EXTRACTED_METADATA, json));
+ });
+ } else {
+ dispatch(receiveFileExtractedMetadata(RECEIVE_FILE_EXTRACTED_METADATA, []));
+ }
+ });
};
}
@@ -72,18 +71,18 @@ export function receiveFileMetadataJsonld(type, json) {
}
export function fetchFileMetadataJsonld(id) {
- let url = `/api/files/${id}/metadata.jsonld`;
+ const url = `/api/files/${id}/metadata.jsonld`;
return (dispatch) => {
return fetch(url, {mode: "cors"})
- .then((response) => {
- if (response.status === 200) {
- response.json().then(json => {
- dispatch(receiveFileMetadataJsonld(RECEIVE_FILE_METADATA_JSONLD, json));
- });
- } else {
- dispatch(receiveFileMetadataJsonld(RECEIVE_FILE_METADATA_JSONLD, []));
- }
- });
+ .then((response) => {
+ if (response.status === 200) {
+ response.json().then(json => {
+ dispatch(receiveFileMetadataJsonld(RECEIVE_FILE_METADATA_JSONLD, json));
+ });
+ } else {
+ dispatch(receiveFileMetadataJsonld(RECEIVE_FILE_METADATA_JSONLD, []));
+ }
+ });
};
}
@@ -111,7 +110,7 @@ export function receiveFilePreviews(type, json) {
export function fetchFilePreviews(id) {
return async function fetchFilePreviewsThunk(dispatch) {
const previews_list = await getPreviewsRequest(id) // list of previews
- console.log("preview", previews_list);
+ // console.log("preview", previews_list);
// [{"file_id": "63e6a5dfe4b034120ec4f035", "previews": [{"pv_route":"/clowder/files/63e6a5dfe4b034120ec4f035/blob","p_main":"html-iframe.js","pv_id":"63e6a5dfe4b034120ec4f035","p_path":"/clowder/assets/javascripts/previewers/html","p_id":"HTML","pv_length":"21348","pv_contenttype":"text/html"}]}]
// [{p_id: "PDF", p_main: "some-library.js", p_path: "/assets/javascripts/previewers/pdf", pv_contenttype: "application/pdf", pv_id: "67057fb9e4b00da0e4ef9937", pv_length: "2324500", pv_route: "/files/67057fb9e4b00da0e4ef9937/blob"}]
if (previews_list !== undefined && previews_list !== null) {
@@ -119,35 +118,35 @@ export function fetchFilePreviews(id) {
} else {
dispatch(receiveFilePreviews(RECEIVE_PREVIEWS, []));
}
- }
+ };
}
export const DELETE_FILE = "DELETE_FILE";
export function deleteFile(fileId) {
- let url = `/api/files/${fileId}`;
+ const url = `/api/files/${fileId}`;
return (dispatch) => {
return fetch(url, {mode: "cors", method: "DELETE"})
- .then((response) => {
- if (response.status === 200) {
- response.json().then(json => {
- dispatch({
- type: DELETE_FILE,
- file: {"id": fileId, "status": json["status"] === undefined ? json["status"] : "success"},
- receivedAt: Date.now(),
+ .then((response) => {
+ if (response.status === 200) {
+ response.json().then(json => {
+ dispatch({
+ type: DELETE_FILE,
+ file: {"id": fileId, "status": json["status"] === undefined ? json["status"] : "success"},
+ receivedAt: Date.now(),
+ });
});
- });
- } else {
- response.json().then(json => {
- dispatch({
- type: DELETE_FILE,
- file: {"id": null, "status": json["status"] === undefined ? json["status"] : "fail"},
- receivedAt: Date.now(),
+ } else {
+ response.json().then(json => {
+ dispatch({
+ type: DELETE_FILE,
+ file: {"id": null, "status": json["status"] === undefined ? json["status"] : "fail"},
+ receivedAt: Date.now(),
+ });
});
- });
- }
- });
+ }
+ });
};
}
diff --git a/src/app.config.js b/src/app.config.js
index 10943a8..5320aa5 100644
--- a/src/app.config.js
+++ b/src/app.config.js
@@ -1,4 +1,4 @@
-let config = {};
+const config = {};
// CLOWDER_REMOTE_HOSTNAME and APIKEY are no longer needed on the client
// All API calls are proxied through the Express server
diff --git a/src/components/Dashboard.js b/src/components/Dashboard.js
index d76b0d0..e6ac639 100644
--- a/src/components/Dashboard.js
+++ b/src/components/Dashboard.js
@@ -1,11 +1,9 @@
-import React, {useEffect, useState, Component} from "react";
-import {Link as RouterLink} from "react-router-dom";
-import {AppBar, Box, Button, Dialog, DialogTitle, Grid, Link, ListItem, Tab, Tabs, Typography} from "@material-ui/core";
+import React, {useEffect} from "react";
+import {Box, Grid} from "@material-ui/core";
import TopBar from "./childComponents/TopBar";
import Intro from "./childComponents/Intro";
import CreateAndUpload from "./childComponents/CreateAndUpload";
import Footer from "./childComponents/Footer";
-import { useTheme } from "@material-ui/core/styles";
import { useDispatch } from "react-redux";
import { resetFileToDefault } from "../actions/file";
import { resetDatasetToDefault } from "../actions/dataset";
@@ -13,7 +11,6 @@ import { resetPdfPreviewToDefault } from "../actions/pdfpreview";
import { resetStatementToDefault, resetUserCategoryToDefault } from "../actions/dashboard";
function Dashboard() {
- const theme = useTheme();
const dispatch = useDispatch();
// Clear all Redux states when Dashboard component mounts
@@ -27,11 +24,11 @@ function Dashboard() {
}, [dispatch]);
return (
-
+
-
+
diff --git a/src/components/Preview.js b/src/components/Preview.js
index 1af04b9..dbe8b4a 100644
--- a/src/components/Preview.js
+++ b/src/components/Preview.js
@@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react";
-import {Box, Button, Grid, ListItem, Typography} from "@material-ui/core";
+import {Box, Typography} from "@material-ui/core";
import FilePreview from "./childComponents/FilePreview";
import TopBar from "./childComponents/TopBar";
@@ -9,14 +9,14 @@ function Preview() {
useEffect(() => {
const checkAuthStatus = async () => {
try {
- const response = await fetch('/isAuthenticated', {
- method: 'GET',
- credentials: 'include',
+ const response = await fetch("/isAuthenticated", {
+ method: "GET",
+ credentials: "include",
});
const data = await response.json();
setIsAuthenticated(data.isAuthenticated);
} catch (error) {
- console.error('Error checking authentication status:', error);
+ // console.error("Error checking authentication status:", error);
}
};
checkAuthStatus();
diff --git a/src/components/childComponents/CreateAndUpload.js b/src/components/childComponents/CreateAndUpload.js
index 19a8374..fc774a3 100644
--- a/src/components/childComponents/CreateAndUpload.js
+++ b/src/components/childComponents/CreateAndUpload.js
@@ -1,26 +1,25 @@
// Create a dataset and upload a file. Submit for extraction and get file previews
-import React, {useEffect, useState, useCallback, useRef} from 'react';
+import {useEffect, useState, useCallback, useRef} from "react";
import { useNavigate } from "react-router-dom";
import {useDispatch, useSelector} from "react-redux";
import LoadingOverlay from "react-loading-overlay-ts";
import {Box, Button, Typography} from "@material-ui/core";
-import Radio from '@mui/material/Radio';
-import RadioGroup from '@mui/material/RadioGroup';
-import FormControlLabel from '@mui/material/FormControlLabel';
+import Radio from "@mui/material/Radio";
+import FormControlLabel from "@mui/material/FormControlLabel";
import { theme } from "../../theme";
import Dropfile from "./Dropfile";
import {createUploadExtract} from "../../actions/client";
import {getDatasetMetadata, getFileInDataset} from "../../utils/dataset";
import {downloadAndSaveFile} from "../../utils/file";
-import {fetchFilePreviews, SET_EXTRACTION_STATUS, setExtractionStatus } from "../../actions/file";
+import {fetchFilePreviews, setExtractionStatus } from "../../actions/file";
import {SET_DATASET_METADATA, setDatasetMetadata} from "../../actions/dataset";
-import {SET_STATEMENT_TYPE, setStatement, SET_USER_CATEGORY, setUserCategory, resetStatementToDefault, resetUserCategoryToDefault, checkAuthenticationStatus} from '../../actions/dashboard';
+import {SET_STATEMENT_TYPE, setStatement, SET_USER_CATEGORY, setUserCategory, resetStatementToDefault, resetUserCategoryToDefault, checkAuthenticationStatus} from "../../actions/dashboard";
import config from "../../app.config";
-import {resetFileToDefault} from '../../actions/file';
-import {resetDatasetToDefault} from '../../actions/dataset';
-import {resetPdfPreviewToDefault} from '../../actions/pdfpreview';
+import {resetFileToDefault} from "../../actions/file";
+import {resetDatasetToDefault} from "../../actions/dataset";
+import {resetPdfPreviewToDefault} from "../../actions/pdfpreview";
export default function CreateAndUpload() {
@@ -30,34 +29,26 @@ export default function CreateAndUpload() {
const [mouseHover, setMouseHover] = useState(false); // mouse hover state for dropzone
const [loading, setLoading] = useState(false); // loading overlay state and button disabled state. set to active when dropfile is active
const [loading_text, setLoadingText] = useState("Processing"); // loading overlay text.
- const [filename, setFilename] = useState(''); // uploaded filename
+ const [filename, setFilename] = useState(""); // uploaded filename
const [spinner, setSpinner] = useState(true); // loading overlay spinner active
const [preview, setPreview] = useState(true); // disabled button state for file preview button
const datasets = useSelector((state) => state.dataset.datasets);
- const filesInDataset = useSelector(state => state.dataset.files);
const extractionStatus = useSelector(state => state.file.extractionStatus);
- const listFilePreviews = (fileId) => dispatch(fetchFilePreviews(fileId));
- const datasetMetadata = (json) => dispatch(setDatasetMetadata(SET_DATASET_METADATA, json));
const statementType = useSelector(state => state.statement.statementType);
const userCategory = useSelector(state => state.userCategory.userCategory);
const datasetStatus = useSelector(state => state.dataset.status);
const isAuthenticated = useSelector(state => state.authentication.isAuthenticated);
- const authenticationLoading = useSelector(state => state.authentication.authenticationLoading);
const [RCTmetadata, setRCTMetadata] = useState({}); // state for RCT metadata
const [PDFmetadata, setPDFMetadata] = useState({}); // state for PDF metadata
- let pdfExtractor;
+ const pdfExtractor = config.pdf_extractor;
const rctExtractor = config.rct_extractor;
- if (userCategory === "author"){
- pdfExtractor = config.pymupdf_extractor;
- }
- else{
- pdfExtractor = config.pdf_extractor;
- }
// Reference to track any active timeouts
const timeoutsRef = useRef([]);
+ // Reference to track if extraction has already completed for current file
+ const extractionCompletedRef = useRef(false);
// Check authentication status on mount using Redux
useEffect(() => {
@@ -66,7 +57,6 @@ export default function CreateAndUpload() {
// Update config when authentication status changes
useEffect(() => {
- console.log("CreateAndUpload isAuthenticated", isAuthenticated);
config.userCategory = isAuthenticated ? "researcher" : "author";
dispatch(setUserCategory(SET_USER_CATEGORY, isAuthenticated ? "researcher" : "author"));
}, [isAuthenticated]);
@@ -81,8 +71,8 @@ export default function CreateAndUpload() {
const onDropFile = (file) => {
// Reset previous extraction state and previews
dispatch(setExtractionStatus(null));
- dispatch({type: 'RESET_FILE_PREVIEWS'});
- dispatch({type: 'RESET_DATASET_METADATA'});
+ dispatch({type: "RESET_FILE_PREVIEWS"});
+ dispatch({type: "RESET_DATASET_METADATA"});
setLoadingText("Uploading file");
setLoading(true);
@@ -107,18 +97,21 @@ export default function CreateAndUpload() {
setRCTMetadata({});
setPDFMetadata({});
+ // Reset extraction completed flag for new upload
+ extractionCompletedRef.current = false;
+
// Clear any pending timeouts
timeoutsRef.current.forEach(timeoutId => clearTimeout(timeoutId));
timeoutsRef.current = [];
try {
acceptedFiles.map(file => {
- onDropFile(file)
- })
- rejectedFiles.map(file => {
+ onDropFile(file);
+ });
+ rejectedFiles.map(() => {
dispatch(setExtractionStatus("File rejected"));
setSpinner(false);
- })
+ });
}
catch(error) {
dispatch(setExtractionStatus("Upload failed"));
@@ -128,67 +121,71 @@ export default function CreateAndUpload() {
// useEffect on extractionStatus for preview generation
- useEffect(async () => {
+ useEffect(() => {
// Skip processing if we're in a state cleanup situation
- if (filename === '') {
+ if (filename === "") {
return;
}
- if (extractionStatus !== null && datasetStatus !== "completed") {
+ // Always update loading text when extractionStatus changes (before ref check)
+ if (extractionStatus !== null) {
setLoadingText(extractionStatus);
+ }
+
+ // Skip if extraction already completed for this file
+ if (extractionCompletedRef.current) {
+ return;
+ }
+
+ if (extractionStatus !== null && datasetStatus !== "completed") {
const file_name = filename.replace(/\.[^/.]+$/, ""); // get filename without extension;
// Make sure datasets exist before proceeding
if (!datasets || datasets.length === 0) {
- console.log("No datasets available");
return;
}
const dataset_id = datasets[0].id;
- let highlights_filename;
+ const highlights_filename = `${file_name}_highlights.json`;
// check extraction status and highlights file generation in loop
const highlights_file_loop = async () => {
- if (pdfExtractor === config.pymupdf_extractor){
- highlights_filename = file_name + "-pymupdf" + '_highlights' + '.json'
- }
- else{
- highlights_filename = file_name + '_highlights' + '.json'
+ // Check again if already completed to prevent race conditions
+ if (extractionCompletedRef.current) {
+ return;
}
const highlightsFile = await getFileInDataset(dataset_id, "application/json", highlights_filename);
if (highlightsFile !== null && typeof highlightsFile.id === "string") {
+ // Mark extraction as completed to prevent duplicate processing
+ extractionCompletedRef.current = true;
+
// {"id":string, "size":string, "date-created":string, "contentType":text/html, "filename":string}
const metadata = await getDatasetMetadata(dataset_id);
- console.log("metadata", metadata);
// get the metadata content list
const contentList = metadata.map(item => item.content);
- console.log("metadata content list", contentList);
const pdfExtractorContent = contentList.find(item => item.extractor === pdfExtractor);
const rctExtractorContent = contentList.find(item => item.extractor === rctExtractor);
- if (pdfExtractorContent){
- setPDFMetadata(pdfExtractorContent);
- }
if (rctExtractorContent){
setRCTMetadata(rctExtractorContent);
}
if (pdfExtractorContent){
+ setPDFMetadata(pdfExtractorContent);
// get pdf preview
- console.log("pdf extractor preview ", pdfExtractorContent)
- const pdf_extractor_extracted_files = pdfExtractorContent["extracted_files"]
- const pdf_input_file = pdf_extractor_extracted_files[0]["file_id"]
- console.log("listFilePreviews", pdf_input_file)
- listFilePreviews(pdf_input_file);
+ const pdf_extractor_extracted_files = pdfExtractorContent["extracted_files"];
+ const pdf_input_file = pdf_extractor_extracted_files[0]["file_id"];
+ console.log("pdfExtractorContent", pdfExtractorContent);
+ console.log("pdf_extractor_extracted_files", pdf_extractor_extracted_files);
+ dispatch(fetchFilePreviews(pdf_input_file));
}
else{
- listFilePreviews(highlightsFile.id);
+ dispatch(fetchFilePreviews(highlightsFile.id));
}
- datasetMetadata(metadata);
+ dispatch(setDatasetMetadata(SET_DATASET_METADATA, metadata));
setPreview(false); // View Results button activated
setSpinner(false); // stop display of spinner
} else {
- console.log("check highlights file after 5s");
const timeoutId = setTimeout(highlights_file_loop, 5000);
timeoutsRef.current.push(timeoutId);
}
@@ -214,16 +211,14 @@ export default function CreateAndUpload() {
await highlights_file_loop();
};
- await timeoutCheckedLoop(); // Start the loop with timeout checking
- } else {
- console.error("Dataset does not exist");
+ timeoutCheckedLoop(); // Start the loop with timeout checking
}
}
else if (extractionStatus === false){
dispatch(setExtractionStatus("Error in extraction"));
setSpinner(false); // stop display of spinner
}
- }, [extractionStatus, datasetStatus]);
+ }, [extractionStatus, datasetStatus, filename, datasets, dispatch, pdfExtractor, rctExtractor]);
// Watch for dataset status changes
useEffect(() => {
@@ -238,15 +233,14 @@ export default function CreateAndUpload() {
setLoading(false); // stop display of overlay
setSpinner(false);
if (userCategory === "author"){
- const reportFileID = RCTmetadata["extracted_files"][1]["file_id"]
- const reportFilename = RCTmetadata["extracted_files"][1]["filename"]
- downloadAndSaveFile(reportFileID, reportFilename).then(r => {
- console.log(r);
+ const reportFileID = RCTmetadata["extracted_files"][1]["file_id"];
+ const reportFilename = RCTmetadata["extracted_files"][1]["filename"];
+ downloadAndSaveFile(reportFileID, reportFilename).then(() => {
// Clear all states
setLoading(false);
setSpinner(false);
setLoadingText("Processing");
- setFilename('');
+ setFilename("");
setPreview(true);
setRCTMetadata({});
setPDFMetadata({});
@@ -259,10 +253,10 @@ export default function CreateAndUpload() {
});
}
else{
- let path = '/preview';
+ const path = "/preview";
navigate(path);
}
- }
+ };
// Add timeout cleanup to prevent memory leaks when component unmounts
useEffect(() => {
@@ -275,71 +269,71 @@ export default function CreateAndUpload() {
// We pass onDrop function and accept prop to the component. It will be used as initial params for useDropzone hook
return (
-
-
-
+
- Select Guideline
-
- }
- label="SPIRIT"
- style={{ fontFamily: theme.typography.fontFamily, margin: 0 }}
- disabled={loading}
- />
- }
- label="CONSORT"
- style={{ fontFamily: theme.typography.fontFamily, margin: 0 }}
- disabled={loading}
- />
-
- ({
- ...base,
- background: 'rgba(163, 90, 244, 1)'
- })
- }}>
- setMouseHover(true)} style={{ marginTop: '1rem' }}>
-
+ Select Guideline
+
+ }
+ label="SPIRIT"
+ style={{ fontFamily: theme.typography.fontFamily, margin: 0 }}
+ disabled={loading}
+ />
+ }
+ label="CONSORT"
+ style={{ fontFamily: theme.typography.fontFamily, margin: 0 }}
+ disabled={loading}
/>
-
-
-
-
+ ({
+ ...base,
+ background: "rgba(163, 90, 244, 1)"
+ })
+ }}>
+ setMouseHover(true)} style={{ marginTop: "1rem" }}>
+
+
+
+
+
+
diff --git a/src/components/childComponents/FilePreview.js b/src/components/childComponents/FilePreview.js
index 2e852e8..3dc0088 100644
--- a/src/components/childComponents/FilePreview.js
+++ b/src/components/childComponents/FilePreview.js
@@ -1,74 +1,101 @@
// Display file previews
-import React, {useEffect, useState, useCallback} from 'react';
-import {useDispatch, useSelector} from "react-redux";
-import {Box, Button, Grid} from "@material-ui/core";
+import {useEffect, useState, useRef} from "react";
+import {useSelector} from "react-redux";
+import {Box} from "@material-ui/core";
import Pdf from "../previewers/Pdf";
import Html from "../previewers/Html";
import Audio from "../previewers/Audio";
import Video from "../previewers/Video";
-import Thumbnail from "../previewers/Thumbnail";
import {getPreviewResources} from "../../utils/file";
import PreviewDrawerLeft from "./PreviewDrawerLeft";
-import Intro from "./Intro";
-import CreateAndUpload from "./CreateAndUpload";
import config from "../../app.config";
export default function FilePreview() {
const pdfExtractor = config.pdf_extractor;
const rctExtractor = config.rct_extractor;
- const dispatch = useDispatch();
const filePreviews = useSelector((state) => state.file.previews);
const [previews, setPreviews] = useState([]); // state for file previews
const datasetMetadata = useSelector((state) => state.dataset.metadata);
const [RCTmetadata, setRCTMetadata] = useState({}); // state for RCT metadata
- const [PDFmetadata, setPDFMetadata] = useState({}); // state for PDF metadata
+
+ // Track the last processed file ID to prevent duplicate processing
+ const lastProcessedFileId = useRef(null);
// We don't want to clear states here as they're needed for preview
// useEffect on filePreviews to download preview resources
- useEffect( async ()=> {
- // Reset the local previews state when filePreviews changes
- setPreviews([]);
+ useEffect(() => {
+ // Flag to track if the effect is still active (for cleanup)
+ let isActive = true;
- if (filePreviews !== undefined && filePreviews.length > 0) {
- const previewsTemp = [];
- // get either pdf preview / html preview
- if (filePreviews.length > 0){
- console.log("filePreviews:", filePreviews);
- const fileId = filePreviews[0][0].file_id;
- const previewsList = filePreviews[0][0].previews;
- previewsList.map(async (preview) => {
- const preview_config = await getPreviewResources(fileId, preview);
- previewsTemp.push(preview_config);
- setPreviews(previewsTemp); // set previews
- });
+ const loadPreviews = async () => {
+ if (filePreviews === undefined || filePreviews.length === 0) {
+ return;
}
- else {
- console.log("Multiple file previews found ", filePreviews)
+
+ // Check if we have valid preview data
+ if (!filePreviews[0] || !filePreviews[0][0]) {
+ return;
}
-
- }
+
+ const fileId = filePreviews[0][0].file_id;
+ const previewsList = filePreviews[0][0].previews;
+
+ // Skip if we've already processed this file ID
+ if (lastProcessedFileId.current === fileId) {
+ return;
+ }
+
+ // Mark this file ID as being processed
+ lastProcessedFileId.current = fileId;
+
+ // Reset the local previews state for new file
+ setPreviews([]);
+
+ // Process all previews and collect results
+ const previewPromises = previewsList.map((preview) =>
+ getPreviewResources(fileId, preview)
+ );
+
+ try {
+ const previewConfigs = await Promise.all(previewPromises);
+
+ // Only update state if the effect is still active
+ if (isActive) {
+ setPreviews(previewConfigs);
+ }
+ } catch (error) {
+ console.error("Error loading preview resources:", error);
+ }
+ };
+
+ loadPreviews();
+
+ // Cleanup function to prevent state updates on unmounted component
+ return () => {
+ isActive = false;
+ };
}, [filePreviews]);
// useEffect on datasetMetadata to load preview leftdrawer metadata
- useEffect( async ()=> {
+ useEffect(() => {
if (datasetMetadata !== undefined && Array.isArray(datasetMetadata)) {
const contentList = datasetMetadata.map(item => item.content);
const pdfExtractorContent = contentList.find(item => item.extractor === pdfExtractor);
const rctExtractorContent = contentList.find(item => item.extractor === rctExtractor);
if (pdfExtractorContent){
- setPDFMetadata(pdfExtractorContent);
+ // setPDFMetadata(pdfExtractorContent);
}
if (rctExtractorContent){
setRCTMetadata(rctExtractorContent);
}
}
- console.log("datasetMetadata ", datasetMetadata);
- }, [datasetMetadata])
+ // console.log("datasetMetadata ", datasetMetadata);
+ }, [datasetMetadata, pdfExtractor, rctExtractor]);
return (
@@ -97,24 +124,24 @@ export default function FilePreview() {
//
// );
} else if (preview["previewType"] === "pdf" || preview["previewType"] === "thumbnail") {
- console.log("previewType is ", preview["previewType"]);
+ // console.log("previewType is ", preview["previewType"]);
return (
-
+
{/* Drawer takes its fixed width */}
{/* Main content area for PDF, allows it to grow and centers the PDF viewer */}
-
+
);
} else if (preview["previewType"] === "html") {
return (
-
+
{/* Drawer takes its fixed width */}
{/* Main content area for HTML, allows it to grow */}
-
+
diff --git a/src/components/childComponents/PreviewDrawerLeft.js b/src/components/childComponents/PreviewDrawerLeft.js
index 386f130..5a1ef73 100644
--- a/src/components/childComponents/PreviewDrawerLeft.js
+++ b/src/components/childComponents/PreviewDrawerLeft.js
@@ -1,23 +1,21 @@
// Preview LeftDrawer Component
-import React, {useEffect, useState} from 'react';
+import {useEffect, useState} from "react";
import {useDispatch, useSelector} from "react-redux";
import {Box, Button, Typography} from "@material-ui/core";
-import Drawer from '@mui/material/Drawer';
-import Toolbar from '@mui/material/Toolbar';
-import Divider from '@mui/material/Divider';
+import Drawer from "@mui/material/Drawer";
import {Badge, List, ListItemIcon} from "@mui/material";
-import ListItem from '@mui/material/ListItem';
-import ListItemButton from '@mui/material/ListItemButton';
-import ListSubheader from '@mui/material/ListSubheader';
-import ListItemText from '@mui/material/ListItemText';
-import Collapse from '@mui/material/Collapse';
-import DownloadIcon from '@mui/icons-material/Download';
-import ExpandLess from '@mui/icons-material/ExpandLess';
-import ExpandMore from '@mui/icons-material/ExpandMore';
-import CancelIcon from '@mui/icons-material/Cancel';
-import CheckIcon from '@mui/icons-material/Check';
-import ToggleButton from '@mui/material/ToggleButton';
-import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
+import ListItem from "@mui/material/ListItem";
+import ListItemButton from "@mui/material/ListItemButton";
+import ListSubheader from "@mui/material/ListSubheader";
+import ListItemText from "@mui/material/ListItemText";
+import Collapse from "@mui/material/Collapse";
+import DownloadIcon from "@mui/icons-material/Download";
+import ExpandLess from "@mui/icons-material/ExpandLess";
+import ExpandMore from "@mui/icons-material/ExpandMore";
+import CancelIcon from "@mui/icons-material/Cancel";
+import CheckIcon from "@mui/icons-material/Check";
+import ToggleButton from "@mui/material/ToggleButton";
+import ToggleButtonGroup from "@mui/material/ToggleButtonGroup";
import { theme } from '../../theme';
import {downloadAndSaveFile} from "../../utils/file";
@@ -31,8 +29,8 @@ export default function PreviewDrawerLeft(props) {
const statementType = useSelector((state) => state.statement.statementType);
const statementString = statementType.toUpperCase();
- const {fileId, fileSrc, metadata, ...other} = props;
- const [extractor, setExtractor] = useState('');
+ const {metadata} = props;
+ const [extractor, setExtractor] = useState("");
const [content, setContent] = useState({});
const [itemsMissed, setItemsMissed] = useState('');
const [checklist, setChecklist] = useState([]);
@@ -44,9 +42,9 @@ export default function PreviewDrawerLeft(props) {
function getCoordsPages(sentences){
- let pageNumbers = new Set();
+ const pageNumbers = new Set();
sentences.forEach(item => {
- const coordsArray = item.coords.split(';');
+ const coordsArray = item.coords.split(";");
coordsArray.forEach(coord => {
const coordValues = coord.split(',');
pageNumbers.add(coordValues[0]);
@@ -57,7 +55,7 @@ export default function PreviewDrawerLeft(props) {
function get_item_found_pages(checklist){
if (checklist.length < 1){
- console.error("No checklist");
+ // console.error("No checklist");
return null
}
else {
@@ -73,7 +71,7 @@ export default function PreviewDrawerLeft(props) {
}
});
});
- console.log("Items found pages", result);
+ // console.log("Items found pages", result);
return result;
@@ -92,7 +90,7 @@ export default function PreviewDrawerLeft(props) {
const handleItemClick = (pagenum) =>{
pageNumber(pagenum);
- console.log("Go to ", pagenum);
+ // console.log("Go to ", pagenum);
};
const isOpen = (name) => {
@@ -106,13 +104,15 @@ export default function PreviewDrawerLeft(props) {
}
const onDownload = () => {
- downloadAndSaveFile(reportFileID, reportFilename).then(r => console.log(r));
+ downloadAndSaveFile(reportFileID, reportFilename).then(r => {
+ // console.log(r);
+ });
}
useEffect(() => {
if (metadata !== undefined){
- let content = metadata;
+ const content = metadata;
setContent(content);
setExtractor(content["extractor"]);
setItemsMissed(content["items_missed"]);
@@ -122,7 +122,7 @@ export default function PreviewDrawerLeft(props) {
setItemFoundPages(get_item_found_pages(content["checklist"]))
}
if (metadata === undefined){
- console.log("Error metadata undefined");
+ // console.log("Error metadata undefined");
}
},[]);
@@ -184,6 +184,7 @@ export default function PreviewDrawerLeft(props) {
component="div"
id="item-checklist-subheader"
sx={{
+ position: 'static',
backgroundColor: 'transparent',
fontWeight: 'bold',
fontSize: '1rem',
diff --git a/src/components/childComponents/TopBar.jsx b/src/components/childComponents/TopBar.jsx
index 214331c..7d6e74d 100644
--- a/src/components/childComponents/TopBar.jsx
+++ b/src/components/childComponents/TopBar.jsx
@@ -1,7 +1,7 @@
-import React, {useEffect, useState} from 'react';
-import {makeStyles} from '@material-ui/core/styles';
-import {AppBar, Link, Toolbar, Typography, Button, Box} from '@material-ui/core';
-import { Link as RouterLink } from 'react-router-dom';
+import {useEffect, useState} from "react";
+import {makeStyles} from "@material-ui/core/styles";
+import {AppBar, Link, Toolbar, Typography, Button, Box} from "@material-ui/core";
+import { Link as RouterLink } from "react-router-dom";
import {useDispatch, useSelector} from "react-redux";
import {checkAuthenticationStatus} from '../../actions/dashboard';
import { theme } from '../../theme';
@@ -27,9 +27,9 @@ const useStyles = makeStyles((theme) => ({
},
toolBarItem: {
margin: "auto 12px auto 12px",
- display: 'flex',
- alignItems: 'center',
- visibility: 'visible',
+ display: "flex",
+ alignItems: "center",
+ visibility: "visible",
opacity: 1
},
toolBarlink: {
@@ -97,8 +97,6 @@ export default function TopBar() {
// Use Redux authentication state
const isAuthenticated = useSelector(state => state.authentication.isAuthenticated);
- const authenticationLoading = useSelector(state => state.authentication.authenticationLoading);
-
const [username, setUsername] = useState("anonymous");
useEffect(() => {
@@ -107,18 +105,18 @@ export default function TopBar() {
const getUsername = async () => {
try {
- console.log('Fetching username...');
- const response = await fetch('/getUser', {
- method: 'GET',
- credentials: 'include',
+ // console.log("Fetching username...");
+ const response = await fetch("/getUser", {
+ method: "GET",
+ credentials: "include",
});
const data = await response.json();
setUsername(data.username);
- console.log('Username set to:', data.username);
+ // console.log("Username set to:", data.username);
} catch (error) {
- console.error('Error fetching username:', error);
- setUsername('anonymous');
- console.log('Username set to: ', username);
+ // console.error("Error fetching username:", error);
+ setUsername("anonymous");
+ // console.log("Username set to: ", username);
}
}
getUsername();
@@ -152,19 +150,19 @@ export default function TopBar() {
- {username !== 'anonymous' && (
+ {username !== "anonymous" && (
Welcome, {username}!
@@ -172,7 +170,7 @@ export default function TopBar() {
)}
-
+
Contact Us
diff --git a/src/components/previewers/Audio.jsx b/src/components/previewers/Audio.jsx
index 19b052a..b43b88e 100644
--- a/src/components/previewers/Audio.jsx
+++ b/src/components/previewers/Audio.jsx
@@ -1,6 +1,4 @@
-import React from "react";
-
export default function Audio(props) {
- const {fileId, audioSrc, ...other} = props;
- return
+ const {fileId, audioSrc} = props;
+ return ;
}
diff --git a/src/components/previewers/Html.jsx b/src/components/previewers/Html.jsx
index 92190bc..acf93e0 100644
--- a/src/components/previewers/Html.jsx
+++ b/src/components/previewers/Html.jsx
@@ -1,7 +1,4 @@
-import React from "react";
-import Link from "@material-ui/core/Link";
-
export default function Html(props) {
- const {fileId, htmlSrc, ...other} = props;
- return
+ const {fileId, htmlSrc} = props;
+ return ;
}
diff --git a/src/components/previewers/Pdf.js b/src/components/previewers/Pdf.js
index 0a7f747..11a0aa1 100644
--- a/src/components/previewers/Pdf.js
+++ b/src/components/previewers/Pdf.js
@@ -1,7 +1,7 @@
// Preview Pdf file using react-pdf package
-import React, { useEffect, useRef, useState } from "react";
+import { useEffect, useRef, useState } from "react";
import {useDispatch, useSelector} from "react-redux";
-import { pdfjs , Document, Page } from 'react-pdf';
+import { pdfjs , Document, Page } from "react-pdf";
import "react-pdf/dist/esm/Page/TextLayer.css";
import {SET_PAGE_NUMBER, setPageNumber} from "../../actions/pdfpreview";
import {
@@ -17,16 +17,15 @@ pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/$
export default function Pdf(props) {
const dispatch = useDispatch();
- const {fileId, pdfSrc, metadata, ...other} = props;
+ const {pdfSrc, metadata} = props;
const [content, setContent] = useState({});
// all sentences from metadata
const [allSentences, setAllSentences] = useState([]); // [{label: "label", sentences: [{coords: "coords", text: "text"}]}]
const highlightCanvasRef = useRef();
- const [isRendered, setIsRendered] = useState();
const [numPages, setNumPages] = useState(null);
- let pageNumber = useSelector((state) => state.pdfpreview.pageNumber);
+ const pageNumber = useSelector((state) => state.pdfpreview.pageNumber);
const dispatchPageNumber = (number) => dispatch(setPageNumber(SET_PAGE_NUMBER, number));
const statementType = useSelector(state => state.statement.statementType);
@@ -34,10 +33,10 @@ export default function Pdf(props) {
const marginWidth = 75; // adjust margin width
const [pageWidth, setPageWidth] = useState(500);
const [pageHeight, setPageHeight]= useState(799);
- let [scale_x, setScaleX] = useState(1); // recalculated dynamically in renderHighlights
- let [scale_y, setScaleY] = useState(1); // recalculated dynamically in renderHighlights
- let pdf_render_scale = 1.5;
- let canvas_render_scale = 1.5; // keep same as pdf_render_scale for coordinate highlighting
+ const [isRendered, setIsRendered] = useState(false);
+ const [scale_x, setScaleX] = useState(1);
+ const [scale_y, setScaleY] = useState(1);
+ const pdf_render_scale = 1.5;
// Cleanup effect when component unmounts
useEffect(() => {
@@ -75,7 +74,7 @@ export default function Pdf(props) {
setAllSentences(sentences_list);
}
if (metadata === undefined){
- console.error("Error metadata undefined");
+ // console.error("Error metadata undefined");
const sentences_list = []
setAllSentences(sentences_list);
}
@@ -250,12 +249,12 @@ export default function Pdf(props) {
// Use the dedicated highlight canvas
const canvas = highlightCanvasRef.current;
if (!canvas) {
- console.error("canvas.current is empty");
+ // console.error("canvas.current is empty");
return;
}
// Ensure we have page dimensions
if (!pageWidth || !pageHeight) {
- console.error("Page dimensions not set.");
+ // console.error("Page dimensions not set.");
return;
}
@@ -281,13 +280,12 @@ export default function Pdf(props) {
// // Scale the canvas to 2x while keeping PDF at 1.5x
// let scale_x = (canvas_height / pageHeight) * (canvas_render_scale/pdf_render_scale);
// let scale_y = (canvas_width / pageWidth) * (canvas_render_scale/pdf_render_scale);
- const scale_factor = pdf_render_scale; // Use the same scale as the PDF render
- let scale_x = (scaledPdfWidth / pageWidth); // Match PDF render scale
- let scale_y = (scaledPdfHeight / pageHeight);
+ const scale_x = pdf_render_scale; // Match PDF render scale
+ const scale_y = pdf_render_scale;
const offset_x = marginWidth;
const pageHighlights = getPageHighlights();
- console.log("pageHighlights:", pageHighlights); // Add logging
+ // console.log("pageHighlights:", pageHighlights); // Add logging
// --- Collision Detection Setup ---
const drawnLabelRects = []; // Stores { x, y, width, height } of drawn labels
@@ -318,22 +316,22 @@ export default function Pdf(props) {
// Skip if there are no labels for this coordGroup (shouldn't happen with new logic, but safe check)
if (labels.length === 0) {
- console.warn("Skipping coordGroup with empty labels array:", coordGroups);
+ // console.warn("Skipping coordGroup with empty labels array:", coordGroups);
return;
}
// put the label only for the first coordGroup for the sentence
const firstCoordGroup = coordGroups[0];
- const parts = firstCoordGroup.split(',');
+ const parts = firstCoordGroup.split(",");
if (parts.length < 5) {
- console.warn("Skipping invalid coordGroup:", firstCoordGroup);
+ // console.warn("Skipping invalid coordGroup:", firstCoordGroup);
return; // Skip this item if coordGroup is malformed
}
const [label_page, label_x, label_y, label_width, label_height] = parts.map(Number);
// Check if parsing resulted in valid numbers
if ([label_page, label_x, label_y, label_width, label_height].some(isNaN)) {
- console.warn("Skipping coordGroup with NaN values:", firstCoordGroup);
+ // console.warn("Skipping coordGroup with NaN values:", firstCoordGroup);
return; // Skip if any part is not a number
}
@@ -344,7 +342,7 @@ export default function Pdf(props) {
let drawHeight = label_height * scale_y;
// --- Label Positioning and Collision Avoidance ---
- const combinedLabelText = labels.join(', '); // Join all labels for display
+ const combinedLabelText = labels.join(", "); // Join all labels for display
const stylingLabel = labels[0]; // Use the first label for color styling (consistent)
// Estimate label width dynamically based on combined text
@@ -393,7 +391,7 @@ export default function Pdf(props) {
// Check if moving right pushed it off-canvas
if (finalLabelX + labelRectWidth > canvas_width) {
- console.warn("Label pushed off-canvas to the right, attempting to place below initial Y instead:", combinedLabelText);
+ // console.warn("Label pushed off-canvas to the right, attempting to place below initial Y instead:", combinedLabelText);
// Fallback strategy: Reset X to its original proposed position
// and try moving Y down from the *initial* Y position
finalLabelX = proposedLabelX; // Reset X
@@ -420,7 +418,7 @@ export default function Pdf(props) {
*/
}
if (attempts === maxAttempts) {
- console.warn("Could not find non-colliding position for label after max attempts:", combinedLabelText);
+ // console.warn("Could not find non-colliding position for label after max attempts:", combinedLabelText);
// Draw at the last attempted position, even if it overlaps
}
// --- End Label Positioning and Collision Avoidance ---
@@ -439,16 +437,16 @@ export default function Pdf(props) {
// Draw the highlight boxes for the text
for (const coordGroup of coordGroups) {
- const parts = coordGroup.split(',');
+ const parts = coordGroup.split(",");
if (parts.length < 5) {
- console.warn("Skipping invalid coordGroup:", coordGroup);
+ // console.warn("Skipping invalid coordGroup:", coordGroup);
continue;
}
const [p, x, y, w, h] = parts.map(Number);
- let highlightDrawX = x * scale_x + offset_x; // Use separate vars for clarity
- let highlightDrawY = y * scale_y;
- let highlightDrawWidth = w * scale_x;
- let highlightDrawHeight = h * scale_y;
+ const highlightDrawX = x * scale_x + offset_x; // Use separate vars for clarity
+ const highlightDrawY = y * scale_y;
+ const highlightDrawWidth = w * scale_x;
+ const highlightDrawHeight = h * scale_y;
highlightText(context, stylingLabel, highlightDrawX, highlightDrawY, highlightDrawWidth, highlightDrawHeight, text);
}
});
diff --git a/src/components/previewers/Thumbnail.jsx b/src/components/previewers/Thumbnail.jsx
index 6a4b6cd..8ccb654 100644
--- a/src/components/previewers/Thumbnail.jsx
+++ b/src/components/previewers/Thumbnail.jsx
@@ -1,8 +1,7 @@
-import React from "react";
import { Typography } from "@material-ui/core";
export default function Thumbnail(props){
- const {fileId, imgSrc, fileType, ...other} = props;
+ const {fileId, imgSrc, fileType} = props;
return (
(() => {
if (fileType === "image/jpeg" || fileType === "image/jpg" || fileType === "image/png"
@@ -11,12 +10,12 @@ export default function Thumbnail(props){
}
else if (fileType === "image/tiff"){
return ;
+ width={750} height={550} src={imgSrc} type="image/tiff" id="embedded" />;
}
else{
return ERROR: Unrecognised image format.;
}
})()
- )
+ );
}
diff --git a/src/components/previewers/Video.jsx b/src/components/previewers/Video.jsx
index d338ca5..eccb005 100644
--- a/src/components/previewers/Video.jsx
+++ b/src/components/previewers/Video.jsx
@@ -1,8 +1,6 @@
-import React from "react";
-
export default function Video(props) {
- const {fileId, videoSrc, ...other} = props;
- return (