/** ****************************************************************************** * Copyright (c) 2022 Precies. Software 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 { FunctionComponent, useContext, useEffect, useState, useRef } from 'react'; import { Button, Dialog, DialogTitle, DialogContent, DialogActions, Typography, Box, Paper } from '@mui/material'; import { CheckCircleOutline } from '@mui/icons-material'; import Dropzone from 'react-dropzone'; import { ButtonWithProgress } from '../../components/button-with-progress'; import { ErrorResult, isError } from '../../extension-registry-types'; import { MainContext } from '../../context'; import { styled, Theme } from '@mui/material/styles'; const getColor = (isFocused: boolean, isDragAccept: boolean, isDragReject: boolean) => { if (isDragAccept) { return 'success.main'; } else if (isDragReject) { return 'error.main'; } else if (isFocused) { return 'secondary.main'; } else { return 'text.primary'; } }; const DropzoneDiv = styled('div')(({ theme }: { theme: Theme }) => ({ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', padding: theme.spacing(3), borderWidth: 2, borderRadius: 2, borderStyle: 'dashed', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, outline: 'none', transition: 'border .24s ease-in-out' })); export const PublishExtensionDialog: FunctionComponent = props => { const [open, setOpen] = useState(false); const [publishing, setPublishing] = useState(false); const [fileToPublish, setFileToPublish] = useState(); const [oldFileToPublish, setOldFileToPublish] = useState(); const context = useContext(MainContext); const abortController = useRef(new AbortController()); useEffect(() => { document.addEventListener('keydown', handleEnter); return () => { abortController.current.abort(); document.removeEventListener('keydown', handleEnter); }; }, []); const toMegaBytes = (bytes: number): string => { const megaBytes = bytes / (1024.0 * 1024.0); return megaBytes.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2, }); }; const handleOpenDialog = () => setOpen(true); const handleCancel = () => { if (publishing) { abortController.current.abort(); abortController.current = new AbortController(); } setOpen(false); setPublishing(false); setFileToPublish(undefined); setOldFileToPublish(undefined); }; const handleUndo = () => { setFileToPublish(oldFileToPublish); setOldFileToPublish(undefined); }; const handleDrop = (acceptedFiles: T[]) => { if (fileToPublish) { setOldFileToPublish(fileToPublish); } setFileToPublish(acceptedFiles[0]); }; const handleFileDialogOpen = () => setOldFileToPublish(undefined); const handlePublish = async () => { if (!context.user || !fileToPublish) { return; } setPublishing(true); let published = false; let retryPublish = false; try { published = await tryPublishExtension(fileToPublish); } catch (err) { try { await tryResolveNamespaceError(err); retryPublish = true; } catch (namespaceError) { context.handleError(namespaceError); } } if (retryPublish) { try { published = await tryPublishExtension(fileToPublish); } catch (err) { context.handleError(err); } } if (published) { props.extensionPublished(); setOpen(false); setFileToPublish(undefined); setOldFileToPublish(undefined); } setPublishing(false); }; const handleEnter = (e: KeyboardEvent) => { if (e.code === 'Enter') { handlePublish(); } }; const tryPublishExtension = async (fileToPublish: File): Promise => { let published = false; const publishResponse = await context.service.publishExtension(abortController.current, fileToPublish); if (isError(publishResponse)) { throw publishResponse; } published = true; return published; }; const tryResolveNamespaceError = async (publishResponse: Readonly) => { const namespaceError = 'Unknown publisher: '; if (!isError(publishResponse) || !publishResponse.error.startsWith(namespaceError)) { throw publishResponse; } const namespace = publishResponse.error.substring(namespaceError.length, publishResponse.error.indexOf('\n', namespaceError.length)); if (!namespace || namespace === 'undefined') { const result: Readonly = { error: `Invalid namespace: ${namespace}` }; throw result; } const namespaceResponse = await context.service.createNamespace(abortController.current, namespace); if (isError(namespaceResponse)) { throw namespaceResponse; } }; const successColor = context.pageSettings.themeType === 'dark' ? '#fff' : '#000'; return <> Publish extension { oldFileToPublish ? Changed extension package. : null } {({ getRootProps, getInputProps, isFocused, isDragAccept, isDragReject }) => (

Drag & drop an extension here, or click to select an extension

(Only 1 *.vsix package at a time is accepted)

{ fileToPublish ? {fileToPublish.name} ({toMegaBytes(fileToPublish.size)} MB) : null }
)}
Publish
; }; export interface PublishExtensionDialogProps { extensionPublished: () => void; }