renderCore()

in webapp/src/share.tsx [332:566]


    renderCore() {
        const { visible, projectName: newProjectName, loading, recordingState, screenshotUri, thumbnails, recordError, pubId, qrCodeUri, qrCodeExpanded, title, sharingError } = this.state;
        const targetTheme = pxt.appTarget.appTheme;
        const header = this.props.parent.state.header;
        const hideEmbed = !!targetTheme.hideShareEmbed || qrCodeExpanded;
        const socialOptions = targetTheme.socialOptions;
        const showSocialIcons = !!socialOptions && !pxt.BrowserUtils.isUwpEdge()
            && !qrCodeExpanded;
        const ready = !!pubId;
        let mode = this.state.mode;
        let url = '';
        let embed = '';

        let shareUrl = pxt.appTarget.appTheme.shareUrl || "https://makecode.com/";
        if (!/\/$/.test(shareUrl)) shareUrl += '/';
        let rootUrl = pxt.appTarget.appTheme.embedUrl
        if (!/\/$/.test(rootUrl)) rootUrl += '/';
        const verPrefix = pxt.webConfig.verprefix || '';

        if (header) {
            if (ready) {
                url = `${shareUrl}${pubId}`;
                let editUrl = `${rootUrl}${verPrefix}#pub:${pubId}`;
                switch (mode) {
                    case ShareMode.Code:
                        embed = pxt.docs.codeEmbedUrl(`${rootUrl}${verPrefix}`, pubId);
                        break;
                    case ShareMode.Editor:
                        embed = pxt.docs.embedUrl(`${rootUrl}${verPrefix}`, "pub", pubId);
                        break;
                    case ShareMode.Simulator:
                        let padding = '81.97%';
                        // TODO: parts aspect ratio
                        let simulatorRunString = `${verPrefix}---run`;
                        if (pxt.webConfig.runUrl) {
                            if (pxt.webConfig.isStatic) {
                                simulatorRunString = pxt.webConfig.runUrl;
                            }
                            else {
                                // Always use live, not /beta etc.
                                simulatorRunString = pxt.webConfig.runUrl.replace(pxt.webConfig.relprefix, "/---")
                            }
                        }
                        if (pxt.appTarget.simulator) padding = (100 / pxt.appTarget.simulator.aspectRatio).toPrecision(4) + '%';
                        const runUrl = rootUrl + simulatorRunString.replace(/^\//, '');
                        embed = pxt.docs.runUrl(runUrl, padding, pubId);
                        break;
                    case ShareMode.Url:
                        embed = editUrl;
                        break;
                }
            }
        }
        const publish = () => {
            pxt.tickEvent("menu.embed.publish", undefined, { interactiveConsent: true });
            this.setState({ sharingError: undefined, loading: true });
            let p = Promise.resolve();
            if (newProjectName && this.props.parent.state.projectName != newProjectName) {
                // save project name if we've made a change change
                p = this.props.parent.updateHeaderNameAsync(newProjectName);
            }
            // if screenshots are enabled, always take one
            if (targetTheme.simScreenshot && !screenshotUri) {
                p = p.then(this.screenshotAsync);
            }
            p.then(() => this.props.parent.anonymousPublishAsync(this.state.screenshotUri))
                .then((id) => {
                    this.setState({ pubId: id, qrCodeUri: undefined, qrCodeExpanded: false });
                    if (pxt.appTarget.appTheme.qrCode)
                        qr.renderAsync(`${shareUrl}${id}`)
                            .then(qruri => {
                                if (this.state.pubId == id) // race
                                    this.setState({ qrCodeUri: qruri });
                            });
                    this.forceUpdate();
                })
                .catch((e: Error) => {
                    pxt.tickEvent("menu.embed.error", { code: (e as any).statusCode })
                    this.setState({
                        pubId: undefined,
                        sharingError: e,
                        qrCodeUri: undefined,
                        qrCodeExpanded: false
                    });
                });
            this.forceUpdate();
        }

        const formats = [
            { mode: ShareMode.Code, label: lf("Code") },
            { mode: ShareMode.Editor, label: lf("Editor") },
            { mode: ShareMode.Simulator, label: lf("Simulator") },
        ];

        const action = !ready ? lf("Publish project") : undefined;
        const actionLoading = loading && !this.state.sharingError;

        let actions: sui.ModalButton[] = [];
        if (action) {
            actions.push({
                label: action,
                onclick: publish,
                icon: 'share alternate',
                loading: actionLoading,
                className: 'primary',
                disabled: recordingState != ShareRecordingState.None
            })
        }

        const light = !!pxt.options.light;
        const disclaimer = lf("You need to publish your project to share it or embed it in other web pages.") + " " +
            lf("You acknowledge having consent to publish this project.");
        const screenshotDisabled = actionLoading || recordingState != ShareRecordingState.None;
        const screenshotText = this.loanedSimulator && targetTheme.simScreenshotKey
            ? lf("Take Screenshot (shortcut: {0})", targetTheme.simScreenshotKey) : lf("Take Screenshot");
        const screenshot = targetTheme.simScreenshot;
        const gif = !light && !!targetTheme.simGif;
        const isGifRecording = recordingState == ShareRecordingState.GifRecording;
        const isGifRendering = recordingState == ShareRecordingState.GifRendering;
        const gifIcon = isGifRecording ? "stop" : "circle";
        const gifTitle = isGifRecording
            ? (targetTheme.simGifKey ? lf("Stop recording (shortcut: {0})", targetTheme.simGifKey) : lf("Stop recording"))
            : isGifRendering ? lf("Cancel rendering")
                : (targetTheme.simGifKey ? lf("Start recording (shortcut: {0})", targetTheme.simGifKey)
                    : lf("Start recording"));
        const gifRecordingClass = isGifRecording ? "glow" : "";
        const gifDisabled = actionLoading;
        const gifLoading = recordingState == ShareRecordingState.GifLoading
            || isGifRendering;
        const screenshotMessage = recordError ? recordError
            : isGifRecording ? lf("Recording in progress...")
                : isGifRendering ? lf("Rendering gif...")
                    : undefined;
        const screenshotMessageClass = recordError ? "warning" : "";
        const tooBigErrorSuggestGitHub = sharingError
            && (sharingError as any).statusCode === 413
            && pxt.appTarget?.cloud?.cloudProviders?.github;
        const unknownError = sharingError && !tooBigErrorSuggestGitHub;
        const qrCodeFull = !!qrCodeUri && qrCodeExpanded;
        const classes = this.props.parent.createModalClasses("sharedialog");

        return (
            <sui.Modal isOpen={visible} className={classes}
                size={thumbnails ? "" : "small"}
                onClose={this.hide}
                dimmer={true} header={title || lf("Share Project")}
                closeIcon={true} buttons={actions}
                closeOnDimmerClick
                closeOnDocumentClick
                closeOnEscape>
                <div className={`ui form`}>
                    {action && !this.loanedSimulator ? <div className="ui field">
                        <div>
                            <sui.Input ref="filenameinput" placeholder={lf("Name")} autoFocus={!pxt.BrowserUtils.isMobile()} id={"projectNameInput"}
                                ariaLabel={lf("Type a name for your project")} autoComplete={false}
                                value={newProjectName || ''} onChange={this.handleProjectNameChange} />
                        </div>
                    </div> : undefined}
                    {action && this.loanedSimulator ? <div className="ui fields">
                        <div id="shareLoanedSimulator" className={`simulator ui six wide field landscape only ${gifRecordingClass}`}></div>
                        <div className="ui ten wide field">
                            <sui.Input ref="filenameinput" placeholder={lf("Name")} autoFocus={!pxt.BrowserUtils.isMobile()} id={"projectNameInput"}
                                ariaLabel={lf("Type a name for your project")} autoComplete={false}
                                value={newProjectName || ''} onChange={this.handleProjectNameChange} />
                            <label></label>
                            <div className="ui buttons landscape only">
                                <sui.Button icon="refresh" title={lf("Restart")} ariaLabel={lf("Restart")} onClick={this.restartSimulator} disabled={screenshotDisabled} />
                                {screenshot ? <sui.Button icon="camera" title={screenshotText} ariaLabel={screenshotText} onClick={this.handleScreenshotClick} disabled={screenshotDisabled} /> : undefined}
                                {gif ? <sui.Button icon={gifIcon} title={gifTitle} loading={gifLoading} onClick={this.handleRecordClick} disabled={gifDisabled} /> : undefined}
                            </div>
                            {screenshotUri || screenshotMessage ?
                                <div className={`ui ${screenshotMessageClass} segment landscape only`}>{
                                    (screenshotUri && !screenshotMessage)
                                        ? <img className="ui small centered image" src={screenshotUri} alt={lf("Recorded gif")} />
                                        : <p className="no-select">{screenshotMessage}</p>}</div> : undefined}
                            <p className="ui tiny message info">{disclaimer}</p>
                        </div>
                    </div> : undefined}
                    {action && !this.loanedSimulator ? <p className="ui tiny message info">{disclaimer}</p> : undefined}
                    {tooBigErrorSuggestGitHub && <p className="ui orange inverted segment">{lf("Oops! Your project is too big. You can create a GitHub repository to share it.")}
                        <sui.Button className="inverted basic" text={lf("Create")} icon="github" onClick={this.handleCreateGitHubRepository} />
                    </p>}
                    {unknownError && <p className="ui red inverted segment">{lf("Oops! There was an error. Please ensure you are connected to the Internet and try again.")}</p>}
                    {url && ready ? <div>
                        {!qrCodeFull && <p>{lf("Your project is ready! Use the address below to share your projects.")}</p>}
                        {!qrCodeFull && <sui.Input id="projectUri" class="mini" readOnly={true} lines={1} value={url} copy={true} autoFocus={!pxt.BrowserUtils.isMobile()} selectOnClick={true} aria-describedby="projectUriLabel" autoComplete={false} />}
                        {!qrCodeFull && <label htmlFor="projectUri" id="projectUriLabel" className="accessible-hidden">{lf("This is the read-only internet address of your project.")}</label>}
                        {!!qrCodeUri && <img className={`ui ${qrCodeFull ? "huge" : "small"} image ${qrCodeExpanded ? "centered" : "floated right"} button pixelart`} alt={lf("QR Code of the saved program")}
                            src={qrCodeUri} onClick={this.handleQrCodeClick} title={lf("Click to expand or collapse.")} tabIndex={0} aria-label={lf("QR Code of the saved program")} onKeyDown={fireClickOnEnter}/>}
                        {showSocialIcons ? <div className="social-icons">
                            <SocialButton url={url} ariaLabel="Facebook" type='facebook' heading={lf("Share on Facebook")} />
                            <SocialButton url={url} ariaLabel="Twitter" type='twitter' heading={lf("Share on Twitter")} />
                            {socialOptions.discourse ? <SocialButton url={url} icon={"comments"} ariaLabel={lf("Post to Forum")} type='discourse' heading={lf("Share on Forum")} /> : undefined}
                        </div> : undefined}
                    </div> : undefined}
                    {(ready && !hideEmbed) && <div>
                        <div className="ui divider"></div>
                        <sui.ExpandableMenu title={lf("Embed")}>
                            <sui.Menu pointing secondary>
                                {formats.map(f =>
                                    <EmbedMenuItem key={`tab${f.label}`} onClick={this.setAdvancedMode} currentMode={mode} {...f} />)}
                            </sui.Menu>
                            <sui.Field>
                                <sui.Input id="embedCode" class="mini" readOnly={true} lines={4} value={embed} copy={ready} disabled={!ready} selectOnClick={true} autoComplete={false} />
                                <label htmlFor="embedCode" id="embedCodeLabel" className="accessible-hidden">{lf("This is the read-only code for the selected tab.")}</label>
                            </sui.Field>
                        </sui.ExpandableMenu>
                    </div>}
                </div>
            </sui.Modal >
        )
    }

    componentDidUpdate() {
        const container = document.getElementById("shareLoanedSimulator");
        if (container && this.loanedSimulator && !this.loanedSimulator.parentNode)
            container.appendChild(this.loanedSimulator);
    }

    protected handleKeyDown = (e: KeyboardEvent) => {
        const { visible } = this.state;
        const targetTheme = pxt.appTarget.appTheme;
        const pressed = e.key.toLocaleLowerCase();

        // Don't fire events if component is hidden or if they are typing in a name
        if (!visible || (document.activeElement && document.activeElement.tagName === "INPUT")) return;

        if (targetTheme.simScreenshotKey && pressed === targetTheme.simScreenshotKey.toLocaleLowerCase()) {
            this.handleScreenshotClick();
        }
        else if (targetTheme.simGifKey && pressed === targetTheme.simGifKey.toLocaleLowerCase()) {
            this.handleRecordClick();
        }
    }
}