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
19 changes: 19 additions & 0 deletions app/components/Project/About/About.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,24 @@ function About(props) {
JSON.stringify(notes) !== JSON.stringify(initialState.notes)
);
};

useEffect(() => {
// Only check for description changes
const isDescriptionDirty =
descriptionEditor !== initialState.descriptionEditor ||
descriptionText !== initialState.descriptionText ||
descriptionUri !== initialState.descriptionUri;

if (props.onDirtyStateChange) {
props.onDirtyStateChange(isDescriptionDirty);
}
}, [
descriptionEditor,
descriptionText,
descriptionUri,
initialState,
props.onDirtyStateChange,
]);

const updatedNoteHandler = (note, text) => {
if (note) {
Expand Down Expand Up @@ -368,6 +386,7 @@ About.propTypes = {
onAddedNote: PropTypes.func,
onDeletedNote: PropTypes.func,
onClickUpdatesLink: PropTypes.func,
onDirtyStateChange: PropTypes.func,
};

export default About;
23 changes: 14 additions & 9 deletions app/components/Project/Project.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ import GeneralUtil from '../../utils/general';
import styles from './Project.css';
import UserContext from '../../contexts/User';

type Props = {};
type Props = {
onDirtyStateChange?: (boolean) => void,
};

class Project extends Component<Props> {
props: Props;
Expand All @@ -40,7 +42,7 @@ class Project extends Component<Props> {
// Safely get and check the previous project ID and the current project ID from
// the properties. If the project selection changes, we want the Dashboard tab
// to be selected. This is the component-class response to useEffect().
var previousId = (prevProps && prevProps.project ) ? prevProps.project.id : null;
var previousId = (prevProps && prevProps.project) ? prevProps.project.id : null;
var currentId = (this.props && this.props.project) ? this.props.project.id : null;
if (previousId != currentId) {
this.setState({ selectedTab: 'about' });
Expand Down Expand Up @@ -297,7 +299,7 @@ class Project extends Component<Props> {
* @param {object} note Optional parameter if there is an existing note being updated. If not provided, a new note is assumed.
*/
checklistUpsertNoteHandler = (checklistItem, text, note) => {
if (this.unchangedNote(note,text)) {
if (this.unchangedNote(note, text)) {
return;
}

Expand Down Expand Up @@ -732,7 +734,7 @@ class Project extends Component<Props> {
const user = this.context;
const currentProject = { ...this.props.project };
const oldExternalAsset = cloneDeep(this.props.project.externalAssets.children ?
this.props.project.externalAssets.children.find(x => x.uri === asset.uri) : null);
this.props.project.externalAssets.children.find(x => x.uri === asset.uri) : null);
const action = {
type: ActionType.EXTERNAL_ASSET_UPDATED,
title: ActionType.EXTERNAL_ASSET_UPDATED,
Expand Down Expand Up @@ -792,6 +794,7 @@ class Project extends Component<Props> {
project={this.props.project}
updates={this.props.logs ? this.props.logs.updates : null}
onClickUpdatesLink={this.clickUpdatesLinkHandler}
onDirtyStateChange={this.props.onDirtyStateChange}
/>
) : null;
const assets = this.props.project ? (
Expand Down Expand Up @@ -883,11 +886,11 @@ class Project extends Component<Props> {
<div className={styles.header}>
<div className={styles.titleContainer}>
<IconButton
color="inherit"
onClick={() => this.props.onFavoriteClick(this.props.project.id)}
>
{this.props.project && this.props.project.favorite ? <Star /> : <StarBorder />}
</IconButton>
color="inherit"
onClick={() => this.props.onFavoriteClick(this.props.project.id)}
>
{this.props.project && this.props.project.favorite ? <Star /> : <StarBorder />}
</IconButton>
<div className={styles.title}>
{name}
{projectPath}
Expand Down Expand Up @@ -948,6 +951,7 @@ Project.propTypes = {
onUpdated: PropTypes.func,
onAssetSelected: PropTypes.func,
onChecklistUpdated: PropTypes.func,
onDirtyStateChange: PropTypes.func,
// This object has the following structure:
// {
// logs: array<string> - the actual log data
Expand Down Expand Up @@ -975,6 +979,7 @@ Project.defaultProps = {
onUpdated: null,
onAssetSelected: null,
onChecklistUpdated: null,
onDirtyStateChange: null,
logs: null,
checklistResponse: null,
configuration: null,
Expand Down
79 changes: 76 additions & 3 deletions app/containers/ProjectPage/ProjectPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ import UserContext from '../../contexts/User';
import Messages from '../../constants/messages';
import ChecklistUtil from '../../utils/checklist';
import { PanelGroup, Panel, PanelResizeHandle } from 'react-resizable-panels';
import {
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
Button,
} from '@mui/material';

class ProjectPage extends Component {
constructor(props) {
Expand Down Expand Up @@ -54,7 +62,13 @@ class ProjectPage extends Component {
// Show progress on scanning details about the project. Allows the user to access what information
// is available, yet be aware that more information may be arriving later.
projectScanStatus: '',
isProjectDirty: false,
showDirtyConfirmation: false,
pendingProject: null,
};
this.handleProjectDirtyStateChange = this.handleProjectDirtyStateChange.bind(this);
this.handleDiscardChanges = this.handleDiscardChanges.bind(this);
this.handleCancelSwitch = this.handleCancelSwitch.bind(this);

this.handleLoadProjectListResponse = this.handleLoadProjectListResponse.bind(this);
this.refreshProjectsHandler = this.refreshProjectsHandler.bind(this);
Expand Down Expand Up @@ -394,6 +408,9 @@ class ProjectPage extends Component {
case Messages.REMOVE_PROJECT_LIST_ENTRY_REQUEST:
ipcRenderer.send(Messages.REMOVE_PROJECT_LIST_ENTRY_REQUEST, projectId);
break;
case Messages.SHOW_ITEM_IN_FOLDER:
ipcRenderer.send(Messages.SHOW_ITEM_IN_FOLDER, projectId);
break;
default:
console.warn(`Unknown project list entry menu event: ${event}`);
}
Expand All @@ -407,6 +424,23 @@ class ProjectPage extends Component {
return;
}

// If the user clicks the project that is already selected, ignore it.
if (this.state.selectedProject && this.state.selectedProject.id === project.id) {
return;
}

if (this.state.isProjectDirty && this.state.selectedProject && this.state.selectedProject.id !== project.id) {
this.setState({
showDirtyConfirmation: true,
pendingProject: project
});
return;
}

this.loadProject(project);
}

loadProject(project) {
// If the project is offline (has loadError), try to refresh its status
if (project.loadError) {
// First update the UI to show we're trying to reconnect
Expand Down Expand Up @@ -446,6 +480,28 @@ class ProjectPage extends Component {
}
}

handleProjectDirtyStateChange(isDirty) {
this.setState({ isProjectDirty: isDirty });
}

handleDiscardChanges() {
this.setState({
showDirtyConfirmation: false,
isProjectDirty: false
}, () => {
if (this.state.pendingProject) {
this.loadProject(this.state.pendingProject);
}
});
}

handleCancelSwitch() {
this.setState({
showDirtyConfirmation: false,
pendingProject: null
});
}

handleProjectUpdate(project, actionType, entityType, entityKey, title, description, details) {
// Update our cached list of projects from which we get the selected projects. We want to ensure
// these are kept in sync with any updates.
Expand Down Expand Up @@ -534,6 +590,7 @@ class ProjectPage extends Component {
configuration={{ assetAttributes: this.state.assetAttributes }}
assetDynamicDetails={this.state.assetDynamicDetails}
scanStatus={this.state.projectScanStatus}
onDirtyStateChange={this.handleProjectDirtyStateChange}
/>
</Panel>
</PanelGroup>
Expand All @@ -547,12 +604,28 @@ class ProjectPage extends Component {
anchorElement={
this.state.projectListMenuAnchor ? this.state.projectListMenuAnchor.element : null
}
project={
this.state.projectListMenuAnchor ? this.state.projectListMenuAnchor.project : null
}
onClose={this.handleCloseProjectListMenu}
onMenuClick={this.handleClickProjectListMenu}
/>
<Dialog
open={this.state.showDirtyConfirmation}
onClose={this.handleCancelSwitch}
>
<DialogTitle style={{ color: 'white' , backgroundColor: '#aa94d1' }}>Discard Changes</DialogTitle>
<DialogContent>
<DialogContentText>
You have unsaved changes in the current project. Switching to another project will discard these changes. Are you sure you want to proceed?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={this.handleDiscardChanges} color="primary" autoFocus>
Discard Changes
</Button>
<Button onClick={this.handleCancelSwitch} color="secondary">
Cancel
</Button>
</DialogActions>
</Dialog>
</div>
);
}
Expand Down