refactor(ai-chat): restructure mention handling and improve type definitions
Refactored the MentionNode and related types to enhance clarity and maintainability. Removed the old convertMentionElement function and updated the importDOM method to throw an error for unimplemented spans. Introduced a base interface for mention data and specific interfaces for different mention types, improving type safety. Updated mention text value utilities to include a new function for display text, enhancing the mention display logic. Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
parent
414ab5232f
commit
e008dfa016
|
|
@ -1,7 +1,6 @@
|
|||
import i18next from "i18next"
|
||||
import type {
|
||||
DOMConversionMap,
|
||||
DOMConversionOutput,
|
||||
DOMExportOutput,
|
||||
EditorConfig,
|
||||
LexicalEditor,
|
||||
|
|
@ -66,14 +65,8 @@ export class MentionNode extends DecoratorNode<React.JSX.Element> {
|
|||
|
||||
static override importDOM(): DOMConversionMap | null {
|
||||
return {
|
||||
span: (domNode: HTMLElement) => {
|
||||
if (!Object.hasOwn(domNode.dataset, "lexicalMention")) {
|
||||
return null
|
||||
}
|
||||
return {
|
||||
conversion: convertMentionElement,
|
||||
priority: 1,
|
||||
}
|
||||
span: () => {
|
||||
throw new Error("Not implemented")
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -157,29 +150,6 @@ export class MentionNode extends DecoratorNode<React.JSX.Element> {
|
|||
}
|
||||
}
|
||||
|
||||
function convertMentionElement(domNode: HTMLElement): DOMConversionOutput {
|
||||
const mentionType = domNode.dataset.mentionType as MentionData["type"] | null
|
||||
const { mentionId } = domNode.dataset
|
||||
const textContent = domNode.textContent || ""
|
||||
|
||||
if (!mentionType || !mentionId) {
|
||||
return { node: null }
|
||||
}
|
||||
|
||||
// Extract name from text content (remove @ prefix)
|
||||
const name = textContent.startsWith("@") ? textContent.slice(1) : textContent
|
||||
|
||||
const mentionData: MentionData = {
|
||||
id: mentionId,
|
||||
name,
|
||||
type: mentionType,
|
||||
value: null,
|
||||
}
|
||||
|
||||
const node = $createMentionNode(mentionData)
|
||||
return { node }
|
||||
}
|
||||
|
||||
export function $createMentionNode(mentionData: MentionData): MentionNode {
|
||||
const mentionNode = new MentionNode(mentionData)
|
||||
return $applyNodeReplacement(mentionNode)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import {
|
|||
} from "../hooks/dateMentionUtils"
|
||||
import { $isMentionNode } from "../MentionNode"
|
||||
import type { MentionData } from "../types"
|
||||
import { getMentionTextValue } from "../utils/mentionTextValue"
|
||||
import { getMentionDisplayTextValue } from "../utils/mentionTextValue"
|
||||
import { MentionTypeIcon } from "./shared/MentionTypeIcon"
|
||||
|
||||
interface MentionComponentProps {
|
||||
|
|
@ -33,7 +33,7 @@ interface MentionComponentProps {
|
|||
}
|
||||
|
||||
const MentionTooltipContent = ({ mentionData }: { mentionData: MentionData }) => {
|
||||
const displayValue = getMentionTextValue(mentionData)
|
||||
const displayValue = getMentionDisplayTextValue(mentionData)
|
||||
|
||||
const getIconBgColor = () => {
|
||||
if (mentionData.type === "view" && typeof mentionData.value === "number") {
|
||||
|
|
@ -60,7 +60,7 @@ const MentionTooltipContent = ({ mentionData }: { mentionData: MentionData }) =>
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2 p-1">
|
||||
<div className="flex items-start gap-2 p-1">
|
||||
<div
|
||||
className={cn(
|
||||
"flex size-5 shrink-0 items-center justify-center rounded text-white",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import type { TFunction } from "i18next"
|
|||
|
||||
import { MENTION_DATE_VALUE_FORMAT } from "~/modules/ai-chat/utils/mentionDate"
|
||||
|
||||
import type { MentionData, MentionLabelDescriptor, MentionLabelValue } from "../types"
|
||||
import type { DateMentionData, MentionLabelDescriptor, MentionLabelValue } from "../types"
|
||||
import type { RelativeDateDefinition } from "./dateMentionConfig"
|
||||
import { RELATIVE_DATE_DEFINITIONS } from "./dateMentionConfig"
|
||||
|
||||
|
|
@ -123,12 +123,12 @@ export const createDateMentionData = ({
|
|||
id?: string
|
||||
range: DateRange
|
||||
label?: MentionLabelDescriptor
|
||||
labelOptions?: MentionData["labelOptions"]
|
||||
labelOptions?: DateMentionData["labelOptions"]
|
||||
translate: LabelTranslator
|
||||
locale: string
|
||||
withRangeKey: I18nKeysForAi
|
||||
displayName?: string
|
||||
}): MentionData => {
|
||||
}): DateMentionData => {
|
||||
const value = formatRangeValue(range)
|
||||
const baseLabel = displayName ?? resolveMentionLabel(label, translate) ?? value
|
||||
let resolvedName = baseLabel
|
||||
|
|
@ -170,7 +170,7 @@ export const parseRangeValue = (value: string): DateRange | null => {
|
|||
}
|
||||
|
||||
export const getDateMentionDisplayName = (
|
||||
mention: Pick<MentionData, "label" | "labelOptions" | "value" | "name">,
|
||||
mention: Pick<DateMentionData, "label" | "labelOptions" | "value" | "name">,
|
||||
translate: LabelTranslator,
|
||||
locale: string,
|
||||
withRangeKey: I18nKeysForAi,
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export const useMentionSearchService = () => {
|
|||
pushResult({
|
||||
id: item.id,
|
||||
name: item.title,
|
||||
type: item.type as MentionType,
|
||||
type: item.type,
|
||||
value: item.id,
|
||||
}),
|
||||
)
|
||||
|
|
@ -77,7 +77,7 @@ export const useMentionSearchService = () => {
|
|||
pushResult({
|
||||
id: item.id,
|
||||
name: item.title,
|
||||
type: item.type as MentionType,
|
||||
type: item.type,
|
||||
value: item.id,
|
||||
}),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
export type MentionType = "entry" | "feed" | "date" | "category" | "view" | "shortcut"
|
||||
import type { FeedViewType } from "@follow-app/client-sdk"
|
||||
|
||||
export type MentionLabelValue = string | number | boolean | MentionLabelDescriptor
|
||||
|
||||
|
|
@ -7,17 +7,55 @@ export interface MentionLabelDescriptor {
|
|||
values?: Record<string, MentionLabelValue>
|
||||
}
|
||||
|
||||
export interface MentionData {
|
||||
export interface MentionBaseData {
|
||||
id: string
|
||||
name: string
|
||||
type: MentionType
|
||||
value: unknown
|
||||
label?: MentionLabelDescriptor
|
||||
}
|
||||
|
||||
export interface EntryMentionData extends MentionBaseData {
|
||||
type: "entry"
|
||||
value: string
|
||||
}
|
||||
|
||||
export interface FeedMentionData extends MentionBaseData {
|
||||
type: "feed"
|
||||
value: string
|
||||
}
|
||||
|
||||
export interface DateMentionData extends MentionBaseData {
|
||||
type: "date"
|
||||
value: string
|
||||
labelOptions?: {
|
||||
appendRange?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface CategoryMentionData extends MentionBaseData {
|
||||
type: "category"
|
||||
value: string
|
||||
}
|
||||
|
||||
export interface ShortcutMentionData extends MentionBaseData {
|
||||
type: "shortcut"
|
||||
value: string
|
||||
}
|
||||
|
||||
export interface ViewMentionData extends MentionBaseData {
|
||||
type: "view"
|
||||
value: FeedViewType
|
||||
}
|
||||
|
||||
export type MentionData =
|
||||
| EntryMentionData
|
||||
| FeedMentionData
|
||||
| DateMentionData
|
||||
| CategoryMentionData
|
||||
| ShortcutMentionData
|
||||
| ViewMentionData
|
||||
|
||||
export type MentionType = MentionData["type"]
|
||||
|
||||
export interface MentionMatch {
|
||||
leadOffset: number
|
||||
matchingString: string
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import { getView } from "@follow/constants"
|
||||
import { getFeedById } from "@follow/store/feed/getter"
|
||||
import { getCategoryFeedIds } from "@follow/store/subscription/getter"
|
||||
|
||||
import { ROUTE_FEED_IN_FOLDER } from "~/constants"
|
||||
import { getRouteParams } from "~/hooks/biz/useRouteParams"
|
||||
import { getI18n } from "~/i18n"
|
||||
|
||||
import type { MentionData } from "../types"
|
||||
|
||||
|
|
@ -20,3 +23,32 @@ export function getMentionTextValue(mentionData: MentionData): string {
|
|||
|
||||
return `<mention-${type} id="${value}"></mention-${type}>`
|
||||
}
|
||||
|
||||
export function getMentionDisplayTextValue(mentionData: MentionData): string {
|
||||
const { type, value } = mentionData
|
||||
|
||||
switch (type) {
|
||||
case "category": {
|
||||
if (typeof value === "string" && value.startsWith(ROUTE_FEED_IN_FOLDER)) {
|
||||
const { view } = getRouteParams()
|
||||
const ids = getCategoryFeedIds(value.slice(ROUTE_FEED_IN_FOLDER.length), view)
|
||||
const feedNames = ids.map((id) => getFeedById(id)?.title).join(", ")
|
||||
return feedNames
|
||||
}
|
||||
return "Unknown Category"
|
||||
}
|
||||
|
||||
case "view": {
|
||||
const viewDef = getView(value)
|
||||
const viewKey = viewDef?.name
|
||||
|
||||
if (viewKey) {
|
||||
return getI18n().t(viewKey, { ns: "common" })
|
||||
}
|
||||
return "Unknown View"
|
||||
}
|
||||
default: {
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue