Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restore file content changed #1005

Merged
merged 4 commits into from
Dec 4, 2018
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
163 changes: 141 additions & 22 deletions client/src/app/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export class App extends Component {
this.tabComponentCache = {};

// TODO(nikku): make state
this.tabHistory = new History();
this.navigationHistory = new History();

// TODO(nikku): make state
this.closedTabs = new History();
Expand Down Expand Up @@ -147,9 +147,7 @@ export class App extends Component {
return tabShown.promise;
}

const tabHistory = this.tabHistory;

tabHistory.push(tab);
this.navigationHistory.push(tab);

return this.setActiveTab(tab);
}
Expand All @@ -170,13 +168,104 @@ export class App extends Component {
return getNextTab(tabs, activeTab, direction);
};

const tabHistory = this.tabHistory;

const nextActiveTab = tabHistory.navigate(direction, nextFn);
const nextActiveTab = this.navigationHistory.navigate(direction, nextFn);

return this.setActiveTab(nextActiveTab);
}

checkFileChanged = async (tab) => {

const {
globals
} = this.props;

const {
fileSystem
} = globals;

const {
file
} = tab;

const tabLastModified = (file || {}).lastModified;

// skip new file
if (isNew(tab) || typeof tabLastModified === 'undefined') {
return;
}

const {
lastModified
} = await fileSystem.readFileStats(file);

// skip unchanged
if (!(lastModified > tabLastModified)) {
return;
}

const answer = await this.showDialog(getContentChangedDialog());

if (answer === 'ok') {
const updatedFile = await fileSystem.readFile(file.path);

return this.updateTab(tab, {
file: updatedFile
});
}
}

/**
* Update the tab with new attributes.
*
* @param {Tab} tab
* @param {Object} newAttrs
*/
updateTab(tab, newAttrs) {

if (newAttrs.id && newAttrs.id !== tab.id) {
throw new Error('must not change tab.id');
}

const updatedTab = {
...tab,
...newAttrs
};

this.setState((state) => {

const {
activeTab,
tabs
} = state;

// replace in tabs
const updatedTabs = tabs.map(t => {
if (t === tab) {
return updatedTab;
}

return t;
});


// replace activeTab
let updatedActiveTab = activeTab;
if (activeTab.id === updatedTab.id) {
updatedActiveTab = updatedTab;
}

return {
activeTab: updatedActiveTab,
tabs: updatedTabs
};
});

// replace in navigation history
this.navigationHistory.replace(tab, updatedTab);

return updatedTab;
}

setActiveTab(tab) {

const {
Expand All @@ -189,7 +278,7 @@ export class App extends Component {
}

if (tab !== EMPTY_TAB) {
const navigationHistory = this.tabHistory;
const navigationHistory = this.navigationHistory;

if (navigationHistory.get() !== tab) {
navigationHistory.push(tab);
Expand Down Expand Up @@ -240,7 +329,7 @@ export class App extends Component {
} = this.state;

const {
tabHistory,
navigationHistory,
closedTabs
} = this;

Expand All @@ -252,7 +341,7 @@ export class App extends Component {

const newTabs = tabs.filter(t => t !== tab);

tabHistory.purge(tab);
navigationHistory.purge(tab);

if (!isNew(tab)) {
closedTabs.push(tab);
Expand All @@ -264,7 +353,7 @@ export class App extends Component {

// open previous tab, if it exists
const nextActive = (
tabHistory.get() ||
navigationHistory.get() ||
newTabs[tabIdx] ||
newTabs[tabIdx - 1] ||
EMPTY_TAB
Expand All @@ -281,8 +370,9 @@ export class App extends Component {
});
}

selectTab = tab => {
return this.setActiveTab(tab);
selectTab = async tab => {
const updatedTab = await this.checkFileChanged(tab);
return this.setActiveTab(updatedTab || tab);
}

moveTab = (tab, newIndex) => {
Expand Down Expand Up @@ -413,17 +503,16 @@ export class App extends Component {
});

if (response == 'create') {
assign(file, {
contents: tabsProvider.getInitialFileContents(fileType)
});

// TODO(philippfromme): fix dirty state
let tab = this.addTab(
tabsProvider.createTabForFile(file)
);

this.handleTabChanged(tab)({
dirty: true
tab = await this.updateTab(tab, {
file: {
contents: tabsProvider.getInitialFileContents(fileType)
}
});

await this.selectTab(tab);
Expand Down Expand Up @@ -597,10 +686,6 @@ export class App extends Component {
});
}

if ('contents' in properties) {
tab.file.contents = properties.contents;
}

tabState = {
...tabState,
...properties
Expand All @@ -613,6 +698,24 @@ export class App extends Component {
this.updateMenu(tabState);
}

/**
* Handle if tab content is updated.
*
* @param {String} new tab content
*/
handleTabContentUpdated = (tab) => (newContent) => {

if (!newContent) {
return;
}

this.updateTab(tab, {
file: {
contents: newContent
}
});
}

tabSaved(tab, newFile) {

const {
Expand Down Expand Up @@ -1099,6 +1202,10 @@ export class App extends Component {
this.openExternalUrl(options);
}

if (action === 'check-file-changed') {
return this.checkFileChanged(activeTab);
}

const tab = this.tabRef.current;

return tab.triggerAction(action, options);
Expand Down Expand Up @@ -1238,6 +1345,7 @@ export class App extends Component {
tab={ activeTab }
layout={ layout }
onChanged={ this.handleTabChanged(activeTab) }
onContentUpdated={ this.handleTabContentUpdated(activeTab) }
onError={ this.handleTabError(activeTab) }
onWarning={ this.handleTabWarning(activeTab) }
onShown={ this.handleTabShown(activeTab) }
Expand Down Expand Up @@ -1393,6 +1501,17 @@ function getFileTypeFromExtension(filePath) {
return filePath.split('.').pop();
}

function getContentChangedDialog() {
return {
title: 'File changed',
message: 'The file has been changed externally.\nWould you like to reload it?',
type: 'question',
buttons: [
{ id: 'ok', label: 'Reload' },
{ id: 'cancel', label: 'Cancel' }
]
};
}
function getOpenFilesDialogFilters(providers) {
const allSupportedFilter = {
name: 'All Supported',
Expand Down
4 changes: 4 additions & 0 deletions client/src/app/AppParent.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ export default class AppParent extends Component {
document.body.classList.remove('loading');
});

backend.on('client:window-focused', (event) => {
this.triggerAction(event, 'check-file-changed');
});

this.registerMenus();
}

Expand Down
13 changes: 9 additions & 4 deletions client/src/app/History.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,18 @@ export default class History {
return this.elements[this.idx];
}

/**
* Replace all instances of element with new element.
*/
replace(element, newElement) {

const elementIdx = this.elements.indexOf(element);
this.elements = this.elements.map(e => {
if (e === element) {
return newElement;
}

if (elementIdx !== -1) {
this.elements[elementIdx] = newElement;
}
return e;
});
}

navigate(direction, nextFn) {
Expand Down
3 changes: 3 additions & 0 deletions client/src/app/TabsProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,9 @@ export default class TabsProvider {
get name() {
return this.file.name;
},
set name(newName) {
this.file.name = newName;
},
get title() {
return this.file.path || 'unsaved';
},
Expand Down
25 changes: 25 additions & 0 deletions client/src/app/__tests__/AppParentSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,31 @@ describe('<AppParent>', function() {

});


describe('on focus', function() {

it('should fire check-file-changed action', function() {

// given
const backend = new Backend();

const {
appParent
} = createAppParent({ globals: { backend } }, mount);

const app = appParent.appRef.current;
const actionSpy = spy(app, 'triggerAction');

// when
backend.send('client:window-focused');

// then
expect(actionSpy).to.have.been.calledWith('check-file-changed');

});

});

});


Expand Down
Loading