From cf65dec202f59a16694764d8b059bb0f5304f921 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Fri, 9 Oct 2020 18:57:22 +0200 Subject: [PATCH] Add after install page to browser extension (#14407) --- .prettierignore | 3 +- .../branded/src/global-styles/background.scss | 7 + client/branded/src/global-styles/colors.scss | 4 + client/branded/src/global-styles/index.scss | 1 + .../src/global-styles/web-content.scss | 6 - client/browser/config/webpack/base.config.ts | 5 +- client/browser/package.json | 3 + client/browser/scripts/tasks.ts | 12 +- client/browser/src/branded.scss | 7 + .../src/browser-extension/ThemeWrapper.tsx | 25 +++ .../AfterInstallPageContent.scss | 15 ++ .../AfterInstallPageContent.story.tsx | 9 + .../AfterInstallPageContent.tsx | 170 ++++++++++++++++++ .../src/browser-extension/manifest.spec.json | 2 +- .../options-page/OptionsMenu.story.tsx | 4 +- .../options-page/ServerUrlForm.story.tsx | 4 +- .../pages/after_install.html | 13 ++ .../src/browser-extension/pages/options.html | 2 +- .../scripts/afterInstallPage.main.tsx | 11 ++ .../scripts/backgroundPage.main.ts | 9 + client/browser/src/options.scss | 17 -- 21 files changed, 288 insertions(+), 41 deletions(-) create mode 100644 client/branded/src/global-styles/background.scss create mode 100644 client/browser/src/branded.scss create mode 100644 client/browser/src/browser-extension/ThemeWrapper.tsx create mode 100644 client/browser/src/browser-extension/after-install-page/AfterInstallPageContent.scss create mode 100644 client/browser/src/browser-extension/after-install-page/AfterInstallPageContent.story.tsx create mode 100644 client/browser/src/browser-extension/after-install-page/AfterInstallPageContent.tsx create mode 100644 client/browser/src/browser-extension/pages/after_install.html create mode 100644 client/browser/src/browser-extension/scripts/afterInstallPage.main.tsx delete mode 100644 client/browser/src/options.scss diff --git a/.prettierignore b/.prettierignore index ee1f78a973c..6fc95863968 100644 --- a/.prettierignore +++ b/.prettierignore @@ -21,10 +21,11 @@ testdata doc/ **/.cache **/__snapshots__ -**/*.html +**/__fixtures__ client/contrib/GH2SG.bookmarklet.js docker-images/grafana/jsonnet/*.json docker-images/grafana/grafonnet-lib/ docker-images/grafana/config/provisioning/dashboards/sourcegraph/ storybook-static/ +browser/code-intel-extensions/ !/.storybook/** diff --git a/client/branded/src/global-styles/background.scss b/client/branded/src/global-styles/background.scss new file mode 100644 index 00000000000..0693e661715 --- /dev/null +++ b/client/branded/src/global-styles/background.scss @@ -0,0 +1,7 @@ +// Extend Bootstrap's bg-* utilities with classes for our grey/blueish theme-aware background colors. + +@for $number from 1 through 3 { + .bg-#{$number} { + background: var(--color-bg-#{$number}); + } +} diff --git a/client/branded/src/global-styles/colors.scss b/client/branded/src/global-styles/colors.scss index 22f0795e20e..410a34d8a57 100644 --- a/client/branded/src/global-styles/colors.scss +++ b/client/branded/src/global-styles/colors.scss @@ -118,6 +118,8 @@ $body-bg-color-dark: #04070e; --body-color: #{$body-color-light}; --body-bg: #{$body-bg-color-light}; --color-bg-1: #{$color-light-bg-1}; + --color-bg-2: #{$color-light-bg-2}; + --color-bg-3: #{$color-light-bg-3}; --text-muted: #{$color-light-text-2}; --link-color: #{$gray-13}; --link-active-color: #{$gray-18}; @@ -132,6 +134,8 @@ $body-bg-color-dark: #04070e; --body-color: #{$body-color-dark}; --body-bg: #{$body-bg-color-dark}; --color-bg-1: #{$color-bg-1}; + --color-bg-2: #{$color-bg-2}; + --color-bg-3: #{$color-bg-3}; --text-muted: #{$color-text-1}; --link-color: #{$gray-07}; --link-active-color: #{$gray-02}; diff --git a/client/branded/src/global-styles/index.scss b/client/branded/src/global-styles/index.scss index 3bc6cb70534..37ac8a5fe83 100644 --- a/client/branded/src/global-styles/index.scss +++ b/client/branded/src/global-styles/index.scss @@ -133,6 +133,7 @@ $code-bg: var(--body-bg); @import 'bootstrap/scss/tooltip'; @import 'bootstrap/scss/transitions'; @import '../../../shared/src/global-styles/icons'; +@import './background'; @import './badge'; @import './card'; @import './dropdown'; diff --git a/client/branded/src/global-styles/web-content.scss b/client/branded/src/global-styles/web-content.scss index 08ae1af9a0e..70dc0938bf9 100644 --- a/client/branded/src/global-styles/web-content.scss +++ b/client/branded/src/global-styles/web-content.scss @@ -50,12 +50,6 @@ } } - strong { - .theme-dark & { - color: $gray-11; - } - } - .search-keyword { color: #329af0; diff --git a/client/browser/config/webpack/base.config.ts b/client/browser/config/webpack/base.config.ts index 2c154ea13e6..87be65f62e6 100644 --- a/client/browser/config/webpack/base.config.ts +++ b/client/browser/config/webpack/base.config.ts @@ -29,8 +29,9 @@ const config: webpack.Configuration = { backgroundEntry, '../../src/browser-extension/scripts/backgroundPage.main.ts' ), - options: buildEntry(extensionEntry, optionsEntry, '../../src/browser-extension/scripts/optionsPage.main.tsx'), inject: buildEntry(extensionEntry, contentEntry, '../../src/browser-extension/scripts/contentPage.main.ts'), + options: buildEntry(extensionEntry, optionsEntry, '../../src/browser-extension/scripts/optionsPage.main.tsx'), + 'after-install': path.resolve(__dirname, '../../src/browser-extension/scripts/afterInstallPage.main.tsx'), // Common native integration entry point (Gitlab, Bitbucket) integration: buildEntry(pageEntry, '../../src/native-integration/integration.main.ts'), @@ -39,7 +40,7 @@ const config: webpack.Configuration = { // Styles style: path.join(__dirname, '../../src/app.scss'), - 'options-style': path.join(__dirname, '../../src/options.scss'), + 'branded-style': path.join(__dirname, '../../src/branded.scss'), }, output: { path: path.join(__dirname, '../../build/dist/js'), diff --git a/client/browser/package.json b/client/browser/package.json index 399b895b619..45ac5e4953d 100644 --- a/client/browser/package.json +++ b/client/browser/package.json @@ -43,6 +43,9 @@ }, { "path": "./build/dist/css/style.bundle.css" + }, + { + "path": "./build/dist/css/branded-style.bundle.css" } ] } diff --git a/client/browser/scripts/tasks.ts b/client/browser/scripts/tasks.ts index a9ebee65ea6..9b739c9a3fe 100644 --- a/client/browser/scripts/tasks.ts +++ b/client/browser/scripts/tasks.ts @@ -62,16 +62,10 @@ export function copyAssets(): void { function copyExtensionAssets(toDirectory: string): void { shelljs.mkdir('-p', `${toDirectory}/js`, `${toDirectory}/css`, `${toDirectory}/img`) - shelljs.cp('build/dist/js/background.bundle.js', `${toDirectory}/js`) - shelljs.cp('build/dist/js/inject.bundle.js', `${toDirectory}/js`) - shelljs.cp('build/dist/js/options.bundle.js', `${toDirectory}/js`) - shelljs.cp('build/dist/js/extensionHostWorker.bundle.js', `${toDirectory}/js`) - shelljs.cp('build/dist/css/style.bundle.css', `${toDirectory}/css`) - shelljs.cp('build/dist/css/options-style.bundle.css', `${toDirectory}/css`) - shelljs.cp('build/dist/css/options-style.bundle.css', `${toDirectory}/css`) + shelljs.cp('build/dist/js/*.bundle.js', `${toDirectory}/js`) + shelljs.cp('build/dist/css/*.bundle.css', `${toDirectory}/css`) shelljs.cp('-R', 'build/dist/img/*', `${toDirectory}/img`) - shelljs.cp('build/dist/background.html', toDirectory) - shelljs.cp('build/dist/options.html', toDirectory) + shelljs.cp('build/dist/*.html', toDirectory) } /** diff --git a/client/browser/src/branded.scss b/client/browser/src/branded.scss new file mode 100644 index 00000000000..01033603fac --- /dev/null +++ b/client/browser/src/branded.scss @@ -0,0 +1,7 @@ +// CSS entry point for branded contexts (options menu and after-install page) +// Bootstrap etc can be used freely here (in opposite to the content page styles in app.scss) + +@import '../../branded/src/global-styles/index.scss'; +@import '../../branded/src/components/Toggle'; +@import './browser-extension/options-page/OptionsContainer'; +@import './browser-extension/after-install-page/AfterInstallPageContent'; diff --git a/client/browser/src/browser-extension/ThemeWrapper.tsx b/client/browser/src/browser-extension/ThemeWrapper.tsx new file mode 100644 index 00000000000..2c1447b858b --- /dev/null +++ b/client/browser/src/browser-extension/ThemeWrapper.tsx @@ -0,0 +1,25 @@ +import React, { useEffect, useMemo, useState } from 'react' +import { ThemeProps } from '../../../shared/src/theme' + +/** + * Wrapper for the browser extension that listens to changes of the OS theme. + */ +export function ThemeWrapper({ + children: Children, +}: { + children: (props: ThemeProps) => JSX.Element | null +}): JSX.Element | null { + const darkThemeMediaList = useMemo(() => window.matchMedia('(prefers-color-scheme: dark)'), []) + const [isLightTheme, setIsLightTheme] = useState(!darkThemeMediaList.matches) + + useEffect(() => { + darkThemeMediaList.addListener(event => setIsLightTheme(!event.matches)) + }, [darkThemeMediaList]) + + useEffect(() => { + document.body.classList.toggle('theme-light', isLightTheme) + document.body.classList.toggle('theme-dark', !isLightTheme) + }, [isLightTheme]) + + return +} diff --git a/client/browser/src/browser-extension/after-install-page/AfterInstallPageContent.scss b/client/browser/src/browser-extension/after-install-page/AfterInstallPageContent.scss new file mode 100644 index 00000000000..96b52d8f17f --- /dev/null +++ b/client/browser/src/browser-extension/after-install-page/AfterInstallPageContent.scss @@ -0,0 +1,15 @@ +.after-install-page-content { + &__sourcegraph-logo { + width: 1.5rem; + position: absolute; + top: 0.5rem; + left: 0.5rem; + } + &__code-host-logo { + height: 2rem !important; + width: 2rem !important; + } + &__code-host-titles { + line-height: 2rem; + } +} diff --git a/client/browser/src/browser-extension/after-install-page/AfterInstallPageContent.story.tsx b/client/browser/src/browser-extension/after-install-page/AfterInstallPageContent.story.tsx new file mode 100644 index 00000000000..3e5447ee1dc --- /dev/null +++ b/client/browser/src/browser-extension/after-install-page/AfterInstallPageContent.story.tsx @@ -0,0 +1,9 @@ +import React from 'react' +import { storiesOf } from '@storybook/react' +import { AfterInstallPageContent } from './AfterInstallPageContent' +import { BrandedStory } from '../../../../branded/src/components/BrandedStory' +import brandedStyles from '../../branded.scss' + +storiesOf('browser/AfterInstallPage', module).add('Default', () => ( + {() => } +)) diff --git a/client/browser/src/browser-extension/after-install-page/AfterInstallPageContent.tsx b/client/browser/src/browser-extension/after-install-page/AfterInstallPageContent.tsx new file mode 100644 index 00000000000..cdcd7064d82 --- /dev/null +++ b/client/browser/src/browser-extension/after-install-page/AfterInstallPageContent.tsx @@ -0,0 +1,170 @@ +import React from 'react' +import LockIcon from 'mdi-react/LockIcon' +import GithubIcon from 'mdi-react/GithubIcon' +import GitlabIcon from 'mdi-react/GitlabIcon' +import BitbucketIcon from 'mdi-react/BitbucketIcon' +import CheckIcon from 'mdi-react/CheckIcon' +import BookOpenPageVariantIcon from 'mdi-react/BookOpenPageVariantIcon' +import { PhabricatorIcon } from '../../../../shared/src/components/icons' +import { SourcegraphIcon } from '../../shared/components/SourcegraphIcon' + +export const AfterInstallPageContent: React.FunctionComponent = () => ( +
+ + +
+

🎉 You’ve just installed the Sourcegraph browser extension!

+

We’ve gathered the most important information that will get your started:

+
+ +
+
+

How do I use the extension?

+
+
+

Code intelligence on your code host

+

+ Sourcegraph browser extension adds code intelligence to files and diffs on GitHub, GitHub + Enterprise, GitLab, Phabricator, and Bitbucket Server. +

+ {/* Video placeholder */} +
+
+

Search shortcut in the URL location bar

+

+ Type src + space in the address bar of your browser to search for queries on Sourcegraph. +

+ {/* Video placeholder */} +
+
+
+
+ +
+
+

Make it work on your codehost

+
+
+
+

+ {' '} + github.com +

+

+ No action required. Your extension works here by + default. +

+
+
+

+
+ {' '} + GitHub Enterprise +
+
+ {' '} + GitLab +
+
+ {' '} + Bitbucket Server +
+
+ {' '} + Phabricator +
+

+

Your extension needs explicit permissions to your code host:

+
    +
  1. Navigate to any page on your code host.
  2. +
  3. + Click the{' '} + + Grant permissions + {' '} + button. +
  4. +
  5. + Click{' '} + + Allow + {' '} + in the permissions request popup. +
  6. +
+
+
+
{/* Video placeholder */}
+
+
+
+ +
+
+

Make it work for private code

+
+
+

By default, the browser extension works only for public code.

+
+
+ +
+

+ To use the browser extension with your private repositories, you need to set up a{' '} + private Sourcegraph instance and connect the extension to it. +

+
+
+

Follow these instructions:

+
    +
  1. + Install Sourcegraph ( + + visit our docs for instructions + + ). Skip this step if you already have a private Sourcegraph instance. +
  2. +
  3. + Click the Sourcegraph extension icon in the browser toolbar to{' '} + + open the settings page + + . +
  4. +
  5. + Enter the URL (including the protocol) of your Sourcegraph instance + (such as https://sourcegraph.example.com). +
  6. +
  7. Make sure a green checkmark appears in the input field.
  8. +
+
+
+
{/* Video placeholder */}
+
+
+
+ +
+
+

Additional resources

+
+
+ +
+

+ Read the{' '} + + Sourcegraph Documentation + {' '} + to learn more about how we respect your privacy, troubleshooting and extension features. +

+
+
+
+
+) diff --git a/client/browser/src/browser-extension/manifest.spec.json b/client/browser/src/browser-extension/manifest.spec.json index 6dc3b01bef7..06469d256a9 100644 --- a/client/browser/src/browser-extension/manifest.spec.json +++ b/client/browser/src/browser-extension/manifest.spec.json @@ -29,7 +29,7 @@ }, "optional_permissions": ["tabs", "http://*/*", "https://*/*"], "content_security_policy": "script-src 'self' blob:; object-src 'self'", - "web_accessible_resources": ["img/*", "css/style.bundle.css", "css/options-style.bundle.css"], + "web_accessible_resources": ["img/*", "css/style.bundle.css", "css/branded-style.bundle.css"], "omnibox": { "keyword": "src" }, diff --git a/client/browser/src/browser-extension/options-page/OptionsMenu.story.tsx b/client/browser/src/browser-extension/options-page/OptionsMenu.story.tsx index 9bf05448d3b..1067c275ebb 100644 --- a/client/browser/src/browser-extension/options-page/OptionsMenu.story.tsx +++ b/client/browser/src/browser-extension/options-page/OptionsMenu.story.tsx @@ -2,11 +2,11 @@ import * as React from 'react' import { action } from '@storybook/addon-actions' import { storiesOf } from '@storybook/react' import { OptionsMenu } from './OptionsMenu' -import optionsStyles from '../../options.scss' +import brandedStyles from '../../branded.scss' import { BrandedStory } from '../../../../branded/src/components/BrandedStory' storiesOf('browser/Options/OptionsMenu', module) - .addDecorator(story => {() => story()}) + .addDecorator(story => {() => story()}) .add('Default', () => ( { public state = { value: 'https://sourcegraph.com', status: 'connected' as ServerUrlFormProps['status'] } @@ -34,7 +34,7 @@ class Container extends React.Component<{}, { value: string; status: ServerUrlFo } storiesOf('browser/Options/ServerUrlForm', module) - .addDecorator(story => {() => story()}) + .addDecorator(story => {() => story()}) .add('Interactive', () => ) .add('Error Status', () => (
diff --git a/client/browser/src/browser-extension/pages/after_install.html b/client/browser/src/browser-extension/pages/after_install.html new file mode 100644 index 00000000000..b4637ca1efb --- /dev/null +++ b/client/browser/src/browser-extension/pages/after_install.html @@ -0,0 +1,13 @@ + + + + + Sourcegraph browser extension + + + +
+ + + + diff --git a/client/browser/src/browser-extension/pages/options.html b/client/browser/src/browser-extension/pages/options.html index 63904302e96..92c9815411b 100644 --- a/client/browser/src/browser-extension/pages/options.html +++ b/client/browser/src/browser-extension/pages/options.html @@ -3,7 +3,7 @@ Sourcegraph extension - + diff --git a/client/browser/src/browser-extension/scripts/afterInstallPage.main.tsx b/client/browser/src/browser-extension/scripts/afterInstallPage.main.tsx new file mode 100644 index 00000000000..abec47036b2 --- /dev/null +++ b/client/browser/src/browser-extension/scripts/afterInstallPage.main.tsx @@ -0,0 +1,11 @@ +// We want to polyfill first. +import '../../shared/polyfills' + +import React from 'react' +import { render } from 'react-dom' +import { AfterInstallPageContent } from '../after-install-page/AfterInstallPageContent' +import { ThemeWrapper } from '../ThemeWrapper' + +const AfterInstallPage: React.FunctionComponent = () => {() => } + +render(, document.querySelector('#root')) diff --git a/client/browser/src/browser-extension/scripts/backgroundPage.main.ts b/client/browser/src/browser-extension/scripts/backgroundPage.main.ts index 5eaa92a7ad4..f01249d4a79 100644 --- a/client/browser/src/browser-extension/scripts/backgroundPage.main.ts +++ b/client/browser/src/browser-extension/scripts/backgroundPage.main.ts @@ -76,6 +76,15 @@ initializeOmniboxInterface(requestGraphQL) async function main(): Promise { const subscriptions = new Subscription() + // Open installation page after the extension was installed + browser.runtime.onInstalled.addListener(event => { + if (event.reason === 'install') { + browser.tabs.create({ url: browser.extension.getURL('after_install.html') }).catch(error => { + console.error('Error opening after-install page:', error) + }) + } + }) + // Mirror the managed sourcegraphURL to sync storage subscriptions.add( observeStorageKey('managed', 'sourcegraphURL') diff --git a/client/browser/src/options.scss b/client/browser/src/options.scss deleted file mode 100644 index 14138579b60..00000000000 --- a/client/browser/src/options.scss +++ /dev/null @@ -1,17 +0,0 @@ -// Options page CSS entry point - -@import '../../branded/src/global-styles/index.scss'; -@import '../../branded/src/components/Toggle'; -@import './browser-extension/options-page/OptionsContainer'; - -:root { - // Use the same root size as the webapp to maintain consistency - font-size: 16px; -} - -body { - // Use a smaller base size than the default 16px, - // because an options menu should feel more like a - // natural extension of the browser's UI than a webpage - font-size: 14px; -}