From 016471a53dc6bdaf07c9f138e3715de934daa368 Mon Sep 17 00:00:00 2001 From: Dev-CasperTheGhost <53900565+Dev-CasperTheGhost@users.noreply.github.com> Date: Sun, 11 Jun 2023 17:19:32 +0200 Subject: [PATCH] feat: render description on Discord Webhooks --- .../911-calls/calls-911-controller.ts | 4 +- .../dispatch/active-calls/CallDescription.tsx | 4 +- apps/client/src/components/editor/editor.tsx | 11 ++-- .../editor/elements/checklist-item.tsx | 2 +- apps/client/src/components/editor/toolbar.tsx | 2 +- apps/client/src/lib/editor/utils.ts | 3 +- apps/client/src/lib/editor/withChecklists.ts | 2 +- apps/client/src/lib/editor/withShortcuts.ts | 2 +- packages/utils/package.json | 11 +++- packages/utils/src/editor/index.ts | 17 +++++ .../utils/src/editor/slate-data-to-string.ts | 2 +- .../utils/src}/editor/types.ts | 0 packages/utils/tests/editor.test.ts | 64 +++++++++++++++++++ 13 files changed, 105 insertions(+), 19 deletions(-) create mode 100644 packages/utils/src/editor/index.ts rename apps/client/src/lib/editor/dataToString.ts => packages/utils/src/editor/slate-data-to-string.ts (90%) rename {apps/client/src/components => packages/utils/src}/editor/types.ts (100%) create mode 100644 packages/utils/tests/editor.test.ts diff --git a/apps/api/src/controllers/dispatch/911-calls/calls-911-controller.ts b/apps/api/src/controllers/dispatch/911-calls/calls-911-controller.ts index 7d8185312..96ebd04e9 100644 --- a/apps/api/src/controllers/dispatch/911-calls/calls-911-controller.ts +++ b/apps/api/src/controllers/dispatch/911-calls/calls-911-controller.ts @@ -47,6 +47,7 @@ import { handleEndCall } from "lib/dispatch/911-calls/handle-end-911-call"; import { AuditLogActionType, createAuditLogEntry } from "@snailycad/audit-logger/server"; import { isFeatureEnabled } from "lib/upsert-cad"; import { _leoProperties, unitProperties } from "utils/leo/includes"; +import { slateDataToString, type Descendant } from "@snailycad/utils/editor"; export const callInclude = Prisma.validator()({ position: true, @@ -637,10 +638,11 @@ export class Calls911Controller { locale?: string | null, ): Promise<{ embeds: APIEmbed[] }> { const t = await getTranslator({ type: "webhooks", locale, namespace: "Calls" }); + const formattedDescription = slateDataToString(call.descriptionData as Descendant[] | null); const caller = call.name || t("unknown"); const location = `${call.location} ${call.postal ? call.postal : ""}`; - const description = call.description || t("couldNotRenderDescription"); + const description = call.description || formattedDescription || t("couldNotRenderDescription"); return { embeds: [ diff --git a/apps/client/src/components/dispatch/active-calls/CallDescription.tsx b/apps/client/src/components/dispatch/active-calls/CallDescription.tsx index 1c302df66..d62160426 100644 --- a/apps/client/src/components/dispatch/active-calls/CallDescription.tsx +++ b/apps/client/src/components/dispatch/active-calls/CallDescription.tsx @@ -1,10 +1,10 @@ import { DEFAULT_EDITOR_DATA } from "components/editor/editor"; import { HoverCard } from "components/shared/HoverCard"; import { classNames } from "lib/classNames"; -import { dataToString } from "lib/editor/dataToString"; import { useTranslations } from "next-intl"; import type { Descendant } from "slate"; import dynamic from "next/dynamic"; +import { slateDataToString } from "@snailycad/utils/editor"; const Editor = dynamic(async () => (await import("components/editor/editor")).Editor, { ssr: false, @@ -19,7 +19,7 @@ export function CallDescription({ data, nonCard }: Props) { const common = useTranslations("Common"); const stringDescription = - dataToString(data.descriptionData as Descendant[] | null) || data.description; + slateDataToString(data.descriptionData as Descendant[] | null) || data.description; if (!stringDescription) { return <>{common("none")}; diff --git a/apps/client/src/components/editor/editor.tsx b/apps/client/src/components/editor/editor.tsx index dd0b5eedb..9a60a3c55 100644 --- a/apps/client/src/components/editor/editor.tsx +++ b/apps/client/src/components/editor/editor.tsx @@ -1,5 +1,6 @@ +import { SlateEditor, SlateElements, Text, slateDataToString } from "@snailycad/utils/editor"; import * as React from "react"; -import { BaseEditor, Editor as _Editor, Node as SlateNode, Descendant, createEditor } from "slate"; +import { Editor as _Editor, Node as SlateNode, Descendant, createEditor } from "slate"; import { Editable, ReactEditor, @@ -8,21 +9,17 @@ import { Slate, withReact, } from "slate-react"; -import { type HistoryEditor, withHistory } from "slate-history"; +import { withHistory } from "slate-history"; import { Toolbar } from "./toolbar"; import { toggleMark } from "lib/editor/utils"; import isHotkey from "is-hotkey"; import { SHORTCUTS, withShortcuts } from "lib/editor/withShortcuts"; import { withChecklists } from "lib/editor/withChecklists"; -import type { SlateElements, Text } from "./types"; import { classNames } from "lib/classNames"; -import { dataToString } from "lib/editor/dataToString"; import { useTranslations } from "use-intl"; import { EditorElement } from "./elements/element"; import { EditorLeaf } from "./elements/leaf"; -export type SlateEditor = BaseEditor & ReactEditor & HistoryEditor; - declare module "slate" { interface CustomTypes { Editor: SlateEditor; @@ -67,7 +64,7 @@ export function Editor(props: EditorProps) { [], ); const isEmpty = React.useMemo(() => { - return dataToString(props.value)?.trim() === ""; + return slateDataToString(props.value)?.trim() === ""; }, [props.value]); function handleChange(value: Descendant[]) { diff --git a/apps/client/src/components/editor/elements/checklist-item.tsx b/apps/client/src/components/editor/elements/checklist-item.tsx index 2e6bfe050..56ee8ec02 100644 --- a/apps/client/src/components/editor/elements/checklist-item.tsx +++ b/apps/client/src/components/editor/elements/checklist-item.tsx @@ -1,7 +1,7 @@ import { classNames } from "lib/classNames"; import { Transforms } from "slate"; import { ReactEditor, useReadOnly, useSlateStatic, type RenderElementProps } from "slate-react"; -import type { CheckListItemElement as ICheckListItemElement } from "../types"; +import { CheckListItemElement as ICheckListItemElement } from "@snailycad/utils/editor"; type Props = RenderElementProps & { element: ICheckListItemElement }; diff --git a/apps/client/src/components/editor/toolbar.tsx b/apps/client/src/components/editor/toolbar.tsx index 9b4e6440a..eaf922fc4 100644 --- a/apps/client/src/components/editor/toolbar.tsx +++ b/apps/client/src/components/editor/toolbar.tsx @@ -16,8 +16,8 @@ import { useSlate } from "slate-react"; import { Button } from "@snailycad/ui"; import { classNames } from "lib/classNames"; import { isBlockActive, toggleMark, toggleBlock, isMarkActive } from "lib/editor/utils"; -import type { SlateElements, Text } from "./types"; import { SelectColorPopover } from "./toolbar/select-color-popover"; +import { SlateElements, Text } from "@snailycad/utils/editor"; /** * mostly example code from: https://github.com/ianstormtaylor/slate/blob/main/site/examples/richtext.tsx diff --git a/apps/client/src/lib/editor/utils.ts b/apps/client/src/lib/editor/utils.ts index c30363d67..2f318b2a7 100644 --- a/apps/client/src/lib/editor/utils.ts +++ b/apps/client/src/lib/editor/utils.ts @@ -1,5 +1,4 @@ -import type { SlateEditor } from "components/editor/editor"; -import type { Text } from "components/editor/types"; +import { SlateEditor, Text } from "@snailycad/utils/editor"; import { Editor, Transforms, Element as SlateElement } from "slate"; const LIST_TYPES = ["numbered-list", "bulleted-list"]; diff --git a/apps/client/src/lib/editor/withChecklists.ts b/apps/client/src/lib/editor/withChecklists.ts index eaf5d8ab7..174f41541 100644 --- a/apps/client/src/lib/editor/withChecklists.ts +++ b/apps/client/src/lib/editor/withChecklists.ts @@ -1,5 +1,5 @@ +import { SlateEditor } from "@snailycad/utils/editor"; import { Editor, Transforms, Range, Point, Element as SlateElement } from "slate"; -import type { SlateEditor } from "components/editor/editor"; export function withChecklists(editor: SlateEditor) { const { deleteBackward } = editor; diff --git a/apps/client/src/lib/editor/withShortcuts.ts b/apps/client/src/lib/editor/withShortcuts.ts index 026eec5b1..ef553c3b9 100644 --- a/apps/client/src/lib/editor/withShortcuts.ts +++ b/apps/client/src/lib/editor/withShortcuts.ts @@ -1,5 +1,5 @@ +import { SlateEditor } from "@snailycad/utils/editor"; import { Editor, Transforms, Range, Point, Element as SlateElement } from "slate"; -import type { SlateEditor } from "components/editor/editor"; type SHORTCUTS = (typeof SHORTCUTS)[keyof typeof SHORTCUTS]; export const SHORTCUTS = { diff --git a/packages/utils/package.json b/packages/utils/package.json index 38664ad58..fa112f8da 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -29,6 +29,10 @@ "require": "./dist/case-number.js", "import": "./dist/case-number.mjs" }, + "./editor": { + "require": "./dist/editor/index.js", + "import": "./dist/editor/index.mjs" + }, "./package.json": "./package.json" }, "files": [ @@ -50,6 +54,9 @@ ], "case-number": [ "dist/case-number.d.ts" + ], + "editor": [ + "dist/editor/index.d.ts" ] } }, @@ -71,7 +78,7 @@ }, "tsup": { "entry": [ - "src/**/*.ts" + "src/**/**/*.ts" ], "dts": true, "bundle": false, @@ -85,4 +92,4 @@ ] }, "sideEffects": false -} +} \ No newline at end of file diff --git a/packages/utils/src/editor/index.ts b/packages/utils/src/editor/index.ts new file mode 100644 index 000000000..cf3a2788b --- /dev/null +++ b/packages/utils/src/editor/index.ts @@ -0,0 +1,17 @@ +import type { SlateElements, Text } from "./types"; +import type { BaseEditor } from "slate"; +import type { ReactEditor } from "slate-react"; +import type { HistoryEditor } from "slate-history"; + +export type SlateEditor = BaseEditor & ReactEditor & HistoryEditor; + +declare module "slate" { + interface CustomTypes { + Element: SlateElements; + Text: Text; + } +} + +export * from "./types"; +export { slateDataToString } from "./slate-data-to-string"; +export { Descendant } from "slate"; diff --git a/apps/client/src/lib/editor/dataToString.ts b/packages/utils/src/editor/slate-data-to-string.ts similarity index 90% rename from apps/client/src/lib/editor/dataToString.ts rename to packages/utils/src/editor/slate-data-to-string.ts index a0d8ab19d..0d57c7070 100644 --- a/apps/client/src/lib/editor/dataToString.ts +++ b/packages/utils/src/editor/slate-data-to-string.ts @@ -1,6 +1,6 @@ import { Editor, Element as SlateElement, Descendant } from "slate"; -export function dataToString(data: Descendant[] | null) { +export function slateDataToString(data: Descendant[] | null) { const string: string[] = []; if (!data) return null; diff --git a/apps/client/src/components/editor/types.ts b/packages/utils/src/editor/types.ts similarity index 100% rename from apps/client/src/components/editor/types.ts rename to packages/utils/src/editor/types.ts diff --git a/packages/utils/tests/editor.test.ts b/packages/utils/tests/editor.test.ts new file mode 100644 index 000000000..243549660 --- /dev/null +++ b/packages/utils/tests/editor.test.ts @@ -0,0 +1,64 @@ +/* eslint-disable quotes */ +import { describe, expect, test } from "vitest"; +import { slateDataToString } from "../src/editor/slate-data-to-string"; +import { Descendant } from "../src/editor"; + +const TEST_VALUE = [ + { + type: "paragraph", + children: [ + { + text: "Hello ", + }, + { + bold: true, + text: "world - this is a test", + }, + ], + }, + { + type: "bulleted-list", + children: [ + { + type: "list-item", + children: [ + { + text: "hello world", + }, + ], + }, + { + type: "list-item", + children: [ + { + text: "testing", + }, + ], + }, + { + type: "list-item", + children: [ + { + text: "", + }, + ], + }, + { + type: "list-item", + children: [ + { + text: "line breaks", + }, + ], + }, + ], + }, +] satisfies Descendant[]; + +describe("slateDataToString", () => { + test("should return a single line string", () => { + expect(slateDataToString(TEST_VALUE)).toMatchInlineSnapshot( + '"Hello world - this is a test hello world testing line breaks"', + ); + }); +});