feat: Comments (#4911)
* Comment model * Framework, model, policy, presenter, api endpoint etc * Iteration, first pass of UI * fixes, refactors * Comment commands * comment socket support * typing indicators * comment component, styling * wip * right sidebar resize * fix: CMD+Enter submit * Add usePersistedState fix: Main page scrolling on comment highlight * drafts * Typing indicator * refactor * policies * Click thread to highlight Improve comment timestamps * padding * Comment menu v1 * Change comments to use editor * Basic comment editing * fix: Hide commenting button when disabled at team level * Enable opening sidebar without mark * Move selected comment to location state * Add comment delete confirmation * Add comment count to document meta * fix: Comment sidebar togglable Add copy link to comment * stash * Restore History changes * Refactor right sidebar to allow for comment animation * Update to new router best practices * stash * Various improvements * stash * Handle click outside * Fix incorrect placeholder in input fix: Input box appearing on other sessions erroneously * stash * fix: Don't leave orphaned child comments * styling * stash * Enable comment toggling again * Edit styling, merge conflicts * fix: Cannot navigate from insights to comments * Remove draft comment mark on click outside * Fix: Empty comment sidebar, tsc * Remove public toggle * fix: All comments are recessed fix: Comments should not be printed * fix: Associated mark should be removed on comment delete * Revert unused changes * Empty state, basic RTL support * Create dont toggle comment mark * Make it feel more snappy * Highlight active comment in text * fix animation * RTL support * Add reply CTA * Translations
This commit is contained in:
@@ -64,7 +64,7 @@ export default class Extension {
|
||||
commands(_options: {
|
||||
type?: NodeType | MarkType;
|
||||
schema: Schema;
|
||||
}): Record<string, CommandFactory> | CommandFactory {
|
||||
}): Record<string, CommandFactory> | CommandFactory | undefined {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ export default class ExtensionManager {
|
||||
Object.entries(value).forEach(([commandName, commandValue]) => {
|
||||
handle(commandName, commandValue);
|
||||
});
|
||||
} else {
|
||||
} else if (value) {
|
||||
handle(name, value);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import { Node } from "prosemirror-model";
|
||||
import headingToSlug from "./headingToSlug";
|
||||
|
||||
export type Heading = {
|
||||
title: string;
|
||||
level: number;
|
||||
id: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Iterates through the document to find all of the headings and their level.
|
||||
*
|
||||
* @param doc Prosemirror document node
|
||||
* @returns Array<Heading>
|
||||
*/
|
||||
export default function getHeadings(doc: Node) {
|
||||
const headings: Heading[] = [];
|
||||
const previouslySeen = {};
|
||||
|
||||
doc.forEach((node) => {
|
||||
if (node.type.name === "heading") {
|
||||
// calculate the optimal id
|
||||
const id = headingToSlug(node);
|
||||
let name = id;
|
||||
|
||||
// check if we've already used it, and if so how many times?
|
||||
// Make the new id based on that number ensuring that we have
|
||||
// unique ID's even when headings are identical
|
||||
if (previouslySeen[id] > 0) {
|
||||
name = headingToSlug(node, previouslySeen[id]);
|
||||
}
|
||||
|
||||
// record that we've seen this id for the next loop
|
||||
previouslySeen[id] =
|
||||
previouslySeen[id] !== undefined ? previouslySeen[id] + 1 : 1;
|
||||
|
||||
headings.push({
|
||||
title: node.textContent,
|
||||
level: node.attrs.level,
|
||||
id: name,
|
||||
});
|
||||
}
|
||||
});
|
||||
return headings;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Node as ProsemirrorNode, Mark } from "prosemirror-model";
|
||||
import { EditorState } from "prosemirror-state";
|
||||
import Node from "../nodes/Node";
|
||||
|
||||
export default function getMarkAttrs(state: EditorState, type: Node) {
|
||||
const { from, to } = state.selection;
|
||||
let marks: Mark[] = [];
|
||||
|
||||
state.doc.nodesBetween(from, to, (node: ProsemirrorNode) => {
|
||||
marks = [...marks, ...node.marks];
|
||||
|
||||
if (node.content) {
|
||||
node.content.forEach((content) => {
|
||||
marks = [...marks, ...content.marks];
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const mark = marks.find((markItem) => markItem.type.name === type.name);
|
||||
|
||||
if (mark) {
|
||||
return mark.attrs;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import { Node } from "prosemirror-model";
|
||||
|
||||
export type Task = {
|
||||
text: string;
|
||||
completed: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Iterates through the document to find all of the tasks and their completion
|
||||
* state.
|
||||
*
|
||||
* @param doc Prosemirror document node
|
||||
* @returns Array<Task>
|
||||
*/
|
||||
export default function getTasks(doc: Node): Task[] {
|
||||
const tasks: Task[] = [];
|
||||
|
||||
doc.descendants((node) => {
|
||||
if (!node.isBlock) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (node.type.name === "checkbox_list") {
|
||||
node.content.forEach((listItem) => {
|
||||
let text = "";
|
||||
|
||||
listItem.forEach((contentNode) => {
|
||||
if (contentNode.type.name === "paragraph") {
|
||||
text += contentNode.textContent;
|
||||
}
|
||||
});
|
||||
|
||||
tasks.push({
|
||||
text,
|
||||
completed: listItem.attrs.checked,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return tasks;
|
||||
}
|
||||
Reference in New Issue
Block a user