diff --git a/actions/publishToPublication.ts b/actions/publishToPublication.ts index 0aff3481..0fc67f65 100644 --- a/actions/publishToPublication.ts +++ b/actions/publishToPublication.ts @@ -2,10 +2,7 @@ import * as Y from "yjs"; import * as base64 from "base64-js"; -import { - restoreOAuthSession, - OAuthSessionError, -} from "src/atproto-oauth"; +import { restoreOAuthSession, OAuthSessionError } from "src/atproto-oauth"; import { getIdentityData } from "actions/getIdentityData"; import { AtpBaseClient, @@ -50,7 +47,10 @@ import { ColorToRGBA, } from "components/ThemeManager/colorToLexicons"; import { parseColor } from "@react-stately/color"; -import { Notification, pingIdentityToUpdateNotification } from "src/notifications"; +import { + Notification, + pingIdentityToUpdateNotification, +} from "src/notifications"; import { v7 } from "uuid"; type PublishResult = @@ -253,7 +253,11 @@ export async function publishToPublication({ // Create notifications for mentions (only on first publish) if (!existingDocUri) { - await createMentionNotifications(result.uri, record, credentialSession.did!); + await createMentionNotifications( + result.uri, + record, + credentialSession.did!, + ); } return { success: true, rkey, record: JSON.parse(JSON.stringify(record)) }; @@ -865,7 +869,10 @@ async function createMentionNotifications( .single(); if (publication && publication.identity_did !== authorDid) { - mentionedPublications.set(publication.identity_did, feature.atURI); + mentionedPublications.set( + publication.identity_did, + feature.atURI, + ); } } else if (uri.collection === "pub.leaflet.document") { // Get the document owner's DID @@ -876,7 +883,8 @@ async function createMentionNotifications( .single(); if (document) { - const docRecord = document.data as PubLeafletDocument.Record; + const docRecord = + document.data as PubLeafletDocument.Record; if (docRecord.author !== authorDid) { mentionedDocuments.set(docRecord.author, feature.atURI); } diff --git a/components/Blocks/Block.tsx b/components/Blocks/Block.tsx index f38b6482..0e899749 100644 --- a/components/Blocks/Block.tsx +++ b/components/Blocks/Block.tsx @@ -10,7 +10,7 @@ import { focusBlock } from "src/utils/focusBlock"; import { useHandleDrop } from "./useHandleDrop"; import { useEntitySetContext } from "components/EntitySetProvider"; -import { TextBlock } from "components/Blocks/TextBlock"; +import { TextBlock } from "./TextBlock/index"; import { ImageBlock } from "./ImageBlock"; import { PageLinkBlock } from "./PageLinkBlock"; import { ExternalLinkBlock } from "./ExternalLinkBlock"; diff --git a/components/Blocks/TextBlock/index.tsx b/components/Blocks/TextBlock/index.tsx index 27cb3cfc..2000866d 100644 --- a/components/Blocks/TextBlock/index.tsx +++ b/components/Blocks/TextBlock/index.tsx @@ -120,6 +120,7 @@ export function RenderedTextBlock(props: { }) { let initialFact = useEntity(props.entityID, "block/text"); let headingLevel = useEntity(props.entityID, "block/heading-level"); + let textSize = useEntity(props.entityID, "block/text-size"); let alignment = useEntity(props.entityID, "block/text-alignment")?.data.value || "left"; let alignmentClass = { @@ -128,6 +129,7 @@ export function RenderedTextBlock(props: { center: "text-center", justify: "text-justify", }[alignment]; + let textStyle = textSize?.data.value === "small" ? "text-sm" : ""; let { permissions } = useEntitySetContext(); let content =
; @@ -159,7 +161,7 @@ export function RenderedTextBlock(props: { className={` ${alignmentClass} ${props.type === "blockquote" ? (props.previousBlock?.type === "blockquote" ? `blockquote pt-3 ` : "blockquote") : ""} - ${props.type === "heading" ? HeadingStyle[headingLevel?.data.value || 1] : ""} + ${props.type === "heading" ? HeadingStyle[headingLevel?.data.value || 1] : textStyle} w-full whitespace-pre-wrap outline-hidden ${props.className} `} > {content} @@ -169,6 +171,7 @@ export function RenderedTextBlock(props: { export function BaseTextBlock(props: BlockProps & { className?: string }) { let headingLevel = useEntity(props.entityID, "block/heading-level"); + let textSize = useEntity(props.entityID, "block/text-size"); let alignment = useEntity(props.entityID, "block/text-alignment")?.data.value || "left"; @@ -184,6 +187,10 @@ export function BaseTextBlock(props: BlockProps & { className?: string }) { center: "text-center", justify: "text-justify", }[alignment]; + let textStyle = + textSize?.data.value === "small" + ? "text-sm text-secondary" + : "text-base text-primary"; let editorState = useEditorStates( (s) => s.editorStates[props.entityID], @@ -258,7 +265,7 @@ export function BaseTextBlock(props: BlockProps & { className?: string }) { grow resize-none align-top whitespace-pre-wrap bg-transparent outline-hidden - ${props.type === "heading" ? HeadingStyle[headingLevel?.data.value || 1] : ""} + ${props.type === "heading" ? HeadingStyle[headingLevel?.data.value || 1] : textStyle} ${props.className}`} ref={mountRef} /> @@ -277,7 +284,7 @@ export function BaseTextBlock(props: BlockProps & { className?: string }) { // if this is the only block on the page and is empty or is a canvas, show placeholder
{props.type === "text" @@ -496,7 +503,7 @@ const useMentionState = (entityID: string) => { // Find the relative positioned parent container const editorEl = view.dom; - const container = editorEl.closest('.relative') as HTMLElement | null; + const container = editorEl.closest(".relative") as HTMLElement | null; if (container) { const containerRect = container.getBoundingClientRect(); diff --git a/components/Toolbar/TextBlockTypeToolbar.tsx b/components/Toolbar/TextBlockTypeToolbar.tsx index 2a872264..c3af36ee 100644 --- a/components/Toolbar/TextBlockTypeToolbar.tsx +++ b/components/Toolbar/TextBlockTypeToolbar.tsx @@ -4,7 +4,7 @@ import { Header3Small, } from "components/Icons/BlockTextSmall"; import { Props } from "components/Icons/Props"; -import { ShortcutKey } from "components/Layout"; +import { ShortcutKey, Separator } from "components/Layout"; import { ToolbarButton } from "components/Toolbar"; import { TextSelection } from "prosemirror-state"; import { useCallback } from "react"; @@ -22,6 +22,8 @@ export const TextBlockTypeToolbar = (props: { focusedBlock?.entityID || null, "block/heading-level", ); + + let textSize = useEntity(focusedBlock?.entityID || null, "block/text-size"); let { rep } = useReplicache(); let setLevel = useCallback( @@ -51,106 +53,133 @@ export const TextBlockTypeToolbar = (props: { ); return ( // This Toolbar should close once the user starts typing again -
-
- { - setLevel(1); - }} - active={ - blockType?.data.value === "heading" && - headingLevel?.data.value === 1 - } - tooltipContent={ -
-
Title
-
- start line with - # -
+ <> + { + setLevel(1); + }} + active={ + blockType?.data.value === "heading" && headingLevel?.data.value === 1 + } + tooltipContent={ +
+
Title
+
+ start line with + #
- } - > - - - { - setLevel(2); - }} - active={ - blockType?.data.value === "heading" && - headingLevel?.data.value === 2 - } - tooltipContent={ -
-
Heading
-
- start line with - ## -
+
+ } + > + +
+ { + setLevel(2); + }} + active={ + blockType?.data.value === "heading" && headingLevel?.data.value === 2 + } + tooltipContent={ +
+
Heading
+
+ start line with + ##
- } - > - - - { - setLevel(3); - }} - active={ - blockType?.data.value === "heading" && - headingLevel?.data.value === 3 - } - tooltipContent={ -
-
Subheading
-
- start line with - ### -
+
+ } + > + +
+ { + setLevel(3); + }} + active={ + blockType?.data.value === "heading" && headingLevel?.data.value === 3 + } + tooltipContent={ +
+
Subheading
+
+ start line with + ###
+
+ } + > + +
+ + { + if (headingLevel) + await rep?.mutate.retractFact({ factID: headingLevel.id }); + if (textSize) await rep?.mutate.retractFact({ factID: textSize.id }); + if (!focusedBlock || !blockType) return; + if (blockType.data.value !== "text") { + let existingEditor = + useEditorStates.getState().editorStates[focusedBlock.entityID]; + let selection = existingEditor?.editor.selection; + await rep?.mutate.assertFact({ + entity: focusedBlock?.entityID, + attribute: "block/type", + data: { type: "block-type-union", value: "text" }, + }); + + let newEditor = + useEditorStates.getState().editorStates[focusedBlock.entityID]; + if (!newEditor || !selection) return; + newEditor.view?.dispatch( + newEditor.editor.tr.setSelection( + TextSelection.create(newEditor.editor.doc, selection.anchor), + ), + ); + + newEditor.view?.focus(); } - > - - - { + }} + active={ + blockType?.data.value === "text" && textSize?.data.value !== "small" + } + tooltipContent={
Normal Text
} + > + Text +
+ { + if (!focusedBlock || !blockType) return; + if (blockType.data.value !== "text") { + // Convert to text block first if it's a heading if (headingLevel) await rep?.mutate.retractFact({ factID: headingLevel.id }); - if (!focusedBlock || !blockType) return; - if (blockType.data.value !== "text") { - let existingEditor = - useEditorStates.getState().editorStates[focusedBlock.entityID]; - let selection = existingEditor?.editor.selection; - await rep?.mutate.assertFact({ - entity: focusedBlock?.entityID, - attribute: "block/type", - data: { type: "block-type-union", value: "text" }, - }); - - let newEditor = - useEditorStates.getState().editorStates[focusedBlock.entityID]; - if (!newEditor || !selection) return; - newEditor.view?.dispatch( - newEditor.editor.tr.setSelection( - TextSelection.create(newEditor.editor.doc, selection.anchor), - ), - ); - - newEditor.view?.focus(); - } - }} - active={blockType?.data.value === "text"} - tooltipContent={
Paragraph
} - > - Paragraph -
-
-
+ await rep?.mutate.assertFact({ + entity: focusedBlock.entityID, + attribute: "block/type", + data: { type: "block-type-union", value: "text" }, + }); + } + // Set text size to small + await rep?.mutate.assertFact({ + entity: focusedBlock.entityID, + attribute: "block/text-size", + data: { type: "text-size-union", value: "small" }, + }); + }} + active={ + blockType?.data.value === "text" && textSize?.data.value === "small" + } + tooltipContent={
Small Text
} + > + Small +
+ ); }; diff --git a/src/replicache/attributes.ts b/src/replicache/attributes.ts index a902d078..adcf52c8 100644 --- a/src/replicache/attributes.ts +++ b/src/replicache/attributes.ts @@ -71,6 +71,10 @@ const BlockAttributes = { type: "number", cardinality: "one", }, + "block/text-size": { + type: "text-size-union", + cardinality: "one", + }, "block/image": { type: "image", cardinality: "one", @@ -318,6 +322,10 @@ export type Data = { type: "text-alignment-type-union"; value: "right" | "left" | "center" | "justify"; }; + "text-size-union": { + type: "text-size-union"; + value: "default" | "small"; + }; "page-type-union": { type: "page-type-union"; value: "doc" | "canvas" }; "block-type-union": { type: "block-type-union"; diff --git a/src/utils/getBlocksAsHTML.tsx b/src/utils/getBlocksAsHTML.tsx index 235735ba..0f2c4003 100644 --- a/src/utils/getBlocksAsHTML.tsx +++ b/src/utils/getBlocksAsHTML.tsx @@ -171,11 +171,13 @@ const BlockTypeToHTML: { }, text: async (b, tx, a) => { let [value] = await scanIndex(tx).eav(b.value, "block/text"); + let [textSize] = await scanIndex(tx).eav(b.value, "block/text-size"); return (