diff --git a/public/locales/ar/translation.json b/public/locales/ar/translation.json index 27088142..7c789d38 100644 --- a/public/locales/ar/translation.json +++ b/public/locales/ar/translation.json @@ -231,5 +231,20 @@ "export": "التصدير", "time-range": "النطاق الزمني", "all": "الكل", - "exporting": "التصدير..." + "exporting": "التصدير...", + "has-image": "لديه صورة", + "has-link": "لديه رابط", + "filter-settings": "إعدادات التصفية", + "tag-status": "حالة العلامة", + "all-notes": "جميع الملاحظات", + "with-tags": "مع العلامات", + "without-tags": "بدون علامات", + "select-tags": "حدد العلامات", + "additional-conditions": "الشروط الإضافية", + "apply-filter": "تطبيق الفلتر", + "to": "إلى", + "start-date": "تاريخ البدء", + "end-date": "تاريخ الانتهاء", + "reset": "إعادة تعيين", + "no-condition": "لا توجد حالة" } diff --git a/public/locales/de/translation.json b/public/locales/de/translation.json index ad279eda..61cde7de 100644 --- a/public/locales/de/translation.json +++ b/public/locales/de/translation.json @@ -227,5 +227,20 @@ "export": "Exportieren", "time-range": "Zeitspanne", "all": "Alle", - "exporting": "Exportieren..." + "exporting": "Exportieren...", + "has-image": "Hat Image", + "has-link": "Hat Link", + "filter-settings": "Filter-Einstellungen", + "tag-status": "Tag Status", + "all-notes": "Alle Anmerkungen", + "with-tags": "Mit Tags", + "without-tags": "Ohne Tags", + "select-tags": "Tags auswählen", + "additional-conditions": "Zusätzliche Bedingungen", + "apply-filter": "Filter anwenden", + "to": "An", + "start-date": "Datum des Beginns", + "end-date": "Enddatum", + "reset": "Zurücksetzen", + "no-condition": "Keine Bedingung" } diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index f7b157ba..25bd4ad6 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -238,5 +238,20 @@ "export-format": "Export Format", "time-range": "Time Range", "all": "All", - "exporting": "Exporting..." + "exporting": "Exporting...", + "tag-status": "Tag Status", + "all-notes": "All Notes", + "with-tags": "With Tags", + "without-tags": "Without Tags", + "select-tags": "Select Tags", + "additional-conditions": "Additional Conditions", + "apply-filter": "Apply Filter", + "has-image": "Has Image", + "has-link": "Has Link", + "filter-settings": "Filter Settings", + "to": "To", + "reset": "Reset", + "start-date": "Start Date", + "end-date": "End Date", + "no-condition": "No Condition" } diff --git a/public/locales/es/translation.json b/public/locales/es/translation.json index 990b45cf..f47b2aa7 100644 --- a/public/locales/es/translation.json +++ b/public/locales/es/translation.json @@ -227,5 +227,20 @@ "export": "Exportar", "time-range": "Intervalo de tiempo", "all": "Todos", - "exporting": "Exportar..." + "exporting": "Exportar...", + "has-image": "Tiene Imagen", + "has-link": "Tiene enlace", + "filter-settings": "Ajustes de filtro", + "tag-status": "Estado de la etiqueta", + "all-notes": "Todas las notas", + "with-tags": "Con etiquetas", + "without-tags": "Sin etiquetas", + "select-tags": "Seleccionar etiquetas", + "additional-conditions": "Condiciones adicionales", + "apply-filter": "Aplicar filtro", + "to": "A", + "start-date": "Fecha de inicio", + "end-date": "Fecha final", + "reset": "Restablecer", + "no-condition": "Ninguna condición" } diff --git a/public/locales/fr/translation.json b/public/locales/fr/translation.json index 3819fa9e..ec2f4a26 100644 --- a/public/locales/fr/translation.json +++ b/public/locales/fr/translation.json @@ -229,5 +229,20 @@ "export": "Exportation", "time-range": "Plage de temps", "all": "Tous", - "exporting": "Exporter..." + "exporting": "Exporter...", + "has-image": "A l'image", + "has-link": "A le lien", + "filter-settings": "Paramètres du filtre", + "tag-status": "Statut de l'étiquette", + "all-notes": "Toutes les notes", + "with-tags": "Avec Tags", + "without-tags": "Sans étiquette", + "select-tags": "Sélectionner les étiquettes", + "additional-conditions": "Conditions supplémentaires", + "apply-filter": "Appliquer le filtre", + "to": "Pour", + "start-date": "Date de début", + "end-date": "Date de fin", + "reset": "Remise à zéro", + "no-condition": "Pas de condition" } diff --git a/public/locales/ja/translation.json b/public/locales/ja/translation.json index f3111773..931e3e29 100644 --- a/public/locales/ja/translation.json +++ b/public/locales/ja/translation.json @@ -226,5 +226,20 @@ "export": "輸出", "time-range": "時間範囲", "all": "すべて", - "exporting": "輸出..." + "exporting": "輸出...", + "has-image": "イメージ", + "has-link": "リンクあり", + "filter-settings": "フィルター設定", + "tag-status": "タグステータス", + "all-notes": "すべての注意事項", + "with-tags": "タグ付き", + "without-tags": "タグなし", + "select-tags": "タグを選択", + "additional-conditions": "追加条件", + "apply-filter": "フィルタを適用する", + "to": "へ", + "start-date": "開始日", + "end-date": "終了日", + "reset": "リセット", + "no-condition": "コンディションなし" } diff --git a/public/locales/ko/translation.json b/public/locales/ko/translation.json index 46fdc74d..498201ad 100644 --- a/public/locales/ko/translation.json +++ b/public/locales/ko/translation.json @@ -228,5 +228,20 @@ "export": "내보내기", "time-range": "시간 범위", "all": "모두", - "exporting": "내보내기..." + "exporting": "내보내기...", + "has-image": "이미지 있음", + "has-link": "링크 있음", + "filter-settings": "필터 설정", + "tag-status": "태그 상태", + "all-notes": "모든 메모", + "with-tags": "태그 포함", + "without-tags": "태그 없음", + "select-tags": "태그 선택", + "additional-conditions": "추가 조건", + "apply-filter": "필터 적용", + "to": "To", + "start-date": "시작 날짜", + "end-date": "종료 날짜", + "reset": "초기화", + "no-condition": "조건 없음" } diff --git a/public/locales/pt/translation.json b/public/locales/pt/translation.json index 98d9fa59..85fe596c 100644 --- a/public/locales/pt/translation.json +++ b/public/locales/pt/translation.json @@ -225,5 +225,20 @@ "export": "Exportação", "time-range": "Intervalo de tempo", "all": "Todos", - "exporting": "Exportar..." + "exporting": "Exportar...", + "has-image": "Tem imagem", + "has-link": "Tem ligação", + "filter-settings": "Definições do filtro", + "tag-status": "Estado da etiqueta", + "all-notes": "Todas as notas", + "with-tags": "Com etiquetas", + "without-tags": "Sem etiquetas", + "select-tags": "Selecionar etiquetas", + "additional-conditions": "Condições adicionais", + "apply-filter": "Aplicar filtro", + "to": "Para", + "start-date": "Data de início", + "end-date": "Data final", + "reset": "Reiniciar", + "no-condition": "Sem condição" } diff --git a/public/locales/ru/translation.json b/public/locales/ru/translation.json index 082c1be1..711add65 100644 --- a/public/locales/ru/translation.json +++ b/public/locales/ru/translation.json @@ -230,5 +230,20 @@ "export": "Экспорт", "time-range": "Диапазон времени", "all": "Все", - "exporting": "Экспорт..." + "exporting": "Экспорт...", + "has-image": "Имеет изображение", + "has-link": "Имеет ссылку", + "filter-settings": "Настройки фильтра", + "tag-status": "Статус тега", + "all-notes": "Все заметки", + "with-tags": "С тегами", + "without-tags": "Без тегов", + "select-tags": "Выберите теги", + "additional-conditions": "Дополнительные условия", + "apply-filter": "Применить фильтр", + "to": "На", + "start-date": "Дата начала", + "end-date": "Дата окончания", + "reset": "Сброс", + "no-condition": "Нет условий" } diff --git a/public/locales/zh-TW/translation.json b/public/locales/zh-TW/translation.json index 06735f1e..155b099d 100644 --- a/public/locales/zh-TW/translation.json +++ b/public/locales/zh-TW/translation.json @@ -230,5 +230,20 @@ "export": "导出", "time-range": "时间范围", "all": "全部", - "exporting": "正在导出..." + "exporting": "正在导出...", + "has-image": "有图像", + "has-link": "有链接", + "filter-settings": "过滤器设置", + "tag-status": "标签状态", + "all-notes": "所有说明", + "with-tags": "带标签", + "without-tags": "无标签", + "select-tags": "选择标签", + "additional-conditions": "附加条件", + "apply-filter": "应用过滤器", + "to": "至", + "start-date": "开始日期", + "end-date": "结束日期", + "reset": "重置", + "no-condition": "无条件" } diff --git a/public/locales/zh/translation.json b/public/locales/zh/translation.json index 7150b116..969f8eb5 100644 --- a/public/locales/zh/translation.json +++ b/public/locales/zh/translation.json @@ -114,7 +114,7 @@ "set-as-public": "设置为公开", "unset-as-public": "取消公开", "with-link": "带链接", - "has-file": "有文件", + "has-file": "包含文件", "no-tag": "无标签", "created-at": "在", "user-list": "用户列表", @@ -165,7 +165,7 @@ "if-you-have-a-lot-of-notes-you-may-consume-a-certain-number-of-tokens": "如果您有大量笔记,您可能会消耗一定数量的代币", "force-rebuild": "强制重建", "force-rebuild-embedding-index": "强制重建将完全重建已索引的所有数据", - "embedding-model-description": "切换嵌入式模型后必须重建索引", + "embedding-model-description": "切换��入式模型后必须重建索引", "top-k-description": "最终返回文件的最大数量", "embedding-score-description": "查询的相似性阈值通常是欧氏总和距离", "embedding-lambda-description": "查询结果多样性加权参数", @@ -234,5 +234,21 @@ "export": "导出", "time-range": "时间范围", "all": "全部", - "exporting": "正在导出..." + "exporting": "正在导出...", + "filter-settings": "筛选设置", + "date-range": "时间范围", + "start-date": "开始日期", + "end-date": "结束日期", + "tag-status": "标签状态", + "all-notes": "所有笔记", + "with-tags": "包含标签", + "without-tags": "不包含标签", + "select-tags": "选择标签", + "additional-conditions": "附加条件", + "has-image": "包含图片", + "has-link": "包含链接", + "apply-filter": "应用筛选", + "to": "至", + "reset": "重置", + "no-condition": "无条件" } diff --git a/src/components/BlinkoCard/cardHeader.tsx b/src/components/BlinkoCard/cardHeader.tsx index c3a61a51..c787a988 100644 --- a/src/components/BlinkoCard/cardHeader.tsx +++ b/src/components/BlinkoCard/cardHeader.tsx @@ -72,7 +72,9 @@ export const CardHeader = ({ blinkoItem, blinko, isShareMode, isExpanded }: Card content={blinkoItem.content + `\n${blinkoItem.attachments?.map(i => window.location.origin + i.path).join('\n')}`} /> - + {!isShareMode && ( + + )} {blinkoItem.isTop && ( { + if (isShareMode) return; blinko.curSelectedNote = _.cloneDeep(blinkoItem); }; const handleDoubleClick = (e: React.MouseEvent) => { + if (isShareMode) return; blinko.curSelectedNote = _.cloneDeep(blinkoItem); ShowEditBlinkoModel(); FocusEditor(true) diff --git a/src/components/BlinkoCard/noteContent.tsx b/src/components/BlinkoCard/noteContent.tsx index 93f86785..61b9752f 100644 --- a/src/components/BlinkoCard/noteContent.tsx +++ b/src/components/BlinkoCard/noteContent.tsx @@ -2,26 +2,30 @@ import { MarkdownRender } from '@/components/Common/MarkdownRender'; import { FilesAttachmentRender } from "../Common/AttachmentRender"; import { Note } from '@/server/types'; import { BlinkoStore } from '@/store/blinkoStore'; +import { observer } from 'mobx-react-lite'; interface NoteContentProps { blinkoItem: Note; blinko: BlinkoStore; isExpanded?: boolean; + isShareMode?: boolean; } -export const NoteContent = ({ blinkoItem, blinko, isExpanded }: NoteContentProps) => { +export const NoteContent = observer(({ blinkoItem, blinko, isExpanded, isShareMode }: NoteContentProps) => { return ( <> { + if (isShareMode) return; blinkoItem.content = newContent blinko.upsertNote.call({ id: blinkoItem.id, content: newContent, refresh: false }) }} + isShareMode={isShareMode} />
); -}; \ No newline at end of file +}); \ No newline at end of file diff --git a/src/components/Common/Editor/editorStore.ts b/src/components/Common/Editor/editorStore.ts index f0e86708..4edfb37d 100644 --- a/src/components/Common/Editor/editorStore.ts +++ b/src/components/Common/Editor/editorStore.ts @@ -44,10 +44,8 @@ export class EditorStore { } replaceMarkdownTag = (text: string, forceFocus = false) => { - console.log('replaceMarkdownTag', this.mdxEditorRef) if (this.mdxEditorRef?.current) { if (this.lastRange) { - console.log('replaceMarkdownTag', this.lastRangeText) const currentTextBeforeRange = this.lastRangeText.replace(/ /g, " ") ?? '' const currentText = this.mdxEditorRef?.current!.getMarkdown().replace(/\\/g, '').replace(/ /g, " ") const tag = currentTextBeforeRange.replace(helper.regex.isEndsWithHashTag, "#" + text + ' ') @@ -170,12 +168,13 @@ export class EditorStore { handlePopTag = () => { const selection = window.getSelection(); if (selection!.rangeCount > 0) { - if (!IsTagSelectVisible()) { - let lastRange = selection!.getRangeAt(0); - this.lastRange = lastRange - this.lastRangeText = lastRange.endContainer.textContent?.slice(0, lastRange.endOffset) ?? '' - this.lastSelection = selection - } + // if (!IsTagSelectVisible()) { + let lastRange = selection!.getRangeAt(0); + this.lastRange = lastRange + this.lastRangeText = lastRange.endContainer.textContent?.slice(0, lastRange.endOffset) ?? '' + // console.log('this.lastRangeText', this.lastRangeText) + this.lastSelection = selection + // } const hasHashTagRegex = /#[^\s#]+/g const endsWithBankRegex = /\s$/g const currentText = this.lastRange?.startContainer.textContent?.slice(0, this.lastRange?.endOffset) ?? '' diff --git a/src/components/Common/MarkdownRender/Code.tsx b/src/components/Common/MarkdownRender/Code.tsx index de8b5dc5..4c8cf34e 100644 --- a/src/components/Common/MarkdownRender/Code.tsx +++ b/src/components/Common/MarkdownRender/Code.tsx @@ -12,15 +12,17 @@ interface CodeProps { export const Code = ({ className, children, ...props }: CodeProps) => { const { theme } = useTheme() const match = /language-(\w+)/.exec(className || ''); - - return match ? ( + + const shouldHighlight = !className || className?.includes('language-') || className?.includes('hljs'); + + return shouldHighlight ? (
void, content: string) => { - return children.map((child: any, index: number) => { + const childrenArray = Array.isArray(children) ? children : [children]; + + return childrenArray?.map((child: any, index: number) => { if (child?.type === 'input') { let text = ''; + let originalText = ''; let renderItem: React.ReactElement[] = []; const siblings = children.filter((_, i) => i !== index); - const textContent = typeof siblings[0] === 'string' ? siblings[0] : ''; + + const textContent = siblings.map(sibling => { + if (typeof sibling === 'string') { + return sibling; + } + if (sibling?.props?.children) { + return sibling.props.children; + } + return ''; + }).join(''); + + const originalTextContent = siblings.map(sibling => { + if (typeof sibling === 'string') { + return sibling; + } + if (sibling?.props?.href) { + return `[${sibling.props.children}](${sibling.props.href})`; + } + if (sibling?.props?.children) { + return sibling.props.children; + } + return ''; + }).join(''); + text = textContent; - renderItem.push({textContent}); + originalText = originalTextContent; + + renderItem.push( + + {siblings.map((sibling, i) => + typeof sibling === 'string' ? sibling : React.cloneElement(sibling, { key: `sibling-${i}` }) + )} + + ); + const isChecked = child.props?.checked ?? false; const iconType = isChecked ? "lets-icons:check-fill" : "ci:radio-unchecked"; + return (
{ const newContent = isChecked - ? content.replace(`* [x]${text}`, `* [ ]${text}`).replace(`- [x]${text}`, `- [ ]${text}`) - : content.replace(`* [ ]${text}`, `* [x]${text}`).replace(`- [ ]${text}`, `- [x]${text}`); + ? content.replace(`* [x]${originalText}`, `* [ ]${originalText}`).replace(`- [x]${originalText}`, `- [ ]${originalText}`) + : content.replace(`* [ ]${originalText}`, `* [x]${originalText}`).replace(`- [ ]${originalText}`, `- [x]${originalText}`); onChange(newContent) }}> -
+
{renderItem}
@@ -44,12 +80,10 @@ const renderListItems = (children: any, onChange: (newContent: string) => void, ); } if (typeof child === 'string') { - return null; } if (child?.props?.children) { return renderListItems(child.props.children, onChange, content); } - return null; }).filter(Boolean); }; @@ -58,6 +92,7 @@ export const ListItem = ({ children, content, onChange }: ListItemProps) => { //@ts-ignore return
    {renderListItems(children, onChange, content)}
; } catch (error) { + console.log(error) return
  • {children}
  • ; } }; \ No newline at end of file diff --git a/src/components/Common/MarkdownRender/index.tsx b/src/components/Common/MarkdownRender/index.tsx index a6c652eb..70edcc07 100644 --- a/src/components/Common/MarkdownRender/index.tsx +++ b/src/components/Common/MarkdownRender/index.tsx @@ -19,6 +19,7 @@ import { ListItem } from './ListItem'; import dynamic from 'next/dynamic'; import { Skeleton } from '@nextui-org/react'; import { TableWrapper } from './TableWrapper'; +import { useRouter } from 'next/router'; const MermaidWrapper = dynamic(() => import('./MermaidWrapper').then(mod => mod.MermaidWrapper), { loading: () => , @@ -35,7 +36,8 @@ const EchartsWrapper = dynamic(() => import('./EchartsWrapper'), { ssr: false }); -const highlightTags = (text) => { +const HighlightTags = observer(({ text, }: { text: any}) => { + const { pathname } = useRouter() if (!text) return text try { const lines = text?.split("\n"); @@ -43,10 +45,15 @@ const highlightTags = (text) => { const parts = line.split(" "); const processedParts = parts.map((part, index) => { if (part.startsWith('#') && part.length > 1 && part.match(helper.regex.isContainHashTag)) { + const isShareMode = pathname.includes('share') + if (isShareMode) return {part + " "} return ( - { - RootStore.Get(BlinkoStore).forceQuery++ - }} href={`/all?searchText=${part}`}> + { + if (isShareMode) return; + RootStore.Get(BlinkoStore).forceQuery++ + }} href={`/all?searchText=${part}`}> {part + " "} ); @@ -59,13 +66,13 @@ const highlightTags = (text) => { } catch (e) { return text } -}; +}); const Table = ({ children }: { children: React.ReactNode }) => { return
    {children}
    ; }; -export const MarkdownRender = observer(({ content = '', onChange, }: { content?: string, onChange?: (newContent: string) => void }) => { +export const MarkdownRender = observer(({ content = '', onChange, isShareMode }: { content?: string, onChange?: (newContent: string) => void, isShareMode?: boolean }) => { const { theme } = useTheme() const contentRef = useRef(null); @@ -91,7 +98,7 @@ export const MarkdownRender = observer(({ content = '', onChange, }: { content?: }] ]} components={{ - p: ({ node, children }) =>

    {highlightTags(children)}

    , + p: ({ node, children }) =>

    , code: ({ node, className, children, ...props }) => { const match = /language-(\w+)/.exec(className || ''); const language = match ? match[1] : ''; diff --git a/src/components/Common/PopoverFloat/filterPop.tsx b/src/components/Common/PopoverFloat/filterPop.tsx new file mode 100644 index 00000000..3121292f --- /dev/null +++ b/src/components/Common/PopoverFloat/filterPop.tsx @@ -0,0 +1,247 @@ +import { Icon } from "@iconify/react"; +import { Card, Popover, PopoverContent, PopoverTrigger, Select, SelectItem, Button, Radio, RadioGroup, Chip, Autocomplete, AutocompleteItem } from "@nextui-org/react"; +import { useTranslation } from "react-i18next"; +import { RootStore } from "@/store"; +import { BlinkoStore } from "@/store/blinkoStore"; +import { useState } from "react"; +import { RangeCalendar } from "@nextui-org/react"; +import { today, getLocalTimeZone } from "@internationalized/date"; +import dayjs from "@/lib/dayjs"; + +export default function FilterPop() { + const { t } = useTranslation(); + const blinkoStore = RootStore.Get(BlinkoStore); + + const [dateRange, setDateRange] = useState<{ + start: any; + end: any; + }>({ + start: null, + end: null + }); + const [focusedValue, setFocusedValue] = useState(today(getLocalTimeZone())); + const [tagStatus, setTagStatus] = useState("all"); + const [selectedTag, setSelectedTag] = useState(null); + const [selectedCondition, setSelectedCondition] = useState(null); + + const conditions = [ + { label: t('has-link'), value: 'hasLink' }, + { label: t('has-file'), value: 'hasFile' }, + ]; + + const handleApplyFilter = () => { + blinkoStore.noteListFilterConfig = { + ...blinkoStore.noteListFilterConfig, + startDate: dateRange.start ? new Date(dateRange.start.toString()) : null, + endDate: dateRange.end ? new Date(dateRange.end.toString()) : null, + tagId: selectedTag ? Number(selectedTag) : null, + withoutTag: tagStatus === 'without', + withFile: selectedCondition === 'hasFile', + withLink: selectedCondition === 'hasLink', + }; + blinkoStore.noteList.resetAndCall({}); + }; + + const handleReset = () => { + setDateRange({ start: null, end: null }); + setTagStatus("all"); + setSelectedTag(null); + setSelectedCondition(null); + + blinkoStore.noteListFilterConfig = { + ...blinkoStore.noteListFilterConfig, + startDate: null, + endDate: null, + tagId: null, + withoutTag: false, + withFile: false, + withLink: false, + }; + blinkoStore.noteList.resetAndCall({}); + }; + + return ( + + + + + +
    +
    +
    + + {t('time-range')} +
    + + +
    + +
    + + {dateRange.start ? dayjs(new Date(dateRange.start.toString())).format('YYYY-MM-DD') : t('start-date')} + + {t('to')} + + {dateRange.end ? dayjs(new Date(dateRange.end.toString())).format('YYYY-MM-DD') : t('end-date')} + +
    +
    +
    + +
    + +
    +
    +
    +
    + +
    +
    + + {t('tag-status')} +
    + +
    + + {tagStatus === "with" && ( +
    +
    + + {t('select-tags')} +
    + + { + const tag = blinkoStore.tagList.value?.falttenTags.find(t => t.id === Number(selectedTag)); + return tag?.icon ? ( +
    {tag.icon}
    + ) : ( + + ); + })() + ) : ( + + ) + } + onSelectionChange={(key) => setSelectedTag(key as string)} + > + {(tag) => ( + +
    + {tag.icon ? ( +
    {tag.icon}
    + ) : ( + + )} + {tag.name} +
    +
    + )} +
    +
    + )} + +
    +
    + + {t('additional-conditions')} +
    + + {t('no-condition')} + {conditions.map(condition => ( + + {condition.label} + + ))} + +
    + +
    + + +
    +
    +
    +
    + ); +} \ No newline at end of file diff --git a/src/components/Layout/index.tsx b/src/components/Layout/index.tsx index 4aa30ff6..f0d1724b 100644 --- a/src/components/Layout/index.tsx +++ b/src/components/Layout/index.tsx @@ -23,6 +23,7 @@ import AiWritePop from "../Common/PopoverFloat/aiWritePop"; import { createPortal } from "react-dom"; import { Sidebar } from "./Sidebar"; import { MobileNavBar } from "./MobileNavBar"; +import FilterPop from "../Common/PopoverFloat/filterPop"; export const SideBarItem = "p-2 flex flex-row items-center cursor-pointer gap-2 hover:bg-hover rounded-xl transition-all" @@ -162,35 +163,7 @@ export const CommonLayout = observer(({ /> } /> - - - - - -
    - blinkoStore.forceQuery++}> - - -
    {t('with-link')}
    -
    - - - blinkoStore.forceQuery++}> - - -
    {t('no-tag')}
    -
    - - - blinkoStore.forceQuery++}> - - -
    {t('has-file')}
    -
    - -
    -
    -
    + {blinkoStore.dailyReviewNoteList.value?.length != 0 && diff --git a/src/pages/signin.tsx b/src/pages/signin.tsx index 47a19fc2..1d3c95fc 100644 --- a/src/pages/signin.tsx +++ b/src/pages/signin.tsx @@ -74,12 +74,16 @@ export default function Component() { const twoFactorRes = await SignInTwoFactor.call(code) if (twoFactorRes?.ok) { RootStore.Get(DialogStore).close() + userStorage.setValue(user) + passwordStorage.setValue(password) router.push('/') } else { RootStore.Get(ToastPlugin).error(twoFactorRes?.error ?? t('user-or-password-error')) } }, SignInTwoFactor.loading.value) } else { + userStorage.setValue(user) + passwordStorage.setValue(password) router.push('/') } } else { diff --git a/src/server/plugins/ai/aiPrompt.ts b/src/server/plugins/ai/aiPrompt.ts index a9100581..7735af0a 100644 --- a/src/server/plugins/ai/aiPrompt.ts +++ b/src/server/plugins/ai/aiPrompt.ts @@ -16,8 +16,6 @@ export class AiPrompt { Important: - Respond in the SAME LANGUAGE as the input content - Use Markdown format - - Replace all spaces with - - Use two line breaks between paragraphs - Ensure line breaks between list items`, polish: `You are a professional text editor. Your task is to polish and optimize the given text: @@ -33,7 +31,6 @@ export class AiPrompt { Important: - Respond in the SAME LANGUAGE as the input content - Use Markdown format - - Replace all spaces with - Use two line breaks between paragraphs - Ensure line breaks between list items`, @@ -43,11 +40,12 @@ export class AiPrompt { 3. Maintain professional writing standards 4. Follow technical documentation best practices when needed + Original content: + {content} + Important: - Respond in the SAME LANGUAGE as the input content - Use Markdown format - - Replace all spaces with - - Use two line breaks between paragraphs - Ensure line breaks between list items - Use appropriate Markdown elements (code blocks, tables, lists, etc.)` }; diff --git a/src/server/plugins/memos.ts b/src/server/plugins/memos.ts index 249d91ac..033b38dc 100644 --- a/src/server/plugins/memos.ts +++ b/src/server/plugins/memos.ts @@ -137,7 +137,7 @@ export class Memos { } if (row?.blob) { - //@ts-ignore!!!!!!! + //@ts-ignore const { filePath } = await FileService.uploadFile(row!.blob, row?.filename); await prisma.attachments.create({ data: { diff --git a/src/server/routers/note.ts b/src/server/routers/note.ts index 63658cc9..d749b134 100644 --- a/src/server/routers/note.ts +++ b/src/server/routers/note.ts @@ -34,6 +34,8 @@ export const noteRouter = router({ withFile: z.boolean().default(false).optional(), withLink: z.boolean().default(false).optional(), isUseAiQuery: z.boolean().default(false).optional(), + startDate: z.union([z.date(), z.null()]).default(null).optional(), + endDate: z.union([z.date(), z.null()]).default(null).optional(), })) .output(z.array(notesSchema.merge( z.object({ @@ -48,7 +50,7 @@ export const noteRouter = router({ })) )) .mutation(async function ({ input, ctx }) { - const { tagId, type, isArchived, isRecycle, searchText, page, size, orderBy, withFile, withoutTag, withLink, isUseAiQuery } = input + const { tagId, type, isArchived, isRecycle, searchText, page, size, orderBy, withFile, withoutTag, withLink, isUseAiQuery, startDate, endDate } = input if (isUseAiQuery && searchText?.trim() != '') { if (page == 1) { return await AiService.enhanceQuery({ query: searchText!, ctx }) @@ -88,6 +90,12 @@ export const noteRouter = router({ if (withoutTag) { where.tags = { none: {} } } + if (startDate) { + where.createdAt = { gte: startDate } + } + if (endDate) { + where.createdAt = { lte: endDate } + } if (withLink) { where.OR = [ { content: { contains: 'http://', mode: 'insensitive' } }, @@ -304,12 +312,21 @@ export const noteRouter = router({ } if (needTobeAddedRelationTags.length != 0) { - await prisma.tagsToNote.createMany({ - data: needTobeAddedRelationTags.map(i => { - const [name, parent] = i.split('') - return { noteId: note.id, tagId: newTags.find(t => (t.name == name) && (t.parent == Number(parent)))!.id } - }) - }) + for (const relationTag of needTobeAddedRelationTags) { + const [name, parent] = relationTag.split(''); + const tagId = newTags.find(t => (t.name == name) && (t.parent == Number(parent)))?.id; + if (tagId) { + try { + await prisma.tagsToNote.create({ + data: { noteId: note.id, tagId } + }); + } catch (error) { + if (error.code !== 'P2002') { + throw error; + } + } + } + } } // add new references diff --git a/src/server/routers/tag.ts b/src/server/routers/tag.ts index 3bc4188c..ae8cd848 100644 --- a/src/server/routers/tag.ts +++ b/src/server/routers/tag.ts @@ -79,8 +79,8 @@ export const tagRouter = router({ deleteOnlyTag: authProcedure.use(demoAuthMiddleware) .meta({ openapi: { - method: 'POST', path: '/v1/tags/delete-only-tag', summary: 'Only delete tag name' - , description: 'Only delete tag name and remove tag from notes, but not delete notes', protect: true, tags: ['Tag'] + method: 'POST', path: '/v1/tags/delete-only-tag', summary: 'Only delete tag name', + description: 'Only delete tag name and remove tag from notes, but not delete notes', protect: true, tags: ['Tag'] } }) .input(z.object({ @@ -97,6 +97,7 @@ export const tagRouter = router({ include: { tagsToNote: true } }) + console.log({ tag }, '+++++++++++++++++++++++') if (!tag) return true const allNotesId = tag.tagsToNote.map(i => i.noteId) @@ -104,15 +105,62 @@ export const tagRouter = router({ for (const noteId of allNotesId) { const note = await prisma.notes.findFirst({ where: { id: noteId } }) if (!note) continue + + const getAllTagIdsInChain = async (tagId: number): Promise => { + const result: number[] = [tagId]; + + let currentTag = await prisma.tag.findFirst({ where: { id: tagId } }); + while (currentTag && currentTag.parent !== 0) { + result.push(currentTag.parent); + currentTag = await prisma.tag.findFirst({ where: { id: currentTag.parent } }); + } + + const childTags = await prisma.tag.findMany({ where: { parent: tagId } }); + for (const childTag of childTags) { + const childChain = await getAllTagIdsInChain(childTag.id); + result.push(...childChain); + } + + return [...new Set(result)]; + }; + + const tagIdsInChain = await getAllTagIdsInChain(tag.id); + await prisma.notes.update({ where: { id: note.id }, - data: { content: note.content.replace(new RegExp(`#${tag.name}`, 'g'), '') } + data: { + content: note.content.replace( + new RegExp(`#[^\\s]*${tag.name}(/[^\\s]*)?(?=\\s|$)`, 'g'), + '' + ).replace(/\s+/g, ' ').trim() + } }) - await prisma.tagsToNote.deleteMany({ where: { tagId: tag.id } }) - } - await prisma.tag.delete({ where: { id } }) + await prisma.tagsToNote.deleteMany({ + where: { + noteId: note.id, + tagId: { + in: tagIdsInChain + } + } + }) + + for (const tagId of tagIdsInChain) { + const tagExists = await prisma.tag.findFirst({ + where: { id: tagId } + }); + + if (tagExists) { + const tagUsageCount = await prisma.tagsToNote.count({ + where: { tagId } + }); + if (tagUsageCount === 0) { + await prisma.tag.delete({ where: { id: tagId } }); + } + } + } + } return true }), deleteTagWithAllNote: authProcedure.use(demoAuthMiddleware) diff --git a/src/store/blinkoStore.tsx b/src/store/blinkoStore.tsx index fddad933..a584348a 100644 --- a/src/store/blinkoStore.tsx +++ b/src/store/blinkoStore.tsx @@ -1,4 +1,3 @@ - import { _ } from '@/lib/lodash'; import { useEffect } from 'react'; import { PromisePageState, PromiseState } from './standard/PromiseState'; @@ -43,7 +42,9 @@ export class BlinkoStore implements Store { withoutTag: false, withFile: false, withLink: false, - isUseAiQuery: false + isUseAiQuery: false, + startDate: null as Date | null, + endDate: null as Date | null, } noteTypeDefault: NoteType = NoteType.BLINKO currentCommonFilter: filterType | null = null @@ -235,6 +236,8 @@ export class BlinkoStore implements Store { this.noteListFilterConfig.withFile = false this.noteListFilterConfig.searchText = searchText ?? '' this.noteListFilterConfig.isRecycle = false + this.noteListFilterConfig.startDate = null + this.noteListFilterConfig.endDate = null if (router.pathname == '/notes') { this.noteListFilterConfig.type = NoteType.NOTE