Add after install page to browser extension (#14407)

This commit is contained in:
Felix Becker 2020-10-09 18:57:22 +02:00 committed by GitHub
parent 2e2becfd20
commit cf65dec202
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 288 additions and 41 deletions

View File

@ -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/**

View File

@ -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});
}
}

View File

@ -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};

View File

@ -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';

View File

@ -50,12 +50,6 @@
}
}
strong {
.theme-dark & {
color: $gray-11;
}
}
.search-keyword {
color: #329af0;

View File

@ -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'),

View File

@ -43,6 +43,9 @@
},
{
"path": "./build/dist/css/style.bundle.css"
},
{
"path": "./build/dist/css/branded-style.bundle.css"
}
]
}

View File

@ -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)
}
/**

View File

@ -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';

View File

@ -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 <Children isLightTheme={isLightTheme} />
}

View File

@ -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;
}
}

View File

@ -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', () => (
<BrandedStory styles={brandedStyles}>{() => <AfterInstallPageContent />}</BrandedStory>
))

View File

@ -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 = () => (
<div className="web-content after-install-page-content">
<SourcegraphIcon className="after-install-page-content__sourcegraph-logo" />
<div className="container">
<h1 className="mt-5">🎉 Youve just installed the Sourcegraph browser extension!</h1>
<p className="lead mb-0">Weve gathered the most important information that will get your started:</p>
</div>
<section className="border-bottom py-5">
<div className="container">
<h2 className="mb-4">How do I use the extension?</h2>
<div className="row">
<div className="col-sm-6">
<h3>Code intelligence on your code host</h3>
<p className="m-0">
Sourcegraph browser extension adds code intelligence to files and diffs on GitHub, GitHub
Enterprise, GitLab, Phabricator, and Bitbucket Server.
</p>
{/* Video placeholder */}
</div>
<div className="col-sm-6">
<h3>Search shortcut in the URL location bar</h3>
<p className="m-0">
Type <code>src</code>
<kbd>space</kbd> in the address bar of your browser to search for queries on Sourcegraph.
</p>
{/* Video placeholder */}
</div>
</div>
</div>
</section>
<section className="border-bottom py-5">
<div className="container">
<h2 className="mb-4">Make it work on your codehost</h2>
<div className="row">
<div className="col-sm-6">
<div className="bg-2 rounded p-3 mb-3">
<h3 className="mb-3 after-install-page-content__code-host-titles">
<GithubIcon className="icon-inline after-install-page-content__code-host-logo" />{' '}
github.com
</h3>
<p className="m-0">
<CheckIcon className="icon-inline" /> No action required. Your extension works here by
default.
</p>
</div>
<div className="bg-2 rounded p-3">
<h3 className="d-flex flex-wrap after-install-page-content__code-host-titles">
<div className="mr-5 mb-3">
<GithubIcon className="icon-inline after-install-page-content__code-host-logo" />{' '}
GitHub Enterprise
</div>
<div className="mr-5 mb-3">
<GitlabIcon className="icon-inline after-install-page-content__code-host-logo" />{' '}
GitLab
</div>
<div className="mr-5 mb-3">
<BitbucketIcon className="icon-inline after-install-page-content__code-host-logo" />{' '}
Bitbucket Server
</div>
<div className="mr-5 mb-3">
<PhabricatorIcon className="icon-inline after-install-page-content__code-host-logo" />{' '}
Phabricator
</div>
</h3>
<p>Your extension needs explicit permissions to your code host:</p>
<ol className="m-0">
<li>Navigate to any page on your code host.</li>
<li>
Click the{' '}
<q>
<strong>Grant permissions</strong>
</q>{' '}
button.
</li>
<li>
Click{' '}
<q>
<strong>Allow</strong>
</q>{' '}
in the permissions request popup.
</li>
</ol>
</div>
</div>
<div className="col-sm-6">{/* Video placeholder */}</div>
</div>
</div>
</section>
<section className="border-bottom py-5">
<div className="container">
<h2 className="mb-4">Make it work for private code</h2>
<div className="row">
<div className="col-sm-6">
<p>By default, the browser extension works only for public code.</p>
<div className="d-flex w-100 align-items-center">
<div className="bg-3 rounded-circle p-2">
<LockIcon className="icon-inline" />
</div>
<p className="flex-grow-1 m-0 ml-3">
To use the browser extension with your private repositories, you need to set up a{' '}
<strong>private Sourcegraph instance</strong> and connect the extension to it.
</p>
</div>
<div className="bg-2 rounded p-3 mt-4">
<p>Follow these instructions:</p>
<ol className="m-0">
<li>
<strong>Install Sourcegraph</strong> (
<a href="https://docs.sourcegraph.com/admin/install" target="_blank" rel="noopener">
visit our docs for instructions
</a>
). Skip this step if you already have a private Sourcegraph instance.
</li>
<li>
Click the Sourcegraph extension icon in the browser toolbar to{' '}
<a href="./options.html" rel="noopener" target="_blank">
open the settings page
</a>
.
</li>
<li>
Enter the <strong>URL</strong> (including the protocol) of your Sourcegraph instance
(such as <q>https://sourcegraph.example.com</q>).
</li>
<li>Make sure a green checkmark appears in the input field.</li>
</ol>
</div>
</div>
<div className="col-sm-6">{/* Video placeholder */}</div>
</div>
</div>
</section>
<section className="py-5">
<div className="container">
<h2 className="mb-4">Additional resources</h2>
<div className="d-flex w-100 align-items-center">
<div className="bg-3 rounded-circle p-2">
<BookOpenPageVariantIcon className="icon-inline" />
</div>
<p className="flex-grow-1 m-0 ml-3">
Read the{' '}
<a
href="https://docs.sourcegraph.com/integration/browser_extension"
rel="noopener"
target="_blank"
>
Sourcegraph Documentation
</a>{' '}
to learn more about how we respect your privacy, troubleshooting and extension features.
</p>
</div>
</div>
</section>
</div>
)

View File

@ -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"
},

View File

@ -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 => <BrandedStory styles={optionsStyles}>{() => story()}</BrandedStory>)
.addDecorator(story => <BrandedStory styles={brandedStyles}>{() => story()}</BrandedStory>)
.add('Default', () => (
<OptionsMenu
version="0.0.0"

View File

@ -3,7 +3,7 @@ import { action } from '@storybook/addon-actions'
import { storiesOf } from '@storybook/react'
import { ConnectionErrors, ServerUrlForm, ServerUrlFormProps } from './ServerUrlForm'
import { BrandedStory } from '../../../../branded/src/components/BrandedStory'
import optionsStyles from '../../options.scss'
import brandedStyles from '../../branded.scss'
class Container extends React.Component<{}, { value: string; status: ServerUrlFormProps['status'] }> {
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 => <BrandedStory styles={optionsStyles}>{() => story()}</BrandedStory>)
.addDecorator(story => <BrandedStory styles={brandedStyles}>{() => story()}</BrandedStory>)
.add('Interactive', () => <Container />)
.add('Error Status', () => (
<div style={{ maxWidth: 400, padding: '1rem' }}>

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<title>Sourcegraph browser extension</title>
<link href="./css/branded-style.bundle.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="root"></div>
<noscript>You must enable JavaScript for the Sourcegraph browser extension to work.</noscript>
<script type="text/javascript" src="./js/after-install.bundle.js"></script>
</body>
</html>

View File

@ -3,7 +3,7 @@
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<title>Sourcegraph extension</title>
<link href="./css/options-style.bundle.css" rel="stylesheet" type="text/css" />
<link href="./css/branded-style.bundle.css" rel="stylesheet" type="text/css" />
</head>
<body>
<script type="text/javascript" src="./js/options.bundle.js"></script>

View File

@ -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 = () => <ThemeWrapper>{() => <AfterInstallPageContent />}</ThemeWrapper>
render(<AfterInstallPage />, document.querySelector('#root'))

View File

@ -76,6 +76,15 @@ initializeOmniboxInterface(requestGraphQL)
async function main(): Promise<void> {
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')

View File

@ -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;
}