Fixed official theme image assets not loading in adminX

refs https://github.com/TryGhost/Team/issues/3432

The admin assets url are modified by ember build process by adding cache busting, so the original urls can't be used directly in adminX. This change passes the image assets from ember admin, causing the right image urls to be passed to adminX and for loading images.
This commit is contained in:
Rishabh 2023-06-16 17:04:26 +05:30
parent 77e3b3e947
commit eeabce2473
5 changed files with 143 additions and 128 deletions

View File

@ -4,16 +4,18 @@ import Heading from './admin-x-ds/global/Heading';
import NiceModal from '@ebay/nice-modal-react';
import Settings from './components/Settings';
import Sidebar from './components/Sidebar';
import {OfficialTheme} from './models/themes';
import {ServicesProvider} from './components/providers/ServiceProvider';
import {Toaster} from 'react-hot-toast';
interface AppProps {
ghostVersion: string;
officialThemes: OfficialTheme[]
}
function App({ghostVersion}: AppProps) {
function App({ghostVersion, officialThemes}: AppProps) {
return (
<ServicesProvider ghostVersion={ghostVersion}>
<ServicesProvider ghostVersion={ghostVersion} officialThemes={officialThemes}>
<DataProvider>
<div className="admin-x-settings">
<Toaster />

View File

@ -1,25 +1,29 @@
import React, {createContext, useContext, useMemo} from 'react';
import setupGhostApi from '../../utils/api';
import {OfficialTheme} from '../../models/themes';
export interface FileService {
uploadImage: (file: File) => Promise<string>;
}
interface ServicesContextProps {
api: ReturnType<typeof setupGhostApi>;
fileService: FileService|null
fileService: FileService|null;
officialThemes: OfficialTheme[];
}
interface ServicesProviderProps {
children: React.ReactNode;
ghostVersion: string;
officialThemes: OfficialTheme[];
}
const ServicesContext = createContext<ServicesContextProps>({
api: setupGhostApi({ghostVersion: ''}),
fileService: null
fileService: null,
officialThemes: []
});
const ServicesProvider: React.FC<ServicesProviderProps> = ({children, ghostVersion}) => {
const ServicesProvider: React.FC<ServicesProviderProps> = ({children, ghostVersion, officialThemes}) => {
const apiService = useMemo(() => setupGhostApi({ghostVersion}), [ghostVersion]);
const fileService = useMemo(() => ({
uploadImage: async (file: File): Promise<string> => {
@ -31,7 +35,8 @@ const ServicesProvider: React.FC<ServicesProviderProps> = ({children, ghostVersi
return (
<ServicesContext.Provider value={{
api: apiService,
fileService
fileService,
officialThemes
}}>
{children}
</ServicesContext.Provider>
@ -40,6 +45,8 @@ const ServicesProvider: React.FC<ServicesProviderProps> = ({children, ghostVersi
export {ServicesContext, ServicesProvider};
export const useServices = () => useContext(ServicesContext)!;
export const useServices = () => useContext(ServicesContext);
export const useApi = () => useServices().api;
export const useOfficialThemes = () => useServices().officialThemes;

View File

@ -2,132 +2,15 @@ import Heading from '../../../../admin-x-ds/global/Heading';
import React from 'react';
import {OfficialTheme} from '../../../../models/themes';
import {getGhostPaths} from '../../../../utils/helpers';
import {useOfficialThemes} from '../../../providers/ServiceProvider';
const OfficialThemes: React.FC<{
onSelectTheme?: (theme: OfficialTheme) => void;
}> = ({
onSelectTheme
}) => {
const {assetRoot} = getGhostPaths();
const officialThemes: OfficialTheme[] = [{
name: 'Casper',
category: 'Blog',
previewUrl: 'https://demo.ghost.io/',
ref: 'default',
image: 'img/themes/Casper.png'
}, {
name: 'Headline',
category: 'News',
url: 'https://github.com/TryGhost/Headline',
previewUrl: 'https://headline.ghost.io',
ref: 'TryGhost/Headline',
image: 'img/themes/Headline.png'
}, {
name: 'Edition',
category: 'Newsletter',
url: 'https://github.com/TryGhost/Edition',
previewUrl: 'https://edition.ghost.io/',
ref: 'TryGhost/Edition',
image: 'img/themes/Edition.png'
}, {
name: 'Solo',
category: 'Blog',
url: 'https://github.com/TryGhost/Solo',
previewUrl: 'https://solo.ghost.io',
ref: 'TryGhost/Solo',
image: 'img/themes/Solo.png'
}, {
name: 'Taste',
category: 'Blog',
url: 'https://github.com/TryGhost/Taste',
previewUrl: 'https://taste.ghost.io',
ref: 'TryGhost/Taste',
image: 'img/themes/Taste.png'
}, {
name: 'Episode',
category: 'Podcast',
url: 'https://github.com/TryGhost/Episode',
previewUrl: 'https://episode.ghost.io',
ref: 'TryGhost/Episode',
image: 'img/themes/Episode.png'
}, {
name: 'Digest',
category: 'Newsletter',
url: 'https://github.com/TryGhost/Digest',
previewUrl: 'https://digest.ghost.io/',
ref: 'TryGhost/Digest',
image: 'img/themes/Digest.png'
}, {
name: 'Bulletin',
category: 'Newsletter',
url: 'https://github.com/TryGhost/Bulletin',
previewUrl: 'https://bulletin.ghost.io/',
ref: 'TryGhost/Bulletin',
image: 'img/themes/Bulletin.png'
}, {
name: 'Alto',
category: 'Blog',
url: 'https://github.com/TryGhost/Alto',
previewUrl: 'https://alto.ghost.io',
ref: 'TryGhost/Alto',
image: 'img/themes/Alto.png'
}, {
name: 'Dope',
category: 'Magazine',
url: 'https://github.com/TryGhost/Dope',
previewUrl: 'https://dope.ghost.io',
ref: 'TryGhost/Dope',
image: 'img/themes/Dope.png'
}, {
name: 'Wave',
category: 'Podcast',
url: 'https://github.com/TryGhost/Wave',
previewUrl: 'https://wave.ghost.io',
ref: 'TryGhost/Wave',
image: 'img/themes/Wave.png'
}, {
name: 'Edge',
category: 'Photography',
url: 'https://github.com/TryGhost/Edge',
previewUrl: 'https://edge.ghost.io',
ref: 'TryGhost/Edge',
image: 'img/themes/Edge.png'
}, {
name: 'Dawn',
category: 'Newsletter',
url: 'https://github.com/TryGhost/Dawn',
previewUrl: 'https://dawn.ghost.io/',
ref: 'TryGhost/Dawn',
image: 'img/themes/Dawn.png'
}, {
name: 'Ease',
category: 'Documentation',
url: 'https://github.com/TryGhost/Ease',
previewUrl: 'https://ease.ghost.io',
ref: 'TryGhost/Ease',
image: 'img/themes/Ease.png'
}, {
name: 'Ruby',
category: 'Magazine',
url: 'https://github.com/TryGhost/Ruby',
previewUrl: 'https://ruby.ghost.io',
ref: 'TryGhost/Ruby',
image: 'img/themes/Ruby.png'
}, {
name: 'London',
category: 'Photography',
url: 'https://github.com/TryGhost/London',
previewUrl: 'https://london.ghost.io',
ref: 'TryGhost/London',
image: 'img/themes/London.png'
}, {
name: 'Journal',
category: 'Newsletter',
url: 'https://github.com/TryGhost/Journal',
previewUrl: 'https://journal.ghost.io/',
ref: 'TryGhost/Journal',
image: 'img/themes/Journal.png'
}];
const {adminRoot} = getGhostPaths();
const officialThemes = useOfficialThemes();
return (
<div className='h-[calc(100vh-74px-40px)] overflow-y-auto overflow-x-hidden p-[8vmin] pt-5'>
@ -143,7 +26,7 @@ const OfficialThemes: React.FC<{
<img
alt="Headline Theme"
className='h-full w-full object-contain'
src={`${assetRoot}/${theme.image}`}
src={`${adminRoot}${theme.image}`}
/>
</div>
<div className='mt-3'>

View File

@ -7,6 +7,7 @@ ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<App
ghostVersion='5.x'
officialThemes={[]}
/>
</React.StrictMode>
);

View File

@ -6,6 +6,127 @@ import {action} from '@ember/object';
import {inject} from 'ghost-admin/decorators/inject';
import {inject as service} from '@ember/service';
// TODO: Long term move asset management directly in AdminX
const officialThemes = [{
name: 'Casper',
category: 'Blog',
previewUrl: 'https://demo.ghost.io/',
ref: 'default',
image: 'assets/img/themes/Casper.png'
}, {
name: 'Headline',
category: 'News',
url: 'https://github.com/TryGhost/Headline',
previewUrl: 'https://headline.ghost.io',
ref: 'TryGhost/Headline',
image: 'assets/img/themes/Headline.png'
}, {
name: 'Edition',
category: 'Newsletter',
url: 'https://github.com/TryGhost/Edition',
previewUrl: 'https://edition.ghost.io/',
ref: 'TryGhost/Edition',
image: 'assets/img/themes/Edition.png'
}, {
name: 'Solo',
category: 'Blog',
url: 'https://github.com/TryGhost/Solo',
previewUrl: 'https://solo.ghost.io',
ref: 'TryGhost/Solo',
image: 'assets/img/themes/Solo.png'
}, {
name: 'Taste',
category: 'Blog',
url: 'https://github.com/TryGhost/Taste',
previewUrl: 'https://taste.ghost.io',
ref: 'TryGhost/Taste',
image: 'assets/img/themes/Taste.png'
}, {
name: 'Episode',
category: 'Podcast',
url: 'https://github.com/TryGhost/Episode',
previewUrl: 'https://episode.ghost.io',
ref: 'TryGhost/Episode',
image: 'assets/img/themes/Episode.png'
}, {
name: 'Digest',
category: 'Newsletter',
url: 'https://github.com/TryGhost/Digest',
previewUrl: 'https://digest.ghost.io/',
ref: 'TryGhost/Digest',
image: 'assets/img/themes/Digest.png'
}, {
name: 'Bulletin',
category: 'Newsletter',
url: 'https://github.com/TryGhost/Bulletin',
previewUrl: 'https://bulletin.ghost.io/',
ref: 'TryGhost/Bulletin',
image: 'assets/img/themes/Bulletin.png'
}, {
name: 'Alto',
category: 'Blog',
url: 'https://github.com/TryGhost/Alto',
previewUrl: 'https://alto.ghost.io',
ref: 'TryGhost/Alto',
image: 'assets/img/themes/Alto.png'
}, {
name: 'Dope',
category: 'Magazine',
url: 'https://github.com/TryGhost/Dope',
previewUrl: 'https://dope.ghost.io',
ref: 'TryGhost/Dope',
image: 'assets/img/themes/Dope.png'
}, {
name: 'Wave',
category: 'Podcast',
url: 'https://github.com/TryGhost/Wave',
previewUrl: 'https://wave.ghost.io',
ref: 'TryGhost/Wave',
image: 'assets/img/themes/Wave.png'
}, {
name: 'Edge',
category: 'Photography',
url: 'https://github.com/TryGhost/Edge',
previewUrl: 'https://edge.ghost.io',
ref: 'TryGhost/Edge',
image: 'assets/img/themes/Edge.png'
}, {
name: 'Dawn',
category: 'Newsletter',
url: 'https://github.com/TryGhost/Dawn',
previewUrl: 'https://dawn.ghost.io/',
ref: 'TryGhost/Dawn',
image: 'assets/img/themes/Dawn.png'
}, {
name: 'Ease',
category: 'Documentation',
url: 'https://github.com/TryGhost/Ease',
previewUrl: 'https://ease.ghost.io',
ref: 'TryGhost/Ease',
image: 'assets/img/themes/Ease.png'
}, {
name: 'Ruby',
category: 'Magazine',
url: 'https://github.com/TryGhost/Ruby',
previewUrl: 'https://ruby.ghost.io',
ref: 'TryGhost/Ruby',
image: 'assets/img/themes/Ruby.png'
}, {
name: 'London',
category: 'Photography',
url: 'https://github.com/TryGhost/London',
previewUrl: 'https://london.ghost.io',
ref: 'TryGhost/London',
image: 'assets/img/themes/London.png'
}, {
name: 'Journal',
category: 'Newsletter',
url: 'https://github.com/TryGhost/Journal',
previewUrl: 'https://journal.ghost.io/',
ref: 'TryGhost/Journal',
image: 'assets/img/themes/Journal.png'
}];
class ErrorHandler extends React.Component {
state = {
hasError: false
@ -118,6 +239,7 @@ export default class AdminXSettings extends Component {
<Suspense fallback={<p className="admin-x-settings-container--loading">Loading settings...</p>}>
<AdminXApp
ghostVersion={config.APP.version}
officialThemes={officialThemes}
/>
</Suspense>
</ErrorHandler>