Skip to content

Commit

Permalink
Merge pull request #728 from tone-row/dev
Browse files Browse the repository at this point in the history
v1.57.0
  • Loading branch information
rob-gordon authored Aug 30, 2024
2 parents a7588f4 + b87f728 commit bd26416
Show file tree
Hide file tree
Showing 59 changed files with 333 additions and 249 deletions.
4 changes: 2 additions & 2 deletions api/_lib/_dates.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { format, parse, parseISO } from "date-fns";

export function niceDate(d: string) {
return format(parse(d, "yyyy-MM-dd", new Date()), "LLLL d, yyyy");
export function niceDate(d: Date) {
return format(d, "LLLL d, yyyy");
}

export function dateString(d: string) {
Expand Down
2 changes: 1 addition & 1 deletion api/blog/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default async function handler(req: VercelRequest, res: VercelResponse) {
if (!("properties" in post)) throw new Error("No properties");
const { properties = {}, id } = post;
const { date, ...props } = getNestedProperties(properties);
const publishDate = niceDate(date);
const publishDate = niceDate(new Date(date));

const htmlContent = await getPostHtmlFromId(id);

Expand Down
66 changes: 47 additions & 19 deletions api/blog/posts.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,59 @@
import { VercelRequest, VercelResponse } from "@vercel/node";
import { dateAsNumber, dateString, niceDate } from "../_lib/_dates";
import { notion, getNestedProperties } from "../_lib/_notion";
import { niceDate } from "../_lib/_dates";
import { notion } from "../_lib/_notion";
import { z } from "zod";
import { BlogPost, NotionPost } from "shared";

export default async function handler(req: VercelRequest, res: VercelResponse) {
const response = await notion.databases.query({
database_id: "b7a09b10aa83485b94092269239a8b38",
});
const posts = response.results.map((page) => {
if (!("properties" in page)) throw new Error("No properties");
const { properties = {}, id } = page;
const { date, ...props } = getNestedProperties(properties);
if (!("title" in props)) throw new Error("No title");
if (!("status" in props)) throw new Error("No status");
if (!("slug" in props)) throw new Error("No slug");
if (!("description" in props)) throw new Error("No description");

if (!date) throw new Error("No date");

const sanitizedDate = dateString(date);
const publishDate = niceDate(date);
const rawDate = dateAsNumber(date);

return { id, rawDate, date: sanitizedDate, publishDate, ...props };
});

const notionPosts = response.results.filter(
isValidNotionPost
) as unknown as NotionPost[];

const posts = notionPosts.map(notionPostToBlogPost);

// Cache for 1 week, stale-while-revalidate
res.setHeader("Cache-Control", "s-maxage=1, stale-while-revalidate");

res.json(posts);
}

const postSchema: z.ZodType<NotionPost> = z.object({
id: z.string(),
created_time: z.string(),
properties: z.object({
title: z.object({
title: z.array(z.object({ plain_text: z.string() })).min(1),
}),
description: z.object({
rich_text: z.array(z.object({ plain_text: z.string() })).min(1),
}),
status: z.object({
status: z.object({ name: z.string() }),
}),
slug: z.object({
rich_text: z.array(z.object({ plain_text: z.string() })).min(1),
}),
}),
});

function isValidNotionPost(post: unknown): post is NotionPost {
const parsed = postSchema.safeParse(post);
if (parsed.success) return true;
return false;
}

function notionPostToBlogPost(post: NotionPost): BlogPost {
return {
id: post.id,
publishDate: new Date(post.created_time).getTime(),
niceDate: niceDate(new Date(post.created_time)),
description: post.properties.description.rich_text[0].plain_text,
slug: post.properties.slug.rich_text[0].plain_text,
status: post.properties.status.status.name,
title: post.properties.title.title[0].plain_text,
};
}
2 changes: 1 addition & 1 deletion api/prompt/_shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export async function handleRateLimit(
redis: kv,
limiter: isPro
? Ratelimit.slidingWindow(3, "1m")
: Ratelimit.fixedWindow(3, "30d"),
: Ratelimit.fixedWindow(2, "30d"),
});

const rateLimitKey = isPro ? `pro_${customerId}` : `unauth_${ip}`;
Expand Down
2 changes: 1 addition & 1 deletion app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "app",
"version": "1.56.2",
"version": "1.57.0",
"main": "module/module.js",
"license": "MIT",
"scripts": {
Expand Down
Binary file modified app/public/template-screenshots/code-flow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/public/template-screenshots/decision-flow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/public/template-screenshots/default.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/public/template-screenshots/mindmap-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/public/template-screenshots/mindmap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/public/template-screenshots/network-diagram-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/public/template-screenshots/org-chart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/public/template-screenshots/pert-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/public/template-screenshots/playful-mindmap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/public/template-screenshots/process-flow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/public/template-screenshots/thumb_code-flow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/public/template-screenshots/thumb_decision-flow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/public/template-screenshots/thumb_default.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/public/template-screenshots/thumb_flowchart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/public/template-screenshots/thumb_knowledge-graph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/public/template-screenshots/thumb_mindmap-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/public/template-screenshots/thumb_mindmap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/public/template-screenshots/thumb_network-diagram-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/public/template-screenshots/thumb_network-diagram-icons.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/public/template-screenshots/thumb_org-chart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/public/template-screenshots/thumb_pert-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/public/template-screenshots/thumb_playful-mindmap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 5 additions & 20 deletions app/scripts/screenshot-templates.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ async function main() {

await screenshotTemplates(filteredTemplates);
await createThumbnails(filteredTemplates);
await createTemplatesIndex(templates); // Always create index with all templates
console.log(
"Don't forget to add the new templates to `shared/src/templates.ts` file."
);
}

/**
Expand Down Expand Up @@ -75,8 +77,8 @@ async function takeScreenshot(browser, template) {
console.error("Error loading template", error);
}

// wait a few seconds
await page.waitForTimeout(3000);
// wait 3 seconds
await new Promise((resolve) => setTimeout(resolve, 3000));

// get the full page link
const screenshotLink = await page.evaluate(() => {
Expand Down Expand Up @@ -137,21 +139,4 @@ async function createThumbnails(templates) {
}
}

async function createTemplatesIndex(templates) {
console.log("Creating templates index...");
const indexPath = path.join(__dirname, "../../shared/templates.ts");
const indexContent = `export const templates = ${JSON.stringify(
templates,
null,
2
)} as const;`;

try {
await writeFile(indexPath, indexContent, "utf8");
console.log("✅ Created templates index file");
} catch (error) {
console.error("Error creating templates index file:", error);
}
}

main().catch(console.error);
31 changes: 27 additions & 4 deletions app/src/components/Graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ const Graph = memo(function Graph({ shouldResize }: { shouldResize: number }) {
export default Graph;

function handleDragFree() {
// If the layout is fcose, we don't want to freeze the layout
// because people may not expect that behavior / Subject to change!
const layout = useGraphStore.getState().layout;
if (layout.name === "fcose" || layout.name === "stress") return;

const nodePositions = getNodePositionsFromCy();
useDoc.setState(
(state) => {
Expand Down Expand Up @@ -240,6 +245,10 @@ function initializeGraph({
}
);

cyCurrent.on("dragstart", "node", () => {
// stop the layout from running
window.__cy?.stop();
});
cyCurrent.on("dragfree", handleDragFree);

// on zoom
Expand Down Expand Up @@ -344,14 +353,13 @@ function getGraphUpdater({

elements = getElements(doc.text);

// Test
cyErrorCatcher.current.json({ elements, layout, style });

// Very specific bug wrt to cose layouts
// If it's the first render, randomize cannot be false
// Because the graph has no positions yet
if (layout.name === "fcose") {
if (isFirstRender.current) {
// @ts-ignore
layout.animate = false;
// @ts-ignore
layout.randomize = true;
// @ts-ignore
Expand All @@ -362,8 +370,20 @@ function getGraphUpdater({
}
}

// Stress layout, need to turn off interactive and animation on first render
// @ts-ignore
layout.animate = false;
if (layout.name === "elk" && layout.elk.algorithm === "stress") {
if (isFirstRender.current) {
// @ts-ignore
layout.elk.interactive = false;
// @ts-ignore
layout.animate = false;
// @ts-ignore
delete layout.animationDuration;
// @ts-ignore
delete layout.animationEasing;
}
}

// Finally we get rid of layouts when user has dragged
// Apply the preset layout if nodePositions is defined
Expand All @@ -376,6 +396,9 @@ function getGraphUpdater({
delete layout.spacingFactor;
}

// Test
cyErrorCatcher.current.json({ elements, layout, style });

cyErrorCatcher.current.layout(layout);

// Set up a listener to mark the graph as initialized after the first layout run
Expand Down
74 changes: 2 additions & 72 deletions app/src/components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* eslint-disable jsx-a11y/no-distracting-elements */
import cx from "classnames";
import { X } from "phosphor-react";
import { memo, ReactNode, Suspense, useState } from "react";
import { memo, ReactNode, Suspense } from "react";
import { Link } from "react-router-dom";

import { useFullscreen, useIsEditorView } from "../lib/hooks";
Expand All @@ -12,15 +11,12 @@ import styles from "./Layout.module.css";
import Loading from "./Loading";
import { VersionCheck } from "./VersionCheck";
import { PaywallModal } from "./PaywallModal";
import { Trans } from "@lingui/macro";

const Layout = memo(({ children }: { children: ReactNode }) => {
const isFullscreen = useFullscreen();
let [showBanner, message, messageType] = getShowBannerAndMessage();
const isEditorView = useIsEditorView();

const [showPHBanner, setShowPHBanner] = useState<boolean>(isPHBannerTime());

// fullscreen disables banners
if (isFullscreen) {
showBanner = false;
Expand All @@ -33,19 +29,8 @@ const Layout = memo(({ children }: { children: ReactNode }) => {
className={styles.LayoutWrapper}
data-showing={isEditorView ? "editor" : undefined}
data-fullscreen={isFullscreen}
data-banner={showBanner || showPHBanner}
data-banner={showBanner}
>
{showPHBanner ? (
<ProductHuntBanner
hide={() => {
setShowPHBanner(false);
// set a cookie to not show the banner again
document.cookie = `showPHBanner=false; path=/; max-age=${
60 * 60 * 24 * 30
}`;
}}
/>
) : null}
{showBanner ? (
<div
className={cx("flex justify-center items-center w-full gap-2", {
Expand Down Expand Up @@ -73,8 +58,6 @@ Layout.displayName = "Layout";

export default Layout;

// ?error=server_error&error_description=Multiple+accounts+with+the+same+email+address+in+the+same+linking+domain+detected%3A+default

function getShowBannerAndMessage(): [boolean, string, "error" | "info"] {
const hash = window.location.hash;
if (hash.startsWith("#message=")) {
Expand All @@ -91,56 +74,3 @@ function getShowBannerAndMessage(): [boolean, string, "error" | "info"] {
}
return [false, "", "info"];
}

/**
* Extremely Temporary Producthunt Banner
*/
export function ProductHuntBanner({ hide }: { hide: () => void }) {
return (
<a
href="https://www.producthunt.com/posts/flowchart-fun-2?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-flowchart-fun-2"
target="_blank"
rel="noopener noreferrer"
className="relative bg-gradient-to-b from-orange-400 to-orange-300/80 text-[#443214] hidden md:flex items-center justify-center group"
onClick={hide}
>
<p className="text-sm font-bold pl-4 z-10">
<Trans>
Love Flowchart Fun? Support us with a vote on Product Hunt!
</Trans>
</p>
<div className="w-[64px] h-[52px] overflow-hidden relative mix-blend-multiply rounded-full z-10">
<div className="absolute -top-px -right-px w-[250px] h-[54px]">
<img
src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=483202&theme=neutral"
alt="Flowchart Fun - Text-to-Flowchart with AI Magic 🪄 | Product Hunt"
width={250}
height={54}
/>
</div>
</div>
<div className="absolute top-0 left-0 w-full h-full bg-gradient-to-b from-transparent to-amber-500 hidden group-hover:block animate-pulseIn" />
</a>
);
}

/**
* This function returns true if it's August 25th, 2024, between 12:00am and 11:59pm PST
*/
export function isPHBannerTime() {
// check if the cookie exists
const cookie = document.cookie
.split("; ")
.find((row) => row.startsWith("showPHBanner="));
if (cookie) {
return false;
}

const pstDate = new Date().toLocaleString("en-US", {
timeZone: "America/Los_Angeles",
});
const [date] = pstDate.split(", ");
const [month, day, year] = date.split("/").map(Number);

return year === 2024 && month === 8 && day === 25;
}
2 changes: 2 additions & 0 deletions app/src/components/LoadTemplateDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ export function LoadTemplateDialog() {
});
}, [template, replaceContent, reset]);

console.log(templates);

return (
<Dialog.Root
open={open}
Expand Down
2 changes: 1 addition & 1 deletion app/src/lib/templates/mindmap-dark-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const theme: FFTheme = {
targetArrowShape: "none",
sourceDistanceFromNode: 5,
targetDistanceFromNode: 5,
edgeTextSize: 10,
edgeTextSize: 1,
rotateEdgeLabel: false,
direction: "DOWN",
fixedHeight: 300,
Expand Down
2 changes: 1 addition & 1 deletion app/src/lib/templates/network-diagram-dark-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const theme: FFTheme = {
sourceDistanceFromNode: 0,
targetDistanceFromNode: 6,
arrowScale: 0.8,
edgeTextSize: 10,
edgeTextSize: 1,
rotateEdgeLabel: false,
fixedHeight: 100,
};
Expand Down
2 changes: 1 addition & 1 deletion app/src/lib/templates/pert-light-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const theme: FFTheme = {
sourceDistanceFromNode: 5,
targetDistanceFromNode: 5,
arrowScale: 1,
edgeTextSize: 10,
edgeTextSize: 1,
rotateEdgeLabel: true,
};

Expand Down
Loading

0 comments on commit bd26416

Please sign in to comment.