Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/frontend/components/link-retry.tsx
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { Button, Tooltip } from "antd";67import {8useEffect,9useIsMountedRef,10useState,11} from "@cocalc/frontend/app-framework";12import { Gap, Icon, Loading } from "@cocalc/frontend/components";13import { open_new_tab } from "@cocalc/frontend/misc";14import { retry_until_success } from "@cocalc/util/async-utils";15import { COLORS } from "@cocalc/util/theme";1617interface Props {18href: string;19mode?: "link" | "button";20children?;21size?: "small" | undefined; // antd button size22loadingText?: string;23onClick?: () => void;24autoStart?: boolean;25maxTime?: number;26tooltip?: React.ReactNode;27}2829const LinkRetry: React.FC<Props> = ({30href,31size,32mode = "link",33children,34onClick,35autoStart,36maxTime = 30000,37loadingText,38tooltip,39}: Props) => {40const isMountedRef = useIsMountedRef();41const [working, setWorking] = useState<boolean>(false);42const [loading, setLoading] = useState<boolean>(false);43const [error, setError] = useState<boolean>(false);4445useEffect(() => {46setError(false);47setLoading(false);48setWorking(false);49}, [href, mode]);5051function open(): void {52// open_new_tab takes care of blocked popups -- https://github.com/sagemathinc/cocalc/issues/259953open_new_tab(href);54}5556async function start(): Promise<void> {57onClick?.();58setLoading(true);59setError(false);60const f = async (): Promise<void> => {61await $.ajax({62url: href,63timeout: 3000,64});65};66try {67await retry_until_success({68f,69max_delay: 500,70max_time: maxTime,71desc: "opening link",72});73} catch (err) {74if (!isMountedRef.current) {75return;76}77setError(true);78setLoading(false);79setWorking(false);80return;81}82// Open even if NOT mounted! E.g., user clicks link then switches tabs.83open();84if (!isMountedRef.current) {85// not mounted, so don't mess with setState.86return;87}88setError(false);89setLoading(false);90setWorking(true);91}9293function click(): void {94//console.log("click , state = ", { error, working, loading });95if (working) {96open();97} else if (!loading) {98start();99}100}101102useEffect(() => {103if (autoStart) {104start();105}106}, [href]);107108function renderError() {109if (!error) return;110return (111<span style={{ color: COLORS.ANTD_RED_WARN }}>112<Gap /> (failed to load)113</span>114);115}116117switch (mode) {118case "button":119const btn = (120<Button onClick={click} size={size}>121{children}122{loading ? <Icon name="cocalc-ring" spin /> : renderError()}123</Button>124);125if (tooltip) {126return <Tooltip title={tooltip}>{btn}</Tooltip>;127} else {128return btn;129}130case "link":131const aLink = (132<a onClick={click} style={{ cursor: "pointer" }}>133{children}134</a>135);136const a = tooltip ? <Tooltip title={tooltip}>{aLink}</Tooltip> : aLink;137return (138<span>139{a}140{mode === "link" && loading && (141<span>142<Gap /> <Loading text={loadingText} />143</span>144)}145{renderError()}146</span>147);148default:149console.warn(`LinkRetry: invalid mode "${mode}"`);150}151};152153export default LinkRetry;154155156