From 30ea13b6ff7e8c767dabace24510a6d56abf2e3c Mon Sep 17 00:00:00 2001 From: amvanbaren Date: Mon, 16 Oct 2023 16:27:37 +0300 Subject: [PATCH] Show Adopters of Open VSX on the Website Add adopters page Add 'Working Group' menu item to header menu Remove 'Community' and 'FAQ' menu items from header menu Add Resources section to About page with links to Community and FAQ --- configuration/application.yml | 2 +- website/dev/mock-service.ts | 2 +- website/src/about.tsx | 25 +++- website/src/adopters.tsx | 34 +++++ website/src/components/adopters-list.tsx | 163 +++++++++++++++++++++++ website/src/menu-content.tsx | 107 +++++++++++---- website/src/page-settings.tsx | 2 + 7 files changed, 298 insertions(+), 37 deletions(-) create mode 100644 website/src/adopters.tsx create mode 100644 website/src/components/adopters-list.tsx diff --git a/configuration/application.yml b/configuration/application.yml index b45bffeb8..868f97176 100644 --- a/configuration/application.yml +++ b/configuration/application.yml @@ -129,7 +129,7 @@ ovsx: storage: primary-service: azure-blob webui: - frontendRoutes: "/extension/**,/namespace/**,/user-settings/**,/admin-dashboard/**,/about,/publisher-agreement-*,/terms-of-use,/members" + frontendRoutes: "/extension/**,/namespace/**,/user-settings/**,/admin-dashboard/**,/about,/publisher-agreement-*,/terms-of-use,/members,/adopters" eclipse: # TODO change back to https://api.eclipse.org/ for release. base-url: https://api-staging.eclipse.org/ diff --git a/website/dev/mock-service.ts b/website/dev/mock-service.ts index 663bd6a80..b2d838897 100644 --- a/website/dev/mock-service.ts +++ b/website/dev/mock-service.ts @@ -14,7 +14,7 @@ import { NamespaceMembershipList, AdminService, PublisherInfo, NewReview, ExtensionFilter, UrlString, MembershipRole } from "openvsx-webui"; -const avatarUrl = 'https://upload.wikimedia.org/wikipedia/commons/1/19/Spongebob_Squarepants_as_a_balloon.jpg'; +const avatarUrl = 'https://upload.wikimedia.org/wikipedia/commons/9/99/Avatar_cupcake.png'; export class MockRegistryService extends ExtensionRegistryService { diff --git a/website/src/about.tsx b/website/src/about.tsx index 7fda45982..1177f0f31 100644 --- a/website/src/about.tsx +++ b/website/src/about.tsx @@ -9,7 +9,7 @@ ********************************************************************************/ import * as React from 'react'; -import { Link, Typography, Container } from '@mui/material'; +import { Link, Typography, Container, List, ListItem, ListItemText } from '@mui/material'; import { styled, Theme } from '@mui/material/styles'; const Heading = styled(Typography)(({ theme }: { theme: Theme }) => ({ @@ -58,11 +58,24 @@ const About = () => { - Publishing Extensions - - The publishing process is described in - the openvsx Wiki. - + Resources + + + + The publishing process is described in the openvsx Wiki. + + + + + The FAQ section explains what you can and cannot do as a user of our service. + + + + + Get involved in the community. + + + ; } diff --git a/website/src/adopters.tsx b/website/src/adopters.tsx new file mode 100644 index 000000000..d8c4f0aff --- /dev/null +++ b/website/src/adopters.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import { Container, Typography, Box, Button } from "@mui/material"; +import { styled, Theme } from '@mui/material/styles'; +import AdoptersList from "./components/adopters-list"; + +const Heading = styled(Typography)(({ theme }: { theme: Theme }) => ({ + marginTop: theme.spacing(4), + marginBottom: theme.spacing(2) +})); + +const Adopters = () => { + return ( + + + Adopters + + + Our open source projects drive innovation across a broad spectrum of industries and on both private and public clouds — enabling organizations of all shapes and sizes to accelerate cloud native development with world-class tools. + + + + + + + ); +} + +export default Adopters; \ No newline at end of file diff --git a/website/src/components/adopters-list.tsx b/website/src/components/adopters-list.tsx new file mode 100644 index 000000000..5333d5d00 --- /dev/null +++ b/website/src/components/adopters-list.tsx @@ -0,0 +1,163 @@ +/******************************************************************************** + * Copyright (c) 2023 TypeFox and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +import React, { FunctionComponent, useState, useEffect, useContext } from 'react'; +import { CircularProgress, Grid, Box, Link, Typography } from '@mui/material'; +import { styled, Theme } from '@mui/material/styles'; +import { MainContext } from 'openvsx-webui/lib/context'; + +interface Project { + project_id: string; + name: string; + url: string; + logo: string; + adopters: Adopter[]; +} + +interface Adopter { + name: string; + logo: string; + projects: string[]; + homepage_url: string; + logo_white: string; +} + +const AdoptersList: FunctionComponent = () => { + const [loaded, setLoaded] = useState(false); + const [adopters, setAdopters] = useState([]); + + useEffect(() => { + if (loaded) return; + + const abortController = new AbortController(); + fetch(`https://api.eclipse.org/adopters/projects?working_group=cloud-development-tools`, { + signal: abortController.signal, + }) + .then(async (res) => { + if (!res.ok) throw new Error('Failed to fetch adopters'); + + const projects = await res.json() as Project[]; + const project = projects.find((p) => p.project_id == 'ecd.openvsx'); + if(project) { + setAdopters(project.adopters); + } + }) + .catch((err) => { + if (err instanceof DOMException && err.name === 'AbortError') return; + console.error(err); + }) + .finally(() => abortController.signal.aborted || setLoaded(true)); + + return () => abortController.abort(); + }, [loaded]); + + if (adopters.length === 0) return ; + return ( + + { adopters.map(adopter => + + )} + + ); +}; + +export default AdoptersList; + +interface AdopterItemProps { + name: string; + logo?: string; + logoWhite?: string; + url?: string; +} + +const bordered = (theme: Theme) => { + return { + border: '1px solid', + borderColor: theme.palette.mode === 'light' + ? theme.palette.grey['300'] + : theme.palette.grey['800'] + }; +}; + +const HeaderBox = styled(Box)(({ theme }: { theme: Theme }) => ({ + ...bordered(theme), + display: 'flex', + alignItems: 'center', + minHeight: '6rem', + backgroundColor: theme.palette.mode === 'light' + ? theme.palette.grey['300'] + : theme.palette.grey['800'] +})); + +const BodyBox = styled(Box)(({ theme }: { theme: Theme }) => ({ + ...bordered(theme), + display: 'flex', + height: '100%', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: theme.palette.background.default +})); + +const GridContainer = styled(Grid)({ + display: 'flex', + flexDirection: 'column', + alignItems: 'stretch', + height: '18rem', + textAlign: 'center', +}); + +const AdopterItem: FunctionComponent = ({ name, logo, logoWhite, url }) => { + const { pageSettings } = useContext(MainContext); + const styles = { + heading: { + width: '100%', + }, + logoContainer: { + width: '100%', + maxWidth: '8rem', + maxHeight: '8rem', + }, + logo: { + width: '100%', + height: '100%', + objectFit: 'contain', + } + }; + + let logoUrl = pageSettings.themeType == 'dark' ? logoWhite : logo; + if(logoUrl) { + logoUrl = 'https://api.eclipse.org/adopters/assets/images/adopters/' + logoUrl; + } + + return ( + + + { url + ? {name} + : {name} + } + + + + { logoUrl + ? + : {name} + } + + + + ); +}; \ No newline at end of file diff --git a/website/src/menu-content.tsx b/website/src/menu-content.tsx index 1a4c445bb..18069223d 100644 --- a/website/src/menu-content.tsx +++ b/website/src/menu-content.tsx @@ -8,18 +8,20 @@ * SPDX-License-Identifier: EPL-2.0 ********************************************************************************/ -import React, { FunctionComponent, PropsWithChildren } from 'react'; -import { Theme, Typography, MenuItem, Link, Button } from '@mui/material'; +import React, { FunctionComponent, PropsWithChildren, useState, useRef } from 'react'; +import { Theme, Typography, Menu, MenuItem, Link, Button, Accordion, AccordionDetails, AccordionSummary } from '@mui/material'; import { styled } from '@mui/material/styles'; import { Link as RouteLink } from 'react-router-dom'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import GitHubIcon from '@mui/icons-material/GitHub'; import MenuBookIcon from '@mui/icons-material/MenuBook'; -import HelpIcon from '@mui/icons-material/Help'; -import ForumIcon from '@mui/icons-material/Forum'; import InfoIcon from '@mui/icons-material/Info'; import StarIcon from '@mui/icons-material/Star'; import StatusIcon from '@mui/icons-material/NetworkCheck'; import PublishIcon from '@mui/icons-material/Publish'; +import GroupWorkIcon from '@mui/icons-material/GroupWork'; +import PeopleAltIcon from '@mui/icons-material/PeopleAlt'; +import HubIcon from '@mui/icons-material/Hub'; import { UserSettingsRoutes } from 'openvsx-webui'; //-------------------- Mobile View --------------------// @@ -71,19 +73,41 @@ export const MobileMenuContent: FunctionComponent = () => { - - + + } + aria-controls="working-group-content" + id="working-group-header" + > - - FAQ + + Working Group - - + + + + + + + Members + + + + + + + + Adopters + + + + + - + - - Community + + Sponsor @@ -95,14 +119,6 @@ export const MobileMenuContent: FunctionComponent = () => { - - - - - Sponsor - - - { !location.pathname.startsWith(UserSettingsRoutes.ROOT) ? @@ -135,29 +151,62 @@ const headerItem = ({ theme }: { theme: Theme }) => ({ } }); +const headerTypography = ({ theme }: { theme: Theme }) => ({ + ...headerItem({theme}), + cursor: 'pointer' +}); + const MenuLink = styled(Link)(headerItem); const MenuRouteLink = styled(RouteLink)(headerItem); +const MenuTypography = styled(Typography)(headerTypography); + +const subMenuItem = ({ theme }: { theme: Theme }) => ({ + '&:focus, &:hover': { + background: 'transparent' + } +}); + +const subMenuLink = ({ theme }: { theme: Theme }) => ({ + ...headerItem({theme}), + margin: theme.spacing(0.5) +}); + +const SubMenuItem = styled(MenuItem)(subMenuItem); +const SubMenuLink = styled(Link)(subMenuLink); + export const DefaultMenuContent: FunctionComponent = () => { + const [workingGroupMenuOpen, setWorkingGroupOpen] = useState(false); + const workingGroupMenuEl = useRef(null); + const toggleWorkingGroupMenu = () => setWorkingGroupOpen(!workingGroupMenuOpen); + const closeWorkingGroupMenu = () => setWorkingGroupOpen(false); + return <> Documentation - - FAQ - Status - - Community + Working Group + + + + Members + + + + + Adopters + + + + + Sponsor About - - Sponsor - diff --git a/website/src/page-settings.tsx b/website/src/page-settings.tsx index 4ba377054..3a912d471 100644 --- a/website/src/page-settings.tsx +++ b/website/src/page-settings.tsx @@ -20,6 +20,7 @@ import OpenVSXLogo from './openvsx-registry-logo'; import footerContent from './footer-content'; import { Document } from './document'; import About from './about'; +import Adopters from './adopters'; import Members from './members'; export default function createPageSettings(theme: Theme, prefersDarkMode: boolean): PageSettings { @@ -64,6 +65,7 @@ export default function createPageSettings(theme: Theme, prefersDarkMode: boolea } /> } /> } /> + } /> ; //---------- REPORT ABUSE LINK