1+ import * as Sentry from '@sentry/electron/renderer' ;
12import { speak } from '@wordpress/a11y' ;
23import { Spinner } from '@wordpress/components' ;
34import { __ , sprintf } from '@wordpress/i18n' ;
45import { useEffect } from 'react' ;
56import { Tooltip } from 'src/components/tooltip' ;
67import { useSyncSites } from 'src/hooks/sync-sites' ;
8+ import { useContentTabs } from 'src/hooks/use-content-tabs' ;
9+ import { useDeleteSite } from 'src/hooks/use-delete-site' ;
710import { useImportExport } from 'src/hooks/use-import-export' ;
811import { useSiteDetails } from 'src/hooks/use-site-details' ;
9- import { isMac } from 'src/lib/app-globals' ;
12+ import { isMac , isWindows } from 'src/lib/app-globals' ;
1013import { cx } from 'src/lib/cx' ;
14+ import { getIpcApi } from 'src/lib/get-ipc-api' ;
15+ import { supportedEditorConfig } from 'src/modules/user-settings/lib/editor' ;
16+ import { getTerminalName } from 'src/modules/user-settings/lib/terminal' ;
17+ import { useGetUserEditorQuery , useGetUserTerminalQuery } from 'src/stores/installed-apps-api' ;
1118
1219interface SiteMenuProps {
1320 className ?: string ;
@@ -108,10 +115,21 @@ function ButtonToRun( { running, id, name }: Pick< SiteDetails, 'running' | 'id'
108115 ) ;
109116}
110117function SiteItem ( { site } : { site : SiteDetails } ) {
111- const { selectedSite, setSelectedSiteId } = useSiteDetails ( ) ;
118+ const {
119+ selectedSite,
120+ setSelectedSiteId,
121+ startServer,
122+ stopServer,
123+ loadingServer,
124+ setIsEditModalOpen,
125+ } = useSiteDetails ( ) ;
126+ const { setSelectedTab } = useContentTabs ( ) ;
127+ const { handleDeleteSite } = useDeleteSite ( ) ;
112128 const isSelected = site === selectedSite ;
113129 const { isSiteImporting, isSiteExporting } = useImportExport ( ) ;
114130 const { isSiteIdPulling } = useSyncSites ( ) ;
131+ const { data : editor } = useGetUserEditorQuery ( ) ;
132+ const { data : terminal } = useGetUserTerminalQuery ( ) ;
115133 const isImporting = isSiteImporting ( site . id ) ;
116134 const isExporting = isSiteExporting ( site . id ) ;
117135 const isPulling = isSiteIdPulling ( site . id ) ;
@@ -128,13 +146,111 @@ function SiteItem( { site }: { site: SiteDetails } ) {
128146 tooltipText = __ ( 'Loading' ) ;
129147 }
130148
149+ const handleContextMenu = ( e : React . MouseEvent ) => {
150+ e . preventDefault ( ) ;
151+ const ipcApi = getIpcApi ( ) ;
152+ const isLoading = loadingServer [ site . id ] || false ;
153+ const isAddingSite = site . isAddingSite || false ;
154+ const finderLabel = isWindows ( ) ? __ ( 'File Explorer' ) : __ ( 'Finder' ) ;
155+ const editorLabel =
156+ editor && supportedEditorConfig [ editor ] ? supportedEditorConfig [ editor ] . label : null ;
157+ const terminalLabel = getTerminalName ( terminal ) ;
158+
159+ ipcApi . showSiteContextMenu ( {
160+ siteId : site . id ,
161+ isRunning : site . running ,
162+ isLoading,
163+ isAddingSite,
164+ finderLabel,
165+ editorLabel,
166+ terminalLabel,
167+ } ) ;
168+ } ;
169+
170+ useEffect ( ( ) => {
171+ const unsubscribe = window . ipcListener . subscribe (
172+ 'site-context-menu-action' ,
173+ async ( _ , data : { action : string ; siteId : string } ) => {
174+ if ( data . siteId === site . id ) {
175+ const ipcApi = getIpcApi ( ) ;
176+ switch ( data . action ) {
177+ case 'start' :
178+ void startServer ( site . id ) ;
179+ break ;
180+ case 'stop' :
181+ void stopServer ( site . id ) ;
182+ break ;
183+ case 'open-site' :
184+ if ( ! site . running ) {
185+ await startServer ( site . id ) ;
186+ }
187+ ipcApi . openSiteURL ( site . id , '' , { autoLogin : false } ) ;
188+ break ;
189+ case 'open-admin' :
190+ if ( ! site . running ) {
191+ await startServer ( site . id ) ;
192+ }
193+ ipcApi . openSiteURL ( site . id , '/wp-admin/' ) ;
194+ break ;
195+ case 'open-finder' :
196+ ipcApi . openLocalPath ( site . path ) ;
197+ break ;
198+ case 'open-editor' :
199+ if ( editor ) {
200+ void ipcApi . openAppAtPath ( editor , site . path ) ;
201+ }
202+ break ;
203+ case 'open-terminal' :
204+ void ( async ( ) => {
205+ try {
206+ await ipcApi . openTerminalAtPath ( site . path ) ;
207+ } catch ( error ) {
208+ Sentry . captureException ( error ) ;
209+ alert ( __ ( 'Could not open the terminal.' ) ) ;
210+ }
211+ } ) ( ) ;
212+ break ;
213+ case 'edit-site' :
214+ if ( site . id !== selectedSite ?. id ) {
215+ setSelectedSiteId ( site . id ) ;
216+ }
217+ setSelectedTab ( 'settings' ) ;
218+ setIsEditModalOpen ( true ) ;
219+ break ;
220+ case 'delete' :
221+ await handleDeleteSite ( site . id , site . name ) ;
222+ break ;
223+ }
224+ }
225+ }
226+ ) ;
227+
228+ return ( ) => {
229+ unsubscribe ?.( ) ;
230+ } ;
231+ } , [
232+ site . id ,
233+ site . name ,
234+ site . path ,
235+ site . running ,
236+ startServer ,
237+ stopServer ,
238+ editor ,
239+ selectedSite ?. id ,
240+ setSelectedTab ,
241+ setIsEditModalOpen ,
242+ setSelectedSiteId ,
243+ handleDeleteSite ,
244+ ] ) ;
245+
131246 return (
132247 < li
133248 className = { cx (
134249 'flex flex-row min-w-[168px] h-8 hover:bg-[#ffffff0C] rounded transition-all ms-1' ,
135250 isMac ( ) ? 'me-5' : 'me-4' ,
136251 isSelected && 'bg-[#ffffff19] hover:bg-[#ffffff19]'
137252 ) }
253+ onContextMenu = { handleContextMenu }
138254 >
139255 < button
140256 className = "p-2 text-xs rounded-tl rounded-bl whitespace-nowrap overflow-hidden text-ellipsis w-full text-left rtl:text-right focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-a8c-blue-50"
0 commit comments