1- import { CodyIDE , type CodyNotice , isWorkspaceInstance } from '@sourcegraph/cody-shared'
2- import { S2_URL } from '@sourcegraph/cody-shared/src/sourcegraph-api/environments'
3- import {
4- ArrowLeftRightIcon ,
5- ArrowRightIcon ,
6- BuildingIcon ,
7- EyeIcon ,
8- HeartIcon ,
9- XIcon ,
10- } from 'lucide-react'
11- import { type FunctionComponent , type ReactNode , useCallback , useMemo , useState } from 'react'
12- import SourcegraphIcon from '../../resources/sourcegraph-mark.svg'
13- import type { UserAccountInfo } from '../Chat'
14- import { CodyLogo } from '../icons/CodyLogo'
15- import { getVSCodeAPI } from '../utils/VSCodeApi'
1+ import type { CodyNotice } from '@sourcegraph/cody-shared'
2+ import { XIcon } from 'lucide-react'
3+ import { type FunctionComponent , useCallback , useMemo , useState } from 'react'
164import { useTelemetryRecorder } from '../utils/telemetry'
175import { MarkdownFromCody } from './MarkdownFromCody'
186import { useLocalStorage } from './hooks'
@@ -24,42 +12,19 @@ interface Notice {
2412 content : JSX . Element
2513}
2614
27- type NoticeVariants = 'default' | 'warning'
28- type NoticeIDs =
29- | 'DogfoodS2'
30- | 'TeamsUpgrade'
31- | 'DeepCodyDotCom'
32- | 'DeepCodyEnterprise'
33- | 'CodyDeprecation'
34-
3515interface NoticesProps {
36- user : UserAccountInfo
3716 instanceNotices : CodyNotice [ ]
3817}
3918
4019const storageKey = 'DismissedWelcomeNotices'
4120
42- // Cody deprecation message constants
43- const CODY_DEPRECATION_MESSAGES = {
44- ENTERPRISE_STARTER_WITH_CODY :
45- "Cody in Enterprise Starter is being sunset. You can continue using Cody until July 23rd. Your code indexing and code search capabilities will not be affected. We encourage you to try Amp, Sourcegraph's new agentic coding tool." ,
46- ENTERPRISE_STARTER_WITHOUT_CODY :
47- "Cody in Enterprise Starter is being sunset. Because you signed up after 6/25, your account doesn't have Cody, we encourage you to try Amp, Sourcegraph's new agentic coding tool." ,
48- PRO_USER :
49- "Cody Pro is being sunset. You can continue using Cody until July 23rd, and your subscription will not renew. We encourage you to try Amp, Sourcegraph's new agentic coding tool." ,
50- FREE_USER_WITH_CODY :
51- "Cody Free is being sunset. You can continue using Cody until July 23rd. We encourage you to try Amp, Sourcegraph's new agentic coding tool." ,
52- FREE_USER_WITHOUT_CODY :
53- "Cody Free is being sunset. Because you signed up after 6/25, your account doesn't have Cody. We encourage you to try Amp, Sourcegraph's new agentic coding tool." ,
54- } as const
55-
56- export const Notices : React . FC < NoticesProps > = ( { user, instanceNotices } ) => {
21+ export const Notices : React . FC < NoticesProps > = ( { instanceNotices } ) => {
5722 const telemetryRecorder = useTelemetryRecorder ( )
5823
5924 // dismissed notices from local storage
6025 const [ dismissedNotices , setDismissedNotices ] = useLocalStorage ( storageKey , '' )
6126 // session-only dismissal - for notices we want to show if the user logs out and logs back in.
62- const [ sessionDismissedNotices , setSessionDismissedNotices ] = useState < string [ ] > ( [ ] )
27+ const [ _ , setSessionDismissedNotices ] = useState < string [ ] > ( [ ] )
6328
6429 const dismissNotice = useCallback (
6530 ( noticeId : string , type : 'sessional' | 'permanent' = 'permanent' ) => {
@@ -77,28 +42,6 @@ export const Notices: React.FC<NoticesProps> = ({ user, instanceNotices }) => {
7742 [ telemetryRecorder , setDismissedNotices ]
7843 )
7944
80- // Helper function to get the CodyDeprecation message based on user type and site Cody enablement
81- const getCodyDeprecationMessage = useCallback ( ( ) => {
82- if ( isWorkspaceInstance ( user . user . endpoint ) ) {
83- // For workspace instances, check if the site has Cody enabled
84- const hasCodyEnabled = user . siteHasCodyEnabled === true
85- if ( hasCodyEnabled ) {
86- return CODY_DEPRECATION_MESSAGES . ENTERPRISE_STARTER_WITH_CODY
87- }
88- return CODY_DEPRECATION_MESSAGES . ENTERPRISE_STARTER_WITHOUT_CODY
89- }
90- if ( user . isCodyProUser ) {
91- return CODY_DEPRECATION_MESSAGES . PRO_USER
92- }
93- // For free users, check if they have a subscription
94- const hasSubscription =
95- user . currentUserCodySubscription !== null && user . currentUserCodySubscription !== undefined
96- if ( hasSubscription && user . currentUserCodySubscription ?. status !== 'CANCELED' ) {
97- return CODY_DEPRECATION_MESSAGES . FREE_USER_WITH_CODY
98- }
99- return CODY_DEPRECATION_MESSAGES . FREE_USER_WITHOUT_CODY
100- } , [ user ] )
101-
10245 const notices : Notice [ ] = useMemo (
10346 ( ) => [
10447 ...instanceNotices . map ( notice => ( {
@@ -112,86 +55,16 @@ export const Notices: React.FC<NoticesProps> = ({ user, instanceNotices }) => {
11255 />
11356 ) ,
11457 } ) ) ,
115- /**
116- * Cody Deprecation Notice
117- */
118- {
119- id : 'CodyDeprecation' ,
120- isVisible : user . isDotComUser || isWorkspaceInstance ( user . user . endpoint ) ,
121- content : (
122- < NoticeContent
123- id = "CodyDeprecation"
124- variant = "default"
125- title = "Important Notice"
126- message = { getCodyDeprecationMessage ( ) }
127- onDismiss = { ( ) => dismissNotice ( 'CodyDeprecation' , 'sessional' ) }
128- actions = { [
129- {
130- label : 'Try Amp' ,
131- variant : 'default' ,
132- href : 'https://ampcode.com' ,
133- } ,
134- {
135- label : 'Learn more' ,
136- variant : 'ghost' ,
137- href : 'https://sourcegraph.com/blog/changes-to-cody-free-pro-and-enterprise-starter-plans' ,
138- } ,
139- ] }
140- />
141- ) ,
142- } ,
143- /**
144- * For Sourcegraph team members who are using Sourcegraph.com to remind them that we want to be dogfooding S2.
145- */
146- {
147- id : 'DogfoodS2' ,
148- isVisible :
149- user . isDotComUser &&
150- user . user . organizations ?. some ( org => org . name === 'sourcegraph' ) &&
151- user . IDE !== CodyIDE . Web ,
152- content : (
153- < NoticeContent
154- id = "DogfoodS2"
155- variant = "warning"
156- title = ""
157- message = "Sourcegraph team members should use S2 not dotcom (except when testing dotcom-specific behavior) so that we dogfood our enterprise customer experience."
158- onDismiss = { ( ) => dismissNotice ( 'DogfoodS2' , 'sessional' ) }
159- actions = { [
160- {
161- label : 'Switch to S2' ,
162- onClick : ( ) =>
163- getVSCodeAPI ( ) . postMessage ( {
164- command : 'auth' ,
165- authKind : 'switch' ,
166- endpoint : S2_URL . href ,
167- } ) ,
168- variant : 'default' ,
169- icon : < ArrowLeftRightIcon size = { 14 } /> ,
170- iconPosition : 'end' ,
171- } ,
172- {
173- label : 'Dismiss' ,
174- onClick : ( ) => dismissNotice ( 'DogfoodS2' , 'sessional' ) ,
175- variant : 'secondary' ,
176- } ,
177- ] }
178- />
179- ) ,
180- } ,
18158 ] ,
182- [ user , dismissNotice , instanceNotices , getCodyDeprecationMessage ]
59+ [ dismissNotice , instanceNotices ]
18360 )
18461
185- // First, modify the activeNotice useMemo to add conditional logic for DogfoodS2
18662 const activeNotice = useMemo (
18763 ( ) =>
18864 notices . find ( notice => {
189- if ( notice . id === 'DogfoodS2' || notice . id === 'CodyDeprecation' ) {
190- return notice . isVisible && ! sessionDismissedNotices . includes ( notice . id )
191- }
19265 return notice . isVisible && ! dismissedNotices ?. includes ( notice . id )
19366 } ) ,
194- [ dismissedNotices , sessionDismissedNotices , notices ]
67+ [ dismissedNotices , notices ]
19568 )
19669
19770 if ( ! activeNotice ) {
@@ -203,120 +76,6 @@ export const Notices: React.FC<NoticesProps> = ({ user, instanceNotices }) => {
20376 )
20477}
20578
206- interface NoticeContentProps {
207- variant : NoticeVariants
208- id : NoticeIDs
209- title : string
210- message : string
211- actions : Array < {
212- label : string
213- onClick ?: ( ) => void
214- href ?: string
215- variant : 'default' | 'ghost' | 'secondary'
216- icon ?: ReactNode
217- iconPosition ?: 'start' | 'end'
218- } >
219- onDismiss : ( ) => void
220- info ?: string
221- footer ?: string
222- }
223-
224- const NoticeContent : FunctionComponent < NoticeContentProps > = ( {
225- variant,
226- title,
227- message,
228- actions,
229- id,
230- info,
231- footer,
232- onDismiss,
233- } ) => {
234- const telemetryRecorder = useTelemetryRecorder ( )
235-
236- const bgColor = {
237- default : 'tw-bg-accent tw-bg-opacity-50 tw-text-accent-foreground' ,
238- warning : 'tw-bg-red-700 tw-text-white' ,
239- } [ variant ]
240-
241- const header = {
242- DeepCodyDotCom : (
243- < >
244- < CodyLogo size = { 16 } />
245- </ >
246- ) ,
247- DeepCodyEnterprise : (
248- < >
249- < CodyLogo size = { 16 } />
250- </ >
251- ) ,
252- DogfoodS2 : (
253- < >
254- < EyeIcon />
255- < HeartIcon />
256- < BuildingIcon />
257- </ >
258- ) ,
259- TeamsUpgrade : (
260- < >
261- < CodyLogo size = { 16 } />
262- < ArrowRightIcon size = { 16 } />
263- < img src = { SourcegraphIcon } alt = "Sourcegraph Logo" className = "tw-h-[16px]" />
264- </ >
265- ) ,
266- CodyDeprecation : null ,
267- } [ id ]
268-
269- return (
270- < aside
271- className = { `tw-w-full tw-relative tw-rounded-md tw-flex tw-flex-col tw-gap-2 tw-p-4 ${ bgColor } ` }
272- >
273- < div className = "tw-flex tw-gap-3 tw-mb-2" > { header } </ div >
274- { title && < h1 className = "tw-text-lg tw-font-semibold" > { title } </ h1 > }
275- { info && < p className = "tw-mb-2" > ⓘ { info } </ p > }
276- < p > { message } </ p >
277- < div className = "tw-mt-3 tw-flex tw-gap-3" >
278- { actions . map ( ( action , _index ) => (
279- < Button
280- key = { action . label + '-button' }
281- variant = { action . variant }
282- size = "sm"
283- onClick = { ( ) => {
284- action . onClick ?.( )
285- telemetryRecorder . recordEvent ( 'cody.notice.cta' , 'clicked' , {
286- privateMetadata : {
287- noticeId : id ,
288- title : action . label ,
289- } ,
290- } )
291- } }
292- className = "tw-flex tw-gap-1"
293- >
294- { action . iconPosition === 'start' && action . icon }
295- { action . href ? (
296- < a
297- href = { action . href }
298- target = "_blank"
299- rel = "noreferrer"
300- className = "tw-text-inherit hover:!tw-text-inherit"
301- >
302- { action . label }
303- </ a >
304- ) : (
305- < span > { action . label } </ span >
306- ) }
307- { action . iconPosition === 'end' && action . icon }
308- </ Button >
309- ) ) }
310- </ div >
311- { footer && < p className = "tw-mt-2" > { footer } </ p > }
312- { /* Dismiss button. */ }
313- < Button variant = "ghost" onClick = { onDismiss } className = "tw-absolute tw-top-2 tw-right-2" >
314- < XIcon size = "14" />
315- </ Button >
316- </ aside >
317- )
318- }
319-
32079interface MarkdownNotice {
32180 title : string
32281 content : string
0 commit comments