mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 14:51:44 +00:00
Svelte: add welcome introduction when enabling svelte for the first time (#64163)
This implements a welcome dialog and some additional messaging around the beta rollout.
This commit is contained in:
parent
78064ba956
commit
be17da7305
@ -96,6 +96,9 @@ export interface TemporarySettingsSchema {
|
||||
|
||||
/** OpenCodeGraph */
|
||||
'openCodeGraph.annotations.visible': boolean
|
||||
|
||||
'webNext.welcomeOverlay.dismissed': boolean
|
||||
'webNext.departureMessage.dismissed': boolean
|
||||
}
|
||||
|
||||
/**
|
||||
@ -161,6 +164,8 @@ const TEMPORARY_SETTINGS: Record<keyof TemporarySettings, null> = {
|
||||
'simple.search.toggle': null,
|
||||
'cody.onboarding.completed': null,
|
||||
'openCodeGraph.annotations.visible': null,
|
||||
'webNext.welcomeOverlay.dismissed': null,
|
||||
'webNext.departureMessage.dismissed': null,
|
||||
}
|
||||
|
||||
export const TEMPORARY_SETTINGS_KEYS = Object.keys(TEMPORARY_SETTINGS) as readonly (keyof TemporarySettings)[]
|
||||
|
||||
4
client/web-sveltekit/src/auto-imports.d.ts
vendored
4
client/web-sveltekit/src/auto-imports.d.ts
vendored
@ -42,6 +42,7 @@ declare global {
|
||||
const ILucideEye: typeof import('~icons/lucide/eye')['default']
|
||||
const ILucideFile: typeof import('~icons/lucide/file')['default']
|
||||
const ILucideFileCode: typeof import('~icons/lucide/file-code')['default']
|
||||
const ILucideFileDiff: typeof import('~icons/lucide/file-diff')['default']
|
||||
const ILucideFileJson: typeof import('~icons/lucide/file-json')['default']
|
||||
const ILucideFileSearch2: typeof import('~icons/lucide/file-search2')['default']
|
||||
const ILucideFileStack: typeof import('~icons/lucide/file-stack')['default']
|
||||
@ -61,12 +62,14 @@ declare global {
|
||||
const ILucideGitCompareArrows: typeof import('~icons/lucide/git-compare-arrows')['default']
|
||||
const ILucideGitFork: typeof import('~icons/lucide/git-fork')['default']
|
||||
const ILucideGitMerge: typeof import('~icons/lucide/git-merge')['default']
|
||||
const ILucideHelp: typeof import('~icons/lucide/help')['default']
|
||||
const ILucideHistory: typeof import('~icons/lucide/history')['default']
|
||||
const ILucideHome: typeof import('~icons/lucide/home')['default']
|
||||
const ILucideInfo: typeof import('~icons/lucide/info')['default']
|
||||
const ILucideLink: typeof import('~icons/lucide/link')['default']
|
||||
const ILucideLock: typeof import('~icons/lucide/lock')['default']
|
||||
const ILucideMenu: typeof import('~icons/lucide/menu')['default']
|
||||
const ILucideNetwork: typeof import('~icons/lucide/network')['default']
|
||||
const ILucideOctagonX: typeof import('~icons/lucide/octagon-x')['default']
|
||||
const ILucidePanelBottomClose: typeof import('~icons/lucide/panel-bottom-close')['default']
|
||||
const ILucidePanelLeftClose: typeof import('~icons/lucide/panel-left-close')['default']
|
||||
@ -74,6 +77,7 @@ declare global {
|
||||
const ILucidePencil: typeof import('~icons/lucide/pencil')['default']
|
||||
const ILucideRegex: typeof import('~icons/lucide/regex')['default']
|
||||
const ILucideRepeat: typeof import('~icons/lucide/repeat')['default']
|
||||
const ILucideScanSearch: typeof import('~icons/lucide/scan-search')['default']
|
||||
const ILucideSearch: typeof import('~icons/lucide/search')['default']
|
||||
const ILucideSearchX: typeof import('~icons/lucide/search-x')['default']
|
||||
const ILucideSettings: typeof import('~icons/lucide/settings')['default']
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
<script lang="ts" context="module">
|
||||
import { Story } from '@storybook/addon-svelte-csf'
|
||||
|
||||
import FeedbackDialog from './FeedbackDialog.svelte'
|
||||
|
||||
export const meta = {
|
||||
component: FeedbackDialog,
|
||||
}
|
||||
</script>
|
||||
|
||||
<Story name="Default">
|
||||
<FeedbackDialog handleOptOut={() => {}} />
|
||||
</Story>
|
||||
@ -0,0 +1,67 @@
|
||||
<script lang="ts">
|
||||
import { getButtonClassName } from '$lib/wildcard/Button'
|
||||
import Toggle from '$lib/wildcard/Toggle.svelte'
|
||||
|
||||
export let handleOptOut: (() => void) | undefined
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<div class="section header">
|
||||
<h4 class="m0">New, faster UX (Beta)</h4>
|
||||
{#if handleOptOut}
|
||||
<Toggle on={true} on:click={() => handleOptOut()} />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="section">
|
||||
<p class="m0">
|
||||
You're currently on the new, faster Code Search user experience. It's in beta, and is our effort to rebuild
|
||||
the tool from the ground up for performance.
|
||||
</p>
|
||||
</div>
|
||||
<div class="section">
|
||||
<p>Got feedback for us on the beta? We'd love to hear from you.</p>
|
||||
<a
|
||||
class={getButtonClassName({ variant: 'secondary' })}
|
||||
href="https://community.sourcegraph.com/c/code-search/9"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
Leave feedback
|
||||
</a>
|
||||
<p class="small">It only takes two minutes and helps a ton!</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.root {
|
||||
width: 360px;
|
||||
|
||||
.section {
|
||||
padding: 1rem;
|
||||
& + .section {
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
}
|
||||
|
||||
.m0 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
a {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.small {
|
||||
color: var(--text-muted);
|
||||
font-size: var(--font-size-extra-small);
|
||||
text-align: center;
|
||||
margin-top: 0.25rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -21,9 +21,10 @@
|
||||
import SourcegraphLogo from '$lib/SourcegraphLogo.svelte'
|
||||
import { isViewportMediumDown } from '$lib/stores'
|
||||
import { Button } from '$lib/wildcard'
|
||||
import Badge from '$lib/wildcard/Badge.svelte'
|
||||
import Toggle from '$lib/wildcard/Toggle.svelte'
|
||||
import { getButtonClassName } from '$lib/wildcard/Button'
|
||||
import ProductStatusBadge from '$lib/wildcard/ProductStatusBadge.svelte'
|
||||
|
||||
import FeedbackDialog from './FeedbackDialog.svelte'
|
||||
import { GlobalNavigation_User } from './GlobalNavigation.gql'
|
||||
import { type NavigationEntry, type NavigationMenu, isNavigationMenu, isCurrent } from './mainNavigation'
|
||||
import UserMenu from './UserMenu.svelte'
|
||||
@ -123,28 +124,24 @@
|
||||
<div class="global-portal" bind:this={$extensionElement} />
|
||||
|
||||
<div class="web-next-notice">
|
||||
{#if handleOptOut}
|
||||
<Toggle on={true} on:click={() => handleOptOut && handleOptOut()} />
|
||||
{/if}
|
||||
<Popover let:toggle let:registerTrigger>
|
||||
<button class="web-next-badge" use:registerTrigger on:click={() => toggle()}>
|
||||
<Badge variant="warning">Experimental</Badge>
|
||||
<ProductStatusBadge status="beta" />
|
||||
<a
|
||||
class={getButtonClassName({ variant: 'secondary', size: 'sm' })}
|
||||
href="https://community.sourcegraph.com/c/code-search/9"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
Feedback
|
||||
</a>
|
||||
<Popover let:registerTrigger let:toggle placement="bottom-end">
|
||||
<button
|
||||
use:registerTrigger
|
||||
class={getButtonClassName({ variant: 'secondary', size: 'sm' })}
|
||||
on:click={() => toggle()}
|
||||
>
|
||||
<Icon icon={ILucideEllipsis} inline />
|
||||
</button>
|
||||
<div slot="content" class="web-next-content">
|
||||
<h3>Experimental web app</h3>
|
||||
<p>
|
||||
You are using an experimental version of the Sourcegraph web app. This version is under active
|
||||
development and may contain bugs or incomplete features.
|
||||
</p>
|
||||
<p>
|
||||
If you encounter any issues, please report them in our <a href="https://community.sourcegraph.com/"
|
||||
>community forums</a
|
||||
>.
|
||||
</p>
|
||||
{#if handleOptOut}
|
||||
<p>You can opt out of the new experience with the toggle above.</p>
|
||||
{/if}
|
||||
</div>
|
||||
<FeedbackDialog slot="content" {handleOptOut} />
|
||||
</Popover>
|
||||
</div>
|
||||
<div>
|
||||
@ -413,35 +410,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Opt out experiment badge and tooltip styles
|
||||
.opt-out {
|
||||
all: unset;
|
||||
cursor: pointer;
|
||||
color: var(--link-color);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.web-next-notice {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.web-next-badge {
|
||||
all: unset;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.web-next-content {
|
||||
padding: 1rem;
|
||||
width: 20rem;
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
font-size: var(--font-size-small);
|
||||
font-weight: 500;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
// Custom menu with sidebar navigation controls styles
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
// In addition to the props explicitly listed here, this component also
|
||||
// accepts any HTMLButton attributes. Note that those will only be used when
|
||||
// the default implementation is used.
|
||||
|
||||
import type { HTMLButtonAttributes } from 'svelte/elements'
|
||||
|
||||
import { type BUTTON_DISPLAY, type BUTTON_SIZES, type BUTTON_VARIANTS, getButtonClassName } from './Button'
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
import { isRouteEnabled } from '$lib/navigation'
|
||||
|
||||
import type { LayoutData } from './$types'
|
||||
import WelcomeOverlay from './WelcomeOverlay.svelte'
|
||||
|
||||
export let data: LayoutData
|
||||
|
||||
@ -81,12 +82,17 @@
|
||||
$: currentUserID = data.user?.id
|
||||
$: handleOptOut = currentUserID
|
||||
? async (): Promise<void> => {
|
||||
if (currentUserID) {
|
||||
await data.disableSvelteFeatureFlags(currentUserID)
|
||||
window.location.reload()
|
||||
}
|
||||
// Show departure message after switching off
|
||||
$temporarySettingsStorage.set('webNext.departureMessage.dismissed', false)
|
||||
await data.disableSvelteFeatureFlags(currentUserID)
|
||||
window.location.reload()
|
||||
}
|
||||
: undefined
|
||||
|
||||
$: welcomeOverlayDismissed = $temporarySettingsStorage.get('webNext.welcomeOverlay.dismissed', false)
|
||||
function handleDismissWelcomeOverlay() {
|
||||
$temporarySettingsStorage.set('webNext.welcomeOverlay.dismissed', true)
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@ -105,6 +111,11 @@
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
<WelcomeOverlay
|
||||
show={(process.env.PW_TEST === 'true' && !$welcomeOverlayDismissed) ?? false}
|
||||
handleDismiss={handleDismissWelcomeOverlay}
|
||||
/>
|
||||
|
||||
<FuzzyFinderContainer />
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
211
client/web-sveltekit/src/routes/WelcomeOverlay.svelte
Normal file
211
client/web-sveltekit/src/routes/WelcomeOverlay.svelte
Normal file
@ -0,0 +1,211 @@
|
||||
<script lang="ts">
|
||||
import { allHotkey } from '$lib/fuzzyfinder/keys'
|
||||
import Icon from '$lib/Icon.svelte'
|
||||
import KeyboardShortcut from '$lib/KeyboardShortcut.svelte'
|
||||
import { isLightTheme } from '$lib/theme'
|
||||
import Button from '$lib/wildcard/Button.svelte'
|
||||
import ProductStatusBadge from '$lib/wildcard/ProductStatusBadge.svelte'
|
||||
|
||||
import WelcomeOverlayScreenshotDark from './WelcomeOverlayScreenshotDark.svelte'
|
||||
import WelcomeOverlayScreenshotLight from './WelcomeOverlayScreenshotLight.svelte'
|
||||
|
||||
export let show: boolean
|
||||
export let handleDismiss: () => void
|
||||
|
||||
let root: HTMLDialogElement
|
||||
$: if (show) {
|
||||
root?.showModal()
|
||||
} else {
|
||||
root?.close()
|
||||
}
|
||||
</script>
|
||||
|
||||
<dialog bind:this={root}>
|
||||
<div class="content">
|
||||
<div class="logo"><Icon icon={ISgMark} /><ProductStatusBadge status="beta" /></div>
|
||||
<div class="message">
|
||||
<h1><span>You've activated a better, faster experience</span> ⚡</h1>
|
||||
<p class="subtitle">
|
||||
Get ready for a new Code Search experience: rewritten from the ground-up for performance to empower your
|
||||
workflow.
|
||||
</p>
|
||||
</div>
|
||||
<div class="features">
|
||||
<div>
|
||||
<Icon icon={ILucideFileDiff} />
|
||||
<h5>New in-line diff view</h5>
|
||||
<p>Easily compare commits and see how a file changed over time, all in-line</p>
|
||||
</div>
|
||||
<div>
|
||||
<Icon icon={ILucideNetwork} />
|
||||
<h5>Revamped code navigation</h5>
|
||||
<p>Quickly find a list of references of a given symbol, or immediately jump to the definition</p>
|
||||
</div>
|
||||
<div>
|
||||
<Icon icon={ILucideScanSearch} />
|
||||
<h5>Reworked fuzzy finder <KeyboardShortcut shortcut={allHotkey} /></h5>
|
||||
<p>Find files and symbols quickly and easily with our whole new fuzzy finder.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cta">
|
||||
<div>
|
||||
<Button variant="secondary" on:click={() => handleDismiss()}>Awesome. I’m ready to use it!</Button>
|
||||
<!-- <a href="TODO">Read release notes</a> -->
|
||||
</div>
|
||||
<p> You can opt out at any time by using the toggle at the top of the screen. </p>
|
||||
<p>
|
||||
Whilst exploring the new experience, consider leaving us some feedback via the button at the top. We'd
|
||||
love to hear from you!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{#if $isLightTheme}
|
||||
<WelcomeOverlayScreenshotLight />
|
||||
{:else}
|
||||
<WelcomeOverlayScreenshotDark />
|
||||
{/if}
|
||||
</dialog>
|
||||
|
||||
<style lang="scss">
|
||||
dialog {
|
||||
width: 80vw;
|
||||
border-radius: 0.75rem;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 2rem;
|
||||
overflow: hidden;
|
||||
background-color: var(--color-bg-1);
|
||||
|
||||
box-shadow: var(--fuzzy-finder-shadow);
|
||||
|
||||
&::backdrop {
|
||||
opacity: 0.48;
|
||||
:global(.theme-light) & {
|
||||
background: var(--color-background, #f9fafb);
|
||||
}
|
||||
:global(.theme-dark) & {
|
||||
background: var(--color-background, #f9fafb);
|
||||
}
|
||||
}
|
||||
|
||||
container-type: inline-size;
|
||||
|
||||
@media (--mobile) {
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
max-height: 100vh;
|
||||
max-width: 100vw;
|
||||
}
|
||||
|
||||
> :global(svg) {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
filter: drop-shadow(0px 25px 50px rgba(15, 17, 26, 0.25));
|
||||
@container (width < 975px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
// TODO: import this from shadcn color library (once it exists)
|
||||
:global(.theme-light) & {
|
||||
--color-text-subtle: var(--text-body);
|
||||
}
|
||||
:global(.theme-dark) & {
|
||||
--color-text-subtle: #a6b6d9;
|
||||
}
|
||||
|
||||
width: calc(100% - 350px);
|
||||
@container (width < 975px) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-direction: column;
|
||||
|
||||
.logo {
|
||||
--icon-color: initial;
|
||||
--icon-size: 32px;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.message {
|
||||
h1 {
|
||||
text-wrap: balance;
|
||||
span {
|
||||
background: linear-gradient(90deg, #00cbec 0%, #a112ff 48.53%, #ff5543 97.06%);
|
||||
color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin: 0;
|
||||
font-size: var(--font-size-large);
|
||||
font-weight: 500;
|
||||
color: var(--color-text-subtle);
|
||||
}
|
||||
|
||||
.features {
|
||||
display: grid;
|
||||
max-width: 700px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 1rem 0.75rem;
|
||||
padding: 1rem 0;
|
||||
|
||||
> div {
|
||||
display: grid;
|
||||
grid-template-columns: min-content auto;
|
||||
gap: 0.25rem 0.75rem;
|
||||
:global([data-icon]) {
|
||||
--icon-size: 20px;
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
h5 {
|
||||
all: unset;
|
||||
font-weight: 600;
|
||||
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
}
|
||||
p {
|
||||
all: unset;
|
||||
font-size: var(--font-size-small);
|
||||
font-weight: 400;
|
||||
color: var(--color-text-subtle);
|
||||
|
||||
grid-column: 2;
|
||||
grid-row: 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cta {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-direction: column;
|
||||
div {
|
||||
grid-column: 1 / -1;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
p {
|
||||
grid-column: 1 / -1;
|
||||
color: var(--text-muted);
|
||||
font-size: var(--font-size-small);
|
||||
font-weight: 400;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 1.5 MiB |
2550
client/web-sveltekit/src/routes/WelcomeOverlayScreenshotLight.svelte
Normal file
2550
client/web-sveltekit/src/routes/WelcomeOverlayScreenshotLight.svelte
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 1.5 MiB |
@ -10,14 +10,6 @@ test('has sign in button', async ({ page }) => {
|
||||
await expect(page).toHaveURL('/sign-in')
|
||||
})
|
||||
|
||||
test('has experimental opt out popover', async ({ sg, page }) => {
|
||||
sg.signIn({ username: 'test' })
|
||||
|
||||
await page.goto('/')
|
||||
await page.getByText('Experimental').click()
|
||||
await expect(page.getByText('opt out')).toBeVisible()
|
||||
})
|
||||
|
||||
test('has user menu', async ({ sg, page }) => {
|
||||
sg.signIn({ username: 'test' })
|
||||
const userMenu = page.getByLabel('Open user menu')
|
||||
|
||||
@ -2,6 +2,11 @@
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
font-size: 0.875rem;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.toggle {
|
||||
@ -13,10 +18,39 @@
|
||||
}
|
||||
|
||||
.popover {
|
||||
padding: 1rem;
|
||||
width: 20rem;
|
||||
width: 24rem;
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
h3 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
.section {
|
||||
padding: 1rem 1.25rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
+ .section {
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
}
|
||||
|
||||
.small {
|
||||
text-align: center;
|
||||
font-size: var(--font-size-small);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.help-icon {
|
||||
color: var(--icon-color);
|
||||
}
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import { FC } from 'react'
|
||||
import { FC, useRef, useEffect, useCallback } from 'react'
|
||||
|
||||
import { useApolloClient } from '@apollo/client'
|
||||
import { mdiHelpCircleOutline, mdiClose } from '@mdi/js'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import { Toggle } from '@sourcegraph/branded/src/components/Toggle'
|
||||
import { Text, H3, Popover, PopoverTrigger, PopoverContent, Badge } from '@sourcegraph/wildcard'
|
||||
import { useTemporarySetting } from '@sourcegraph/shared/src/settings/temporary'
|
||||
import { Text, H3, Popover, PopoverTrigger, PopoverContent, Icon, Button } from '@sourcegraph/wildcard'
|
||||
|
||||
import { enableSvelteAndReload, canEnableSvelteKit } from './util'
|
||||
|
||||
@ -13,28 +15,91 @@ import styles from './SvelteKitNavItem.module.scss'
|
||||
export const SvelteKitNavItem: FC<{ userID?: string }> = ({ userID }) => {
|
||||
const location = useLocation()
|
||||
const client = useApolloClient()
|
||||
const [departureDismissed, setDepartureDismissed] = useTemporarySetting('webNext.departureMessage.dismissed', false)
|
||||
const [_welcomeDismissed, setWelcomeDismissed] = useTemporarySetting('webNext.welcomeOverlay.dismissed', false)
|
||||
|
||||
const departureRef = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
const handleClickOutside = useCallback(
|
||||
(event: MouseEvent) => {
|
||||
if (departureRef.current && !departureRef.current.contains(event.target as Node)) {
|
||||
setDepartureDismissed(true)
|
||||
}
|
||||
},
|
||||
[departureRef, setDepartureDismissed]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('click', handleClickOutside)
|
||||
return () => {
|
||||
document.removeEventListener('click', handleClickOutside)
|
||||
}
|
||||
}, [handleClickOutside])
|
||||
|
||||
if (!userID || !canEnableSvelteKit(location.pathname)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const showDeparture = !departureDismissed
|
||||
const popoverProps = showDeparture ? { isOpen: true, onOpenChange: () => {} } : {}
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Toggle
|
||||
value={false}
|
||||
onToggle={() => enableSvelteAndReload(client, userID)}
|
||||
title="Go to experimental web app"
|
||||
className={styles.toggle}
|
||||
/>
|
||||
<Popover>
|
||||
<PopoverTrigger className={styles.badge}>
|
||||
<Badge variant="warning">Try the new experience</Badge>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className={styles.popover}>
|
||||
<H3>Sourcegraph is getting a refresh!</H3>
|
||||
<Text>Try it out early with the toggle above.</Text>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
<Popover {...popoverProps}>
|
||||
<PopoverTrigger className={styles.badge}>
|
||||
<div className={styles.container}>
|
||||
<Icon className={styles.helpIcon} svgPath={mdiHelpCircleOutline} aria-hidden={true} />
|
||||
<Text>New, faster UX</Text>
|
||||
<Toggle
|
||||
value={false}
|
||||
onToggle={() => {
|
||||
setWelcomeDismissed(false) // Show welcome after switching on
|
||||
enableSvelteAndReload(client, userID)
|
||||
}}
|
||||
title="Enable new, faster UX"
|
||||
className={styles.toggle}
|
||||
/>
|
||||
</div>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className={styles.popover} position="bottomEnd">
|
||||
{showDeparture ? (
|
||||
<div ref={departureRef}>
|
||||
<div className={styles.section}>
|
||||
<H3>
|
||||
<span>Switched out of the new experience?</span>
|
||||
<Button variant="icon" onClick={() => setDepartureDismissed(true)}>
|
||||
<Icon svgPath={mdiClose} inline={true} aria-label="close" />
|
||||
</Button>
|
||||
</H3>
|
||||
<Text>
|
||||
Remember, you can always switch back using the toggle above. We're still working on it,
|
||||
so check back soon.
|
||||
</Text>
|
||||
</div>
|
||||
<div className={styles.section}>
|
||||
<Text>Got feedback for us on the beta? We’d love to hear from you.</Text>
|
||||
<Button
|
||||
as="a"
|
||||
variant="secondary"
|
||||
href="https://community.sourcegraph.com/c/code-search/9"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
Leave feedback
|
||||
</Button>
|
||||
<Text className={styles.small}>It only takes two minutes and helps a ton!</Text>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.section}>
|
||||
<H3>What's this "New, faster UX"?</H3>
|
||||
<Text>
|
||||
We've been busy at work on a new Code Search experience, built from the ground up for
|
||||
performance, which is now available in beta.
|
||||
</Text>
|
||||
<Text>Simply activate the toggle to get it.</Text>
|
||||
</div>
|
||||
)}
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user