diff --git a/Dockerfile b/Dockerfile index f5fe8f94..9822c903 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,27 +1,25 @@ -ARG SERVER_VERSION=v0.17.0 +ARG SERVER_VERSION=v0.18.0 # Builder image to compile the website -FROM ubuntu as builder +FROM ubuntu AS builder WORKDIR /workdir +# See https://github.com/nodesource/distributions for Node.js package RUN apt-get update \ && apt-get install --no-install-recommends -y \ bash \ ca-certificates \ curl \ + && rm -rf /var/lib/apt/lists/* \ + && curl -sSL https://deb.nodesource.com/setup_20.x | bash - \ + && apt-get install -y nodejs \ && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -# See https://github.com/nodesource/distributions/blob/main/README.md#debinstall -RUN curl -sSL https://deb.nodesource.com/setup_20.x | bash - \ - && apt-get install -y nodejs - -RUN corepack enable -RUN corepack prepare yarn@stable --activate + && corepack enable \ + && corepack prepare yarn@stable --activate # bump to update website -ENV WEBSITE_VERSION 0.12.0 +ENV WEBSITE_VERSION 0.13.0 COPY . /workdir RUN /usr/bin/yarn --cwd website \ diff --git a/website/dev/mock-service.ts b/website/dev/mock-service.ts index dfeca598..e9860565 100644 --- a/website/dev/mock-service.ts +++ b/website/dev/mock-service.ts @@ -19,9 +19,7 @@ const avatarUrl = 'https://upload.wikimedia.org/wikipedia/commons/9/99/Avatar_cu export class MockRegistryService extends ExtensionRegistryService { constructor() { - // FIXME cannot reference `this` in super constructor call - super('', new MockAdminService(undefined!)); - (this.admin.registry as any) = this; + super('', MockAdminService); } search(abortController: AbortController, filter?: ExtensionFilter): Promise> { @@ -152,7 +150,9 @@ export class MockRegistryService extends ExtensionRegistryService { } } -export class MockAdminService extends AdminService { +export class MockAdminService implements AdminService { + + constructor(readonly registry: ExtensionRegistryService) {} getExtension(abortController: AbortController, namespace: string, extension: string): Promise> { return this.registry.getExtensionDetail(abortController, '') as Promise; @@ -187,4 +187,8 @@ export class MockAdminService extends AdminService { async revokePublisherContributions(abortController: AbortController, provider: string, login: string): Promise> { return Promise.resolve({ success: 'ok' }); } + + changeNamespace(abortController: AbortController, req: {oldNamespace: string, newNamespace: string, removeOldNamespace: boolean, mergeIfNewNamespaceAlreadyExists: boolean}): Promise> { + return Promise.resolve({ success: 'ok' }); + } } diff --git a/website/package.json b/website/package.json index a27d8743..fff4359d 100644 --- a/website/package.json +++ b/website/package.json @@ -6,7 +6,7 @@ "repository": "https://github.com/eclipse/open-vsx.org", "license": "EPL-2.0", "dependencies": { - "openvsx-webui": "0.12.0" + "openvsx-webui": "0.13.0" }, "peerDependencies": { "@babel/core": "^7.0.0" diff --git a/website/src/footer-content.tsx b/website/src/footer-content.tsx index 6f0211aa..f626a777 100644 --- a/website/src/footer-content.tsx +++ b/website/src/footer-content.tsx @@ -43,56 +43,59 @@ const LegalLink = styled(RouteLink)(({ theme }: { theme: Theme }) => ({ fontWeight: theme.typography.fontWeightLight })); - +interface MainFooterProps { + isSmallDisplay: boolean + isLargeDisplay: boolean +} + +const MainFooter = ({isSmallDisplay, isLargeDisplay}: MainFooterProps) => { + const itemSpacing = 2.5; + return + {isSmallDisplay ? null : repositoryLink()} + { + isLargeDisplay ? + + + {privacyPolicy()} + + + {termsOfUse()} + + + {publisherAgreement()} + + + {copyrightAgent()} + + + {legalResources()} + + + {manageCookies()} + + + {copyrightText()} + + + {rightsReservedText()} + + + : + <> + {copyrightText()} + + Legal + + + } + ; +} const FooterContent: FunctionComponent<{ expanded: boolean }> = ({ expanded }) => { const theme = useTheme(); const isSmallDisplay = useMediaQuery(theme.breakpoints.down('sm')); const isLargeDisplay = useMediaQuery(theme.breakpoints.up('xl')); - const MainFooter = () => { - const itemSpacing = 2.5; - return - {isSmallDisplay ? null : repositoryLink()} - { - isLargeDisplay ? - - - {privacyPolicy()} - - - {termsOfUse()} - - - {publisherAgreement()} - - - {copyrightAgent()} - - - {legalResources()} - - - {manageCookies()} - - - {copyrightText()} - - - {rightsReservedText()} - - - : - <> - {copyrightText()} - - Legal - - - } - ; - } - if (expanded && !isLargeDisplay) { const itemSpacing = 1; return @@ -116,10 +119,10 @@ const FooterContent: FunctionComponent<{ expanded: boolean }> = ({ expanded }) = {manageCookies()} - + ; } else { - return ; + return ; } }; diff --git a/website/src/members.tsx b/website/src/members.tsx index 66722f75..fee3c6af 100644 --- a/website/src/members.tsx +++ b/website/src/members.tsx @@ -32,13 +32,20 @@ const Members = () => { VSX Registry at open-vsx.org. - + + diff --git a/website/src/menu-content.tsx b/website/src/menu-content.tsx index 1f3373ce..d3188db5 100644 --- a/website/src/menu-content.tsx +++ b/website/src/menu-content.tsx @@ -176,10 +176,10 @@ const SubMenuLink = styled(Link)(subMenuLink); export const DefaultMenuContent: FunctionComponent = () => { - const [workingGroupMenuOpen, setWorkingGroupOpen] = useState(false); + const [workingGroupMenuOpen, setWorkingGroupMenuOpen] = useState(false); const workingGroupMenuEl = useRef(null); - const toggleWorkingGroupMenu = () => setWorkingGroupOpen(!workingGroupMenuOpen); - const closeWorkingGroupMenu = () => setWorkingGroupOpen(false); + const toggleWorkingGroupMenu = () => setWorkingGroupMenuOpen(!workingGroupMenuOpen); + const closeWorkingGroupMenu = () => setWorkingGroupMenuOpen(false); return <> diff --git a/website/src/page-settings.tsx b/website/src/page-settings.tsx index 23ed1a5b..6a337020 100644 --- a/website/src/page-settings.tsx +++ b/website/src/page-settings.tsx @@ -15,7 +15,7 @@ import { Link as RouteLink, Route, useParams } from 'react-router-dom'; import { PageSettings, Extension, NamespaceDetails } from 'openvsx-webui'; import { ExtensionListRoutes } from 'openvsx-webui/lib/pages/extension-list/extension-list-container'; import { DefaultMenuContent, MobileMenuContent } from './menu-content'; -import InfoIcon from '@mui/icons-material/Info'; +// import InfoIcon from '@mui/icons-material/Info'; import OpenVSXLogo from './openvsx-registry-logo'; import footerContent from './footer-content'; import { Document } from './document'; @@ -23,7 +23,93 @@ import About from './about'; import Adopters from './adopters'; import Members from './members'; -export default function createPageSettings(theme: Theme, prefersDarkMode: boolean, serverVersionPromise: Promise): PageSettings { +//---------- HEAD TAGS +const HeadTags: FunctionComponent<{title?: string, description?: string, keywords?: string, url?: string, imageUrl?: string, type?: string}> = (props) => { + const handleChangeClientState = (newState: any, addedTags: HelmetTags, removedTags: HelmetTags): void => { + if (addedTags.metaTags) { + addedTags.metaTags.forEach((value: HTMLMetaElement) => { + if (!value.content) { + value.remove(); + } + }); + } + }; + + const twitterCard = props.imageUrl ? 'summary_large_image' : 'summary'; + const type = props.type || 'website'; + return + {props.title} + + {/* SEO Meta Tags */} + + + + + + + + + {/* Google Meta Tags */} + + + + + {/* Twitter Meta Tags */} + + + + + ; +}; + +const MainHeadTags: FunctionComponent<{pageSettings: PageSettings}> = (props) => { + const title = props.pageSettings.pageTitle; + const description = 'Open VSX is an Eclipse open-source project and alternative to the Visual Studio Marketplace. It is deployed by the Eclipse Foundation at open-vsx.org.'; + const keywords = 'eclipse,ide,open source,development environment,development,vs code,visual studio code,extension,plugin,plug-in,registry,theia'; + const url = `${location.protocol}//${location.host}`; + const imageUrl = url + '/openvsx-preview.png'; + + return (); +}; + +const ExtensionHeadTags: FunctionComponent<{extension?: Extension, pageSettings: PageSettings}> = (props) => { + const { name, namespace } = useParams(); + let title = ` – ${props.pageSettings.pageTitle}`; + let url = `${location.protocol}//${location.host}/extension/`; + let description: string | undefined; + let keywords: string | undefined; + if (props.extension) { + title = (props.extension.displayName || props.extension.name) + title; + url += `${props.extension.namespace}/${props.extension.name}`; + description = props.extension.description; + // extension description can be up to 2048 characters, truncate it. + if (description && description.length > 255) { + let lastWordIndex = description.indexOf(' ', 255); + lastWordIndex = lastWordIndex !== -1 ? lastWordIndex - 1 : 255; + description = description.substring(0, lastWordIndex); + } + if (props.extension.tags) { + keywords = props.extension.tags.filter(t => !t.startsWith('__')).join(); + } + } else { + title = name + title; + url += `${namespace}/${name}`; + } + + return() +}; + +const NamespaceHeadTags: FunctionComponent<{namespaceDetails?: NamespaceDetails, pageSettings: PageSettings}> = (props) => { + const { name } = useParams(); + const namespaceName = props.namespaceDetails?.displayName ?? props.namespaceDetails?.name ?? name + const title = `${namespaceName} – ${props.pageSettings.pageTitle}`; + const url = `${location.protocol}//${location.host}/namespace/${namespaceName}`; + const description = props.namespaceDetails?.description + return() +}; + +export default function createPageSettings(theme: Theme, prefersDarkMode: boolean, serverVersionPromise: Promise): PageSettings { + //---------- SERVER VERSION const ServerVersion = lazy(async () => { const version = await serverVersionPromise; @@ -41,18 +127,18 @@ export default function createPageSettings(theme: Theme, prefersDarkMode: boolea ; - //---------- ANNOUNCEMENT BANNER - const bannerContent: FunctionComponent = () => - - - - - - Eclipse OCX and EclipseCon 2024 are right around the corner October 22-24. Whether your IDEs are desktop cloud-based or a mix, we have a - great agenda of - talks. Register now! - - ; + // //---------- ANNOUNCEMENT BANNER + // const bannerContent: FunctionComponent = () => + // + // + // + // + // + // Eclipse OCX and EclipseCon 2024 are right around the corner October 22-24. Whether your IDEs are desktop cloud-based or a mix, we have a + // great agenda of + // talks. Register now! + // + // ; //---------- SEARCH HEADER const searchHeader: FunctionComponent = () => @@ -98,99 +184,7 @@ export default function createPageSettings(theme: Theme, prefersDarkMode: boolea Claim Ownership ; - //---------- HEAD TAGS - const headTags: FunctionComponent<{title?: string, description?: string, keywords?: string, url?: string, imageUrl?: string, type?: string}> = (props) => { - const handleChangeClientState = (newState: any, addedTags: HelmetTags, removedTags: HelmetTags): void => { - if (addedTags.metaTags) { - addedTags.metaTags.forEach((value: HTMLMetaElement) => { - if (!value.content) { - value.remove(); - } - }); - } - }; - - const twitterCard = props.imageUrl ? 'summary_large_image' : 'summary'; - const type = props.type || 'website'; - return - {props.title} - - {/* SEO Meta Tags */} - - - - - - - - - {/* Google Meta Tags */} - - - - - {/* Twitter Meta Tags */} - - - - - ; - }; - - const mainHeadTags: FunctionComponent<{pageSettings: PageSettings}> = (props) => { - const title = props.pageSettings.pageTitle; - const description = 'Open VSX is an Eclipse open-source project and alternative to the Visual Studio Marketplace. It is deployed by the Eclipse Foundation at open-vsx.org.'; - const keywords = 'eclipse,ide,open source,development environment,development,vs code,visual studio code,extension,plugin,plug-in,registry,theia'; - const url = `${location.protocol}//${location.host}`; - const imageUrl = url + '/openvsx-preview.png'; - - return headTags({ title, description, keywords, url, imageUrl }); - }; - - const extensionHeadTags: FunctionComponent<{extension?: Extension, pageSettings: PageSettings}> = (props) => { - let title = ` – ${props.pageSettings.pageTitle}`; - let url = `${location.protocol}//${location.host}/extension/`; - let description: string | undefined; - let keywords: string | undefined; - if (props.extension) { - title = (props.extension.displayName || props.extension.name) + title; - url += `${props.extension.namespace}/${props.extension.name}`; - description = props.extension.description; - // extension description can be up to 2048 characters, truncate it. - if (description && description.length > 255) { - let lastWordIndex = description.indexOf(' ', 255); - lastWordIndex = lastWordIndex !== -1 ? lastWordIndex - 1 : 255; - description = description.substring(0, lastWordIndex); - } - if (props.extension.tags) { - keywords = props.extension.tags.filter(t => !t.startsWith('__')).join(); - } - } else { - const { name, namespace } = useParams(); - title = name + title; - url += `${namespace}/${name}`; - } - - return headTags({ title, url, description, keywords }); - }; - - const namespaceHeadTags: FunctionComponent<{namespaceDetails?: NamespaceDetails, pageSettings: PageSettings}> = (props) => { - let title = ` – ${props.pageSettings.pageTitle}`; - let url = `${location.protocol}//${location.host}/namespace/`; - let description: string | undefined; - if (props.namespaceDetails) { - title = (props.namespaceDetails.displayName || props.namespaceDetails.name) + title; - url += props.namespaceDetails.name; - description = props.namespaceDetails.description; - } else { - const { name } = useParams(); - title = name + title; - url += name; - } - return headTags({ title, url, description }); - }; - return { pageTitle: 'Open VSX Registry', themeType: prefersDarkMode ? 'dark' : 'light', @@ -198,21 +192,21 @@ export default function createPageSettings(theme: Theme, prefersDarkMode: boolea defaultMenuContent: DefaultMenuContent, mobileMenuContent: MobileMenuContent, toolbarContent, - banner: { - content: bannerContent, - props: { - dismissButton: { - show: true, - label: 'Got It' - }, - color: 'info' - }, - cookie: { - key: 'ocx-2024', - value: 'closed', - path: '/' - } - }, + // banner: { + // content: bannerContent, + // props: { + // dismissButton: { + // show: true, + // label: 'Got It' + // }, + // color: 'info' + // }, + // cookie: { + // key: 'ocx-2024', + // value: 'closed', + // path: '/' + // } + // }, footer: { content: footerContent, props: { @@ -224,9 +218,9 @@ export default function createPageSettings(theme: Theme, prefersDarkMode: boolea additionalRoutes, reportAbuse, claimNamespace, - mainHeadTags, - extensionHeadTags, - namespaceHeadTags + mainHeadTags: MainHeadTags, + extensionHeadTags: ExtensionHeadTags, + namespaceHeadTags: NamespaceHeadTags }, urls: { extensionDefaultIcon: '/default-icon.png', diff --git a/website/static/index.html b/website/static/index.html index 50130ecc..cb55bf50 100644 --- a/website/static/index.html +++ b/website/static/index.html @@ -2,6 +2,7 @@ + Open VSX Registry @@ -39,7 +40,7 @@ - +
diff --git a/website/yarn.lock b/website/yarn.lock index 1193a23d..4cf888fc 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -1515,9 +1515,9 @@ __metadata: linkType: hard "dompurify@npm:^3.0.4": - version: 3.0.5 - resolution: "dompurify@npm:3.0.5" - checksum: 10/24ed3a26486806ec77dd4e3ab248d9c617e851237e3989609d5188312bbb5461028ea08a851f688cde8773cf45eacfeb0bff66c166ef9699fc3ed6a60a565169 + version: 3.1.6 + resolution: "dompurify@npm:3.1.6" + checksum: 10/036844bc9b717b172ba27f5863b56f950289a05d8eebfb702d6953bbf80bd021e480ce4217bd084567186f2d0ada13358ce5556963492cfe402d774e8667f120 languageName: node linkType: hard @@ -2399,7 +2399,7 @@ __metadata: "@types/react-router-dom": "npm:^5.3.3" css-loader: "npm:^6.8.1" express: "npm:^4.21.0" - openvsx-webui: "npm:0.12.0" + openvsx-webui: "npm:0.13.0" source-map-loader: "npm:^4.0.1" style-loader: "npm:^3.3.3" typescript: "npm:~5.1.6" @@ -2420,9 +2420,9 @@ __metadata: languageName: node linkType: hard -"openvsx-webui@npm:0.12.0": - version: 0.12.0 - resolution: "openvsx-webui@npm:0.12.0" +"openvsx-webui@npm:0.13.0": + version: 0.13.0 + resolution: "openvsx-webui@npm:0.13.0" dependencies: "@emotion/react": "npm:^11.11.1" "@emotion/styled": "npm:^11.11.0" @@ -2448,7 +2448,7 @@ __metadata: react-router-dom: "npm:^6.14.1" peerDependencies: "@babel/core": ^7.0.0 - checksum: 10/dc1881ec826aa878519d8f3ffba906c9e26840991ea0c84a088b6277cecaa9fe80721cef166e0277eb9c56756e2778fc6dcb6eac4dda9de4441ba68aea634527 + checksum: 10/b0245fd107f2b88d86cf7fea5a9ded80ad9084a13fdff16904614f5bdeb9f750b916feb976748eee9da0e230065b0d9fc06f789f74153f21005d16eef57c1f07 languageName: node linkType: hard