diff --git a/src/components/content-tab-settings.tsx b/src/components/content-tab-settings.tsx index 56020180d..e52e68fd9 100644 --- a/src/components/content-tab-settings.tsx +++ b/src/components/content-tab-settings.tsx @@ -4,6 +4,7 @@ import { useGetWpVersion } from '../hooks/use-get-wp-version'; import { decodePassword } from '../lib/passwords'; import { CopyTextButton } from './copy-text-button'; import DeleteSite from './delete-site'; +import EditAbsoluteUrl from './edit-absolute-url'; import EditPhpVersion from './edit-php-version'; import EditSite from './edit-site'; @@ -29,6 +30,7 @@ export function ContentTabSettings( { selectedSite }: ContentTabSettingsProps ) const storedPassword = decodePassword( selectedSite.adminPassword ?? '' ); const password = storedPassword === '' ? 'password' : storedPassword; const wpVersion = useGetWpVersion( selectedSite ); + const url = selectedSite.absoluteUrl || `http://localhost:${ selectedSite.port }`; return (
@@ -44,14 +46,17 @@ export function ContentTabSettings( { selectedSite }: ContentTabSettingsProps ) - - - { `localhost:${ selectedSite.port }` } - + +
+ + { url.replace( /http(s)?:\/\//, '' ) } + + +
diff --git a/src/components/edit-absolute-url.tsx b/src/components/edit-absolute-url.tsx new file mode 100644 index 000000000..f6ce254ca --- /dev/null +++ b/src/components/edit-absolute-url.tsx @@ -0,0 +1,89 @@ +import { useI18n } from '@wordpress/react-i18n'; +import { FormEvent, useCallback, useState } from 'react'; +import { useSiteDetails } from '../hooks/use-site-details'; +import Button from './button'; +import Modal from './modal'; +import TextControlComponent from './text-control'; + +export default function EditAbsoluteUrl() { + const { __ } = useI18n(); + const { updateSite, selectedSite, stopServer, startServer } = useSiteDetails(); + const [ isEditingSite, setIsEditingSite ] = useState( false ); + const [ showEditAbsoluteUrlModal, setShowEditAbsoluteUrlModal ] = useState( false ); + const [ localUrl, setLocalUrl ] = useState( + selectedSite?.absoluteUrl || `http://localhost:${ selectedSite?.port }` + ); + + const onLocalUrlEdit = useCallback( + async ( event: FormEvent ) => { + event.preventDefault(); + if ( ! selectedSite ) { + return; + } + + setIsEditingSite( true ); + await updateSite( { + ...selectedSite, + absoluteUrl: localUrl, + } ); + if ( selectedSite.running ) { + await stopServer( selectedSite.id ); + await startServer( selectedSite.id ); + } + setIsEditingSite( false ); + setShowEditAbsoluteUrlModal( false ); + }, + [ selectedSite, localUrl, updateSite, stopServer, startServer ] + ); + + return ( + <> + { showEditAbsoluteUrlModal && ( + setShowEditAbsoluteUrlModal( false ) } + > +
+ +
+ + +
+ +
+ ) } + + + ); +} diff --git a/src/ipc-types.d.ts b/src/ipc-types.d.ts index a67a68a03..e445a243d 100644 --- a/src/ipc-types.d.ts +++ b/src/ipc-types.d.ts @@ -17,6 +17,7 @@ interface StoppedSiteDetails { id: string; name: string; path: string; + absoluteUrl?: string; port?: number; phpVersion: string; adminPassword?: string; diff --git a/src/site-server.ts b/src/site-server.ts index 102b650e8..7476d23a3 100644 --- a/src/site-server.ts +++ b/src/site-server.ts @@ -88,8 +88,7 @@ export class SiteServer { siteTitle: this.details.name, php: this.details.phpVersion, } ); - const absoluteUrl = `http://localhost:${ port }`; - options.absoluteUrl = absoluteUrl; + options.absoluteUrl = this.details.absoluteUrl || `http://localhost:${ port }`; options.siteLanguage = await getPreferredSiteLanguage( options.wordPressVersion ); if ( options.mode !== WPNowMode.WORDPRESS ) { @@ -124,6 +123,7 @@ export class SiteServer { name: site.name, path: site.path, phpVersion: site.phpVersion, + absoluteUrl: site.absoluteUrl, }; } diff --git a/src/storage/user-data.ts b/src/storage/user-data.ts index bf62df612..22414571e 100644 --- a/src/storage/user-data.ts +++ b/src/storage/user-data.ts @@ -104,29 +104,35 @@ export async function saveUserData( data: UserData ): Promise< void > { function toDiskFormat( { sites, ...rest }: UserData ): PersistedUserData { return { version: 1, - sites: sites.map( ( { id, path, adminPassword, port, phpVersion, name, themeDetails } ) => { - // No object spreading allowed. TypeScript's structural typing is too permissive and - // will permit us to persist properties that aren't in the type definition. - // Add each property explicitly instead. - const persistedSiteDetails: PersistedUserData[ 'sites' ][ number ] = { - id, - name, - path, - adminPassword, - port, - phpVersion, - themeDetails: { - name: themeDetails?.name || '', - path: themeDetails?.path || '', - slug: themeDetails?.slug || '', - isBlockTheme: themeDetails?.isBlockTheme || false, - supportsWidgets: themeDetails?.supportsWidgets || false, - supportsMenus: themeDetails?.supportsMenus || false, - }, - }; + sites: sites.map( + ( { id, path, adminPassword, port, phpVersion, name, themeDetails, absoluteUrl } ) => { + // No object spreading allowed. TypeScript's structural typing is too permissive and + // will permit us to persist properties that aren't in the type definition. + // Add each property explicitly instead. + const persistedSiteDetails: PersistedUserData[ 'sites' ][ number ] = { + id, + name, + path, + adminPassword, + port, + phpVersion, + themeDetails: { + name: themeDetails?.name || '', + path: themeDetails?.path || '', + slug: themeDetails?.slug || '', + isBlockTheme: themeDetails?.isBlockTheme || false, + supportsWidgets: themeDetails?.supportsWidgets || false, + supportsMenus: themeDetails?.supportsMenus || false, + }, + }; - return persistedSiteDetails; - } ), + if ( absoluteUrl ) { + persistedSiteDetails.absoluteUrl = absoluteUrl; + } + + return persistedSiteDetails; + } + ), ...rest, }; }