Skip to content
This repository was archived by the owner on Jan 22, 2019. It is now read-only.

Commit 0c49ab1

Browse files
authored
feat: accept settings updates from Sourcegraph pages (#146)
1 parent 82ee5d5 commit 0c49ab1

File tree

5 files changed

+93
-77
lines changed

5 files changed

+93
-77
lines changed

package-lock.json

Lines changed: 10 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@
8585
},
8686
"dependencies": {
8787
"@sourcegraph/codeintellify": "^3.8.3",
88-
"@sourcegraph/extensions-client-common": "^8.1.4",
88+
"@sourcegraph/extensions-client-common": "^8.2.0",
8989
"@sourcegraph/react-loading-spinner": "0.0.6",
9090
"@sqs/jsonc-parser": "^1.0.3",
9191
"bootstrap": "^4.0.0",

src/libs/sourcegraph/inject.tsx

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,44 @@
1+
import { connectAsClient } from '@sourcegraph/extensions-client-common/lib/messaging'
2+
import storage from '../../browser/storage'
3+
import { updateExtensionSettings } from '../../shared/backend/extensions'
4+
15
export function injectSourcegraphApp(marker: HTMLElement): void {
26
if (document.getElementById(marker.id)) {
37
return
48
}
59

10+
// Generate and insert DOM element, in case this code executes first.
11+
document.body.appendChild(marker)
12+
13+
connectAsClient()
14+
.then(connection => {
15+
storage.observeSync('clientSettings').subscribe(settings => {
16+
connection.sendSettings(settings)
17+
})
18+
19+
connection.onEditSetting(edit => updateExtensionSettings('Client', edit).toPromise())
20+
21+
connection.onGetSettings(
22+
() =>
23+
new Promise<string>(resolve => {
24+
storage.getSync(storageItems => {
25+
resolve(storageItems.clientSettings)
26+
})
27+
})
28+
)
29+
})
30+
.catch(error => console.error(error))
31+
632
window.addEventListener('load', () => {
7-
dispatchSourcegraphEvents(marker)
33+
dispatchSourcegraphEvents()
834
})
935

1036
if (document.readyState === 'complete' || document.readyState === 'interactive') {
11-
dispatchSourcegraphEvents(marker)
37+
dispatchSourcegraphEvents()
1238
}
1339
}
1440

15-
function dispatchSourcegraphEvents(marker: HTMLElement): void {
16-
// Generate and insert DOM element, in case this code executes first.
17-
document.body.appendChild(marker)
41+
function dispatchSourcegraphEvents(): void {
1842
// Send custom webapp <-> extension registration event in case webapp listener is attached first.
1943
document.dispatchEvent(new CustomEvent<{}>('sourcegraph:browser-extension-registration'))
2044
}

src/shared/backend/extensions.ts

Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { UpdateExtensionSettingsArgs } from '@sourcegraph/extensions-client-common/lib/context'
12
import { Controller as ExtensionsContextController } from '@sourcegraph/extensions-client-common/lib/controller'
23
import { ConfiguredExtension } from '@sourcegraph/extensions-client-common/lib/extensions/extension'
34
import { gql, graphQLContent } from '@sourcegraph/extensions-client-common/lib/graphql'
@@ -17,15 +18,14 @@ import { isEqual } from 'lodash'
1718
import Alert from 'mdi-react/AlertIcon'
1819
import MenuDown from 'mdi-react/MenuDownIcon'
1920
import Menu from 'mdi-react/MenuIcon'
20-
import { combineLatest, Observable, Subject, throwError } from 'rxjs'
21+
import { combineLatest, from, Observable, throwError } from 'rxjs'
2122
import { distinctUntilChanged, map, mergeMap, switchMap, take } from 'rxjs/operators'
2223
import { ClientOptions } from 'sourcegraph/module/client/client'
2324
import { MessageTransports } from 'sourcegraph/module/jsonrpc2/connection'
2425
import { TextDocumentDecoration } from 'sourcegraph/module/protocol'
25-
import { ConfigurationUpdateParams } from 'sourcegraph/module/protocol'
2626
import uuid from 'uuid'
2727
import { Disposable } from 'vscode-languageserver'
28-
import storage from '../../browser/storage'
28+
import storage, { StorageItems } from '../../browser/storage'
2929
import { ExtensionConnectionInfo } from '../../extension/scripts/background'
3030
import { onFirstMessage } from '../../extension/scripts/background'
3131
import { getContext } from './context'
@@ -255,45 +255,7 @@ export function createExtensionsContextController(
255255
map(([gqlCascade, storageCascade]) => mergeCascades(gqlToCascade(gqlCascade), storageCascade)),
256256
distinctUntilChanged((a, b) => isEqual(a, b))
257257
),
258-
updateExtensionSettings: (
259-
subjectID,
260-
args: { extensionID: string; edit?: ConfigurationUpdateParams; enabled?: boolean; remove?: boolean }
261-
) => {
262-
if (subjectID !== 'Client') {
263-
return throwError('Cannot update settings for ' + subjectID + '.')
264-
}
265-
const update = new Subject<undefined>()
266-
storage.getSync(storageItems => {
267-
let clientSettings = storageItems.clientSettings
268-
269-
const format = { tabSize: 2, insertSpaces: true, eol: '\n' }
270-
271-
if (args.edit) {
272-
clientSettings = applyEdits(
273-
clientSettings,
274-
// TODO(chris): remove `.slice()` (which guards against
275-
// mutation) once
276-
// https://github.com/Microsoft/node-jsonc-parser/pull/12
277-
// is merged in.
278-
setProperty(clientSettings, args.edit.path.slice(), args.edit.value, format)
279-
)
280-
} else if (typeof args.enabled === 'boolean') {
281-
clientSettings = applyEdits(
282-
clientSettings,
283-
setProperty(clientSettings, ['extensions', args.extensionID], args.enabled, format)
284-
)
285-
} else if (args.remove) {
286-
clientSettings = applyEdits(
287-
clientSettings,
288-
removeProperty(clientSettings, ['extensions', args.extensionID], format)
289-
)
290-
}
291-
storage.setSync({ clientSettings }, () => {
292-
update.next(undefined)
293-
})
294-
})
295-
return update
296-
},
258+
updateExtensionSettings,
297259
queryGraphQL: (request, variables) =>
298260
storage.observeSync('sourcegraphURL').pipe(
299261
take(1),
@@ -315,3 +277,41 @@ export function createExtensionsContextController(
315277
},
316278
})
317279
}
280+
281+
export const updateExtensionSettings = (subjectID, args: UpdateExtensionSettingsArgs): Observable<undefined> => {
282+
if (subjectID !== 'Client') {
283+
return throwError('Cannot update settings for ' + subjectID + '.')
284+
}
285+
return from(
286+
new Promise<StorageItems>(resolve => storage.getSync(storageItems => resolve(storageItems))).then(
287+
storageItems => {
288+
let clientSettings = storageItems.clientSettings
289+
290+
const format = { tabSize: 2, insertSpaces: true, eol: '\n' }
291+
292+
if ('edit' in args && args.edit) {
293+
clientSettings = applyEdits(
294+
clientSettings,
295+
// TODO(chris): remove `.slice()` (which guards against
296+
// mutation) once
297+
// https://github.com/Microsoft/node-jsonc-parser/pull/12
298+
// is merged in.
299+
setProperty(clientSettings, args.edit.path.slice(), args.edit.value, format)
300+
)
301+
} else if ('extensionID' in args) {
302+
clientSettings = applyEdits(
303+
clientSettings,
304+
typeof args.enabled === 'boolean'
305+
? setProperty(clientSettings, ['extensions', args.extensionID], args.enabled, format)
306+
: removeProperty(clientSettings, ['extensions', args.extensionID], format)
307+
)
308+
}
309+
return new Promise<undefined>(resolve =>
310+
storage.setSync({ clientSettings }, () => {
311+
resolve(undefined)
312+
})
313+
)
314+
}
315+
)
316+
)
317+
}

src/shared/components/options/ConnectionCard.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,17 @@ export class ConnectionCard extends React.Component<Props, State> {
4545
}
4646
}
4747

48+
private setContentScriptUrls(props: Props): void {
49+
this.contentScriptUrls = [...props.storage.clientConfiguration.contentScriptUrls, props.storage.sourcegraphURL]
50+
}
51+
4852
public componentDidMount(): void {
49-
this.contentScriptUrls = this.props.storage.clientConfiguration.contentScriptUrls
53+
this.setContentScriptUrls(this.props)
5054
this.checkConnection()
5155
}
5256

5357
public componentWillReceiveProps(nextProps: Props): void {
54-
this.contentScriptUrls = nextProps.storage.clientConfiguration.contentScriptUrls
58+
this.setContentScriptUrls(nextProps)
5559
}
5660

5761
private sourcegraphServerAlert = (): JSX.Element => {
@@ -74,12 +78,14 @@ export class ConnectionCard extends React.Component<Props, State> {
7478
</div>
7579
)
7680
}
77-
const hasPermissions = this.contentScriptUrls.every(val => permissionOrigins.indexOf(`${val}/*`) >= 0)
78-
if (!hasPermissions && !permissionOrigins.includes('<all_urls>')) {
81+
const forbiddenUrls = permissionOrigins.includes('<all_urls>')
82+
? []
83+
: this.contentScriptUrls.filter(url => !permissionOrigins.includes(`${url}/*`))
84+
if (forbiddenUrls.length !== 0) {
7985
return (
8086
<div className="pt-2">
8187
<Alert color="warning">
82-
{`Missing content script permissions: ${this.contentScriptUrls.join(', ')}.`}
88+
{`Missing content script permissions: ${forbiddenUrls.join(', ')}.`}
8389
<div className="pt-2">
8490
<Button
8591
onClick={this.requestPermissions}

0 commit comments

Comments
 (0)