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

Offline mode #54831

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
56a85df
First draft for offline mode
ramya-rao-a Jul 22, 2018
892c555
Fix imports
ramya-rao-a Jul 23, 2018
f1c8435
Fix tests
ramya-rao-a Jul 23, 2018
e5bdf92
Merge remote-tracking branch 'origin/master' into offline-mode
ramya-rao-a Jul 23, 2018
ee86939
Refactoring
ramya-rao-a Jul 23, 2018
d03d190
Merge remote-tracking branch 'origin/master' into offline-mode
ramya-rao-a Jul 23, 2018
ad1c8c1
dont fetch experiments in offline mode
ramya-rao-a Jul 23, 2018
f490af7
changelog opens release notes in browser in offline mode
ramya-rao-a Jul 23, 2018
7fef11f
Use MenuRegistry to register menu items
ramya-rao-a Jul 24, 2018
35d20ed
Check for updates in offline mode from context menu
ramya-rao-a Jul 24, 2018
b6dd980
Refactoring
ramya-rao-a Jul 24, 2018
ea5d929
Disclaimer
ramya-rao-a Jul 24, 2018
2a6d5f7
Register cmd pallet commands using MenuRegistry
ramya-rao-a Jul 24, 2018
1fcbb6c
Release notes in offline mode
ramya-rao-a Jul 24, 2018
10fa520
logUploader and others in offline mode
ramya-rao-a Jul 24, 2018
7cae07e
Prevent future attempt at requestservice when in offline mode
ramya-rao-a Jul 24, 2018
67b0554
Since release notes action fallsback to browser, changlog doesnt need…
ramya-rao-a Jul 24, 2018
cfdfa40
Release notes refactor
ramya-rao-a Jul 24, 2018
5004eb2
Merge remote-tracking branch 'origin/master' into offline-mode
ramya-rao-a Jul 24, 2018
36d74df
Offline mode tests
ramya-rao-a Jul 24, 2018
23f3391
Merge remote-tracking branch 'origin/master' into offline-mode
ramya-rao-a Jul 24, 2018
7d83013
Other tests
ramya-rao-a Jul 24, 2018
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: 9 additions & 1 deletion src/vs/code/electron-main/logUploader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import product from 'vs/platform/node/product';
import { IRequestService } from 'vs/platform/request/node/request';
import { IRequestContext } from 'vs/base/node/request';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { offlineModeSetting, unSupportedInOfflineModeMsg } from 'vs/platform/actions/common/offlineMode';

interface PostResult {
readonly blob_id: string;
Expand All @@ -36,8 +38,14 @@ class Endpoint {
export async function uploadLogs(
channel: ILaunchChannel,
requestService: IRequestService,
environmentService: IEnvironmentService
environmentService: IEnvironmentService,
configurationService: IConfigurationService
): Promise<any> {
if (configurationService.getValue(offlineModeSetting) === true) {
console.error(unSupportedInOfflineModeMsg);
return;
}

const endpoint = Endpoint.getFromProduct();
if (!endpoint) {
console.error(localize('invalidEndpoint', 'Invalid log uploader endpoint'));
Expand Down
3 changes: 2 additions & 1 deletion src/vs/code/electron-main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ function setupIPC(accessor: ServicesAccessor): TPromise<Server> {
const logService = accessor.get(ILogService);
const environmentService = accessor.get(IEnvironmentService);
const requestService = accessor.get(IRequestService);
const configurationService = accessor.get(IConfigurationService);

function allowSetForegroundWindow(service: LaunchChannelClient): TPromise<void> {
let promise = TPromise.wrap<void>(void 0);
Expand Down Expand Up @@ -204,7 +205,7 @@ function setupIPC(accessor: ServicesAccessor): TPromise<Server> {

// Log uploader
if (typeof environmentService.args['upload-logs'] !== 'undefined') {
return uploadLogs(channel, requestService, environmentService)
return uploadLogs(channel, requestService, environmentService, configurationService)
.then(() => TPromise.wrapError(new ExpectedError()));
}

Expand Down
8 changes: 8 additions & 0 deletions src/vs/code/electron-main/menubar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { IHistoryMainService } from 'vs/platform/history/common/history';
import { IWorkspaceIdentifier, getWorkspaceLabel, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { IMenubarData, IMenubarMenuItemAction, IMenubarMenuItemSeparator } from 'vs/platform/menubar/common/menubar';
import URI from 'vs/base/common/uri';
import { offlineModeSetting } from 'vs/platform/actions/common/offlineMode';

// interface IExtensionViewlet {
// id: string;
Expand Down Expand Up @@ -581,6 +582,13 @@ export class Menubar {
}

private getUpdateMenuItems(): Electron.MenuItem[] {
if (this.configurationService.getValue(offlineModeSetting) === true) {
return [new MenuItem({
label: nls.localize('miCheckForUpdates', "Check for Updates..."), click: () => {
this.runActionInRenderer('workbench.action.notifyUnsupportedFeatureInOfflineMode');
}
})];
}
const state = this.updateService.state;

switch (state.type) {
Expand Down
8 changes: 8 additions & 0 deletions src/vs/code/electron-main/menus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { IWindowsMainService, IWindowsCountChangedEvent } from 'vs/platform/wind
import { IHistoryMainService } from 'vs/platform/history/common/history';
import { IWorkspaceIdentifier, getWorkspaceLabel, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import URI from 'vs/base/common/uri';
import { offlineModeSetting } from 'vs/platform/actions/common/offlineMode';

interface IMenuItemClickHandler {
inDevTools: (contents: Electron.WebContents) => void;
Expand Down Expand Up @@ -1124,6 +1125,13 @@ export class CodeMenu {
}

private getUpdateMenuItems(): Electron.MenuItem[] {
if (this.configurationService.getValue(offlineModeSetting) === true) {
return [new MenuItem({
label: nls.localize('miCheckForUpdates', "Check for Updates..."), click: () => {
this.runActionInRenderer('workbench.action.notifyUnsupportedFeatureInOfflineMode');
}
})];
}
const state = this.updateService.state;

switch (state.type) {
Expand Down
173 changes: 173 additions & 0 deletions src/vs/platform/actions/common/offlineMode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

'use strict';

import { localize } from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import { TPromise } from 'vs/base/common/winjs.base';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';

export const offlineModeSetting = 'workbench.enableOfflineMode';
export const unSupportedInOfflineModeMsg = localize('offlineModeUnsupportedFeature', "This feature is not supported in offline mode");

export class EnableOfflineMode extends Action {
static readonly ID = 'workbench.action.enableOfflineMode';
static LABEL = localize('enableOfflineMode', 'Enable Offline Mode');

private disposables: IDisposable[] = [];
private readonly disclaimerStorageKey = 'workbench.offlineMode.disclaimer.dontShowAgain';

constructor(
id: string = EnableOfflineMode.ID,
label: string = EnableOfflineMode.LABEL,
@IConfigurationService private configurationService: IConfigurationService,
@IStorageService private storageService: IStorageService,
@INotificationService private notificationService: INotificationService
) {
super(id, label);
this.updateEnabled();
this.configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(offlineModeSetting)) {
this.updateEnabled();
}
}, this, this.disposables);
}

private updateEnabled() {
this.enabled = this.configurationService.getValue(offlineModeSetting) !== true;
}

run(): TPromise<any> {
if (this.storageService.getBoolean(this.disclaimerStorageKey, StorageScope.GLOBAL, false) === false) {
this.notificationService.prompt(Severity.Info, localize('offlineModeDisclaimer', "VS Code cannot stop extensions from making network requests in offline mode. If extensions make such requests, please log an issue against them."), [
Copy link
Collaborator

@mjbvz mjbvz Jul 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way for extensions to check if the user is in offline mode?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mjbvz Yes, they can look at the value for the workbench.enableOfflineMode setting. I havent yet gone through built-in extensions to ensure they are able to comply with the offline mode. That will be another PR.

Copy link
Collaborator

@mjbvz mjbvz Jul 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so that will added to vscode.d.ts? Or read the setting value directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Read the setting value directly

{
label: localize('DontShowAgain', "Don't Show Again"),
run: () => {
this.storageService.store(this.disclaimerStorageKey, true);
}
}
]);
}
return this.configurationService.updateValue(offlineModeSetting, true);
}

dispose(): void {
this.disposables = dispose(this.disposables);
super.dispose();
}
}

export class DisableOfflineMode extends Action {
static readonly ID = 'workbench.action.disableOfflineMode';
static LABEL = localize('disableOfflineMode', 'Disable Offline Mode');

private disposables: IDisposable[] = [];

constructor(
id: string = DisableOfflineMode.ID,
label: string = DisableOfflineMode.LABEL,
@IConfigurationService private configurationService: IConfigurationService
) {
super(id, label);
this.updateEnabled();
this.configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(offlineModeSetting)) {
this.updateEnabled();
}
}, this, this.disposables);
}

private updateEnabled() {
this.enabled = this.configurationService.getValue(offlineModeSetting) === true;
}

run(): TPromise<any> {
return this.configurationService.updateValue(offlineModeSetting, false);
}

dispose(): void {
this.disposables = dispose(this.disposables);
super.dispose();
}
}

export class NotifyUnsupportedFeatureInOfflineMode extends Action {
static readonly ID = 'workbench.action.notifyUnsupportedFeatureInOfflineMode';

constructor(
id: string = NotifyUnsupportedFeatureInOfflineMode.ID,
label: string = '',
@IConfigurationService private configurationService: IConfigurationService,
@INotificationService private notificationService: INotificationService
) {
super(id, label);
}

run(): TPromise<any> {
this.notificationService.prompt(Severity.Info, unSupportedInOfflineModeMsg, [
{
label: DisableOfflineMode.LABEL,
run: () => {
return this.configurationService.updateValue(offlineModeSetting, false);
}
}
]);
return TPromise.as(null);
}
}

MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, {
group: '5_offline',
command: {
id: EnableOfflineMode.ID,
title: localize({ key: 'miEnableOfflineMode', comment: ['&& denotes a mnemonic'] }, "Enable &&Offline Mode")
},
order: 1,
when: ContextKeyExpr.not('config.' + offlineModeSetting)
});

MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, {
group: '5_offline',
command: {
id: DisableOfflineMode.ID,
title: localize({ key: 'miDisableOfflineMode', comment: ['&& denotes a mnemonic'] }, "Disable &&Offline Mode")
},
order: 1,
when: ContextKeyExpr.has('config.' + offlineModeSetting)
});

CommandsRegistry.registerCommand(EnableOfflineMode.ID, serviceAccesor => {
serviceAccesor.get(IInstantiationService).createInstance(EnableOfflineMode, EnableOfflineMode.ID, EnableOfflineMode.LABEL).run();
});
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: EnableOfflineMode.ID,
title: 'Preferences: Enable Offline Mode'
},
when: ContextKeyExpr.not('config.' + offlineModeSetting)
});

CommandsRegistry.registerCommand(DisableOfflineMode.ID, serviceAccesor => {
serviceAccesor.get(IInstantiationService).createInstance(DisableOfflineMode, DisableOfflineMode.ID, DisableOfflineMode.LABEL).run();
});
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: DisableOfflineMode.ID,
title: 'Preferences: Disable Offline Mode'
},
when: ContextKeyExpr.has('config.' + offlineModeSetting)
});

CommandsRegistry.registerCommand(NotifyUnsupportedFeatureInOfflineMode.ID, serviceAccesor => {
serviceAccesor.get(IInstantiationService).createInstance(NotifyUnsupportedFeatureInOfflineMode, NotifyUnsupportedFeatureInOfflineMode.ID, '').run();
});
Loading