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/next/components/landing/news-banner.tsx
Views: 687
/*1* This file is part of CoCalc: Copyright © 2023 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { useEffect, useRef, useState } from "react";6import { useAsyncEffect } from "use-async-effect";78import { Icon, IconName } from "@cocalc/frontend/components/icon";9import { slugURL } from "@cocalc/util/news";10import { COLORS } from "@cocalc/util/theme";11import { CHANNELS_ICONS, RecentHeadline } from "@cocalc/util/types/news";12import { Paragraph } from "components/misc";13import A from "components/misc/A";14import { TagList } from "components/news/news";15import { useDateStr } from "components/news/useDateStr";16import { MAX_WIDTH } from "lib/config";1718const PADDING = "15px";19const FONT_SIZE = "16px";20const ROTATE_DELAY_S = 15; // every that number of second a new news item is shown21const ANIMATE_DELAY_MS = 10; // less means faster2223// This is similar to the "BannerWithLinks" component, but showing recent news24export function NewsBanner({25recentHeadlines,26headlineIndex,27}: {28recentHeadlines: RecentHeadline[] | null;29headlineIndex: number;30}) {31// we have to initialize it with a value from the server to avoid these hydration errors32const [index, setIndex] = useState<number>(headlineIndex);3334useEffect(() => {35// every $ROTATE_DELAY_S, rotate to the next headline36const interval = setInterval(() => {37setIndex((i) => ((i ?? 0) + 1) % (recentHeadlines?.length ?? 0));38}, ROTATE_DELAY_S * 1000);3940return () => clearInterval(interval);41}, []);4243if (recentHeadlines == null || recentHeadlines.length === 0) return null;4445return (46<div style={{ backgroundColor: COLORS.YELL_LL, overflow: "hidden" }}>47<NewsHeader item={recentHeadlines[index]} />48</div>49);50}5152function NewsHeader({ item }: { item: RecentHeadline }) {53const [first, setFirst] = useState(true);54const textRef = useRef<HTMLDivElement>(null);55const [cur, setCur] = useState<RecentHeadline>(item);56const [top, setTop] = useState(0);57const [opacity, setOpacity] = useState(1);5859useAsyncEffect(60async (isMounted) => {61if (first) {62setFirst(false);63return;64}6566// height of the textRef element67const offset = textRef.current?.offsetHeight ?? 0;68for (let i = 0; i < offset; i++) {69await new Promise((resolve) => setTimeout(resolve, ANIMATE_DELAY_MS));70if (!isMounted()) return;71setTop(i);72setOpacity(Math.max(0, 1 - (2 * i) / offset));73}74setTop(-offset);75setCur(item);76for (let i = 0; i < offset; i++) {77await new Promise((resolve) => setTimeout(resolve, ANIMATE_DELAY_MS));78if (!isMounted()) return;79setTop(-offset + i);80setOpacity(Math.min(1, (2 * i) / offset));81}82},83[item],84);8586const permalink = slugURL(cur);87const dateStr = useDateStr(cur);8889function renderHeadline() {90if (cur == null) return null;91const { channel, title, tags } = cur;92return (93<>94<div style={{ paddingRight: ".5em" }}>95<Icon name={CHANNELS_ICONS[channel] as IconName} />96</div>97{dateStr}{" "}98<A99href={permalink}100style={{101paddingLeft: PADDING,102fontWeight: "bold",103// https://github.com/sagemathinc/cocalc/issues/6684104maxWidth: "800px",105textOverflow: "ellipsis",106overflow: "hidden",107whiteSpace: "nowrap",108}}109>110{title}111</A>{" "}112<TagList113tags={tags}114mode="news"115style={{ paddingLeft: PADDING }}116styleTag={{ fontSize: FONT_SIZE }}117/>118</>119);120}121122return (123<div124ref={textRef}125style={{126padding: "10px",127textAlign: "center",128whiteSpace: "nowrap",129}}130>131<Paragraph132style={{133display: "flex",134flexDirection: "row",135justifyContent: "center",136margin: "0 auto",137position: "relative",138top,139opacity,140fontSize: FONT_SIZE,141maxWidth: MAX_WIDTH,142}}143>144{renderHeadline()}145<span style={{ paddingLeft: PADDING }}>146<A href={"/news"}>All news...</A>147</span>148</Paragraph>149</div>150);151}152153154