diff --git a/.blinko/files/pic01.png b/.blinko/files/pic01.png deleted file mode 100644 index 0b56278..0000000 Binary files a/.blinko/files/pic01.png and /dev/null differ diff --git a/.blinko/files/pic02.png b/.blinko/files/pic02.png deleted file mode 100644 index 0de9150..0000000 Binary files a/.blinko/files/pic02.png and /dev/null differ diff --git a/.blinko/files/pic03.png b/.blinko/files/pic03.png deleted file mode 100644 index 00c5d9e..0000000 Binary files a/.blinko/files/pic03.png and /dev/null differ diff --git a/.blinko/files/pic04.png b/.blinko/files/pic04.png deleted file mode 100644 index 7796fa8..0000000 Binary files a/.blinko/files/pic04.png and /dev/null differ diff --git a/.blinko/files/pic06.png b/.blinko/files/pic06.png deleted file mode 100644 index c0ba876..0000000 Binary files a/.blinko/files/pic06.png and /dev/null differ diff --git a/.blinko/files/story.txt b/.blinko/files/story.txt deleted file mode 100644 index e69de29..0000000 diff --git a/.github/workflows/docker-image.yml b/.github/workflows/build-and-push-release-image.yml similarity index 97% rename from .github/workflows/docker-image.yml rename to .github/workflows/build-and-push-release-image.yml index f5020cb..b962dcd 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/build-and-push-release-image.yml @@ -1,4 +1,4 @@ -name: Build and Push Docker Image +name: Build and Push Release Docker Image on: push: diff --git a/.github/workflows/build-and-push-test-image.yml b/.github/workflows/build-and-push-test-image.yml new file mode 100644 index 0000000..31ad259 --- /dev/null +++ b/.github/workflows/build-and-push-test-image.yml @@ -0,0 +1,62 @@ +name: Build and Push Test Docker Image + +on: + push: + branches: [main] +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Extract build args + run: | + echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV + + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ github.token }} + + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v3 + with: + install: true + version: v0.9.1 + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: | + blinkospace/blinko + ghcr.io/blinko-space/blinko + tags: | + type=raw,value=${{ env.VERSION }} + flavor: | + latest=false + + - name: Build and Push + id: docker_build + uses: docker/build-push-action@v6 + with: + context: ./ + file: Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.gitignore b/.gitignore index 3106dcc..8e2f878 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,7 @@ yarn.lock public/robots.txt public/sitemap* public/assets +.blinko/faiss/* +.blinko/pgdump/* +.blinko/files/* dump.sql \ No newline at end of file diff --git a/next-env.d.ts b/next-env.d.ts index 7b88d7d..725dd6f 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,5 +1,6 @@ /// /// /// + // NOTE: This file should not be edited // see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. diff --git a/next.config.js b/next.config.js index 354b72a..e9dff5f 100644 --- a/next.config.js +++ b/next.config.js @@ -1,3 +1,4 @@ +const isProduction = process.env.NODE_ENV === 'production'; module.exports = { transpilePackages: ['@mdxeditor/editor', 'react-diff-view'], webpack: (config, { isServer }) => { @@ -10,9 +11,10 @@ module.exports = { } return config; }, - reactStrictMode: true, + outputFileTracing: true, + reactStrictMode: isProduction? true : false, swcMinify:false, eslint: { ignoreDuringBuilds: true, }, -} +} \ No newline at end of file diff --git a/package.json b/package.json index b1cd694..d95472c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blinko", - "version": "0.0.3", + "version": "0.0.4", "private": true, "browser": { "fs": false, @@ -20,7 +20,7 @@ "db-reset": "prisma migrate dev reset", "dev": "next dev -p 1111", "build": "set NODE_ENV=production & next build", - "start": "next start -p 1111", + "start": "prisma migrate & prisma db seed & next start -p 1111", "lint": "next lint", "analyze": "ANALYZE=true next build", "postinstall": "pnpm generate" @@ -49,6 +49,7 @@ "@types/mime-types": "^2.1.4", "adm-zip": "^0.5.16", "axios": "^1.7.7", + "canvas-confetti": "^1.9.3", "clsx": "^2.1.1", "cron": "^3.1.7", "dataloader": "^2.2.2", @@ -102,6 +103,7 @@ "remark-gfm": "^4.0.0", "superjson": "^2.2.1", "swagger-ui-react": "^5.17.14", + "swiper": "^11.1.14", "tailwind-merge": "^1.13.0", "trpc-to-openapi": "^2.0.2", "typed-emitter": "^2.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ed2e150..cd6749d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -80,6 +80,9 @@ importers: axios: specifier: ^1.7.7 version: 1.7.7 + canvas-confetti: + specifier: ^1.9.3 + version: 1.9.3 clsx: specifier: ^2.1.1 version: 2.1.1 @@ -239,6 +242,9 @@ importers: swagger-ui-react: specifier: ^5.17.14 version: 5.17.14(@types/react@18.2.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + swiper: + specifier: ^11.1.14 + version: 11.1.14 tailwind-merge: specifier: ^1.13.0 version: 1.14.0 @@ -3328,6 +3334,9 @@ packages: caniuse-lite@1.0.30001669: resolution: {integrity: sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==} + canvas-confetti@1.9.3: + resolution: {integrity: sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g==} + ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -6472,6 +6481,10 @@ packages: react: '>=16.8.0 <19' react-dom: '>=16.8.0 <19' + swiper@11.1.14: + resolution: {integrity: sha512-VbQLQXC04io6AoAjIUWuZwW4MSYozkcP9KjLdrsG/00Q/yiwvhz9RQyt0nHXV10hi9NVnDNy1/wv7Dzq1lkOCQ==} + engines: {node: '>= 4.7.0'} + tabbable@6.2.0: resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} @@ -11315,6 +11328,8 @@ snapshots: caniuse-lite@1.0.30001669: {} + canvas-confetti@1.9.3: {} + ccount@2.0.1: {} chalk@2.4.2: @@ -14827,6 +14842,8 @@ snapshots: - '@types/react' - debug + swiper@11.1.14: {} + tabbable@6.2.0: {} tailwind-merge@1.14.0: {} diff --git a/prisma/migrations/20241027072348_0_0_3/migration.sql b/prisma/migrations/20241027072348_0_0_3/migration.sql new file mode 100644 index 0000000..46f9ec6 --- /dev/null +++ b/prisma/migrations/20241027072348_0_0_3/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "notes" ADD COLUMN "isReviewed" BOOLEAN NOT NULL DEFAULT false; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 23544e0..bef6e62 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -48,6 +48,7 @@ model notes { isRecycle Boolean @default(false) isShare Boolean @default(false) isTop Boolean @default(false) + isReviewed Boolean @default(false) sharePassword String @default("") @db.VarChar metadata Json? @db.Json users Int @default(0) diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 547d99a..e253869 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -87,5 +87,9 @@ "every-half-year": "Every half year", "import": "Import", "impoort-from-bko": "Import from .bko", - "not-a-bko-file": "not a bko file" + "not-a-bko-file": "not a bko file", + "convert-to-note": "Convert to Note", + "convert-to-blinko": "Convert to Blinko", + "reviewed": "Reviewed", + "congratulations-youve-reviewed-everything-today": "you've reviewed everything today." } diff --git a/public/locales/zh/translation.json b/public/locales/zh/translation.json index d3740f0..5f9c5d5 100644 --- a/public/locales/zh/translation.json +++ b/public/locales/zh/translation.json @@ -86,5 +86,9 @@ "every-day": "每天", "import": "导入", "impoort-from-bko": "从.bko文件导入", - "not-a-bko-file": "不是 bko 文件" + "not-a-bko-file": "不是 bko 文件", + "convert-to-note": "转换为笔记", + "convert-to-blinko": "转换为闪念", + "reviewed": "已回顾", + "congratulations-youve-reviewed-everything-today": "恭喜你,今天你已经回顾了所有内容" } diff --git a/src/components/BlinkoRightClickMenu/index.tsx b/src/components/BlinkoRightClickMenu/index.tsx index 6569cb1..b8f92e0 100644 --- a/src/components/BlinkoRightClickMenu/index.tsx +++ b/src/components/BlinkoRightClickMenu/index.tsx @@ -12,14 +12,15 @@ import { DialogStore } from "@/store/module/Dialog"; import { BlinkoEditor } from "../BlinkoEditor"; import { useEffect, useState } from "react"; import { NoteType } from "@/server/types"; +import { reaction } from "mobx"; export const BlinkoRightClickMenu = observer(() => { const { t } = useTranslation(); const blinko = RootStore.Get(BlinkoStore) - const [forceUpdate, setForceUpdate] = useState(0) const store = RootStore.Local(() => ({ editorHeight: 90, editBlinko() { + console.log(123) RootStore.Get(DialogStore).setData({ size: '2xl', isOpen: true, @@ -28,13 +29,8 @@ export const BlinkoRightClickMenu = observer(() => { }) }, })) - useEffect(() => { - setForceUpdate(forceUpdate + 1) - }, [blinko.curSelectedNote]) return - { - store.editBlinko() - }}> + { store.editBlinko() }}>
{t('edit')}
@@ -42,7 +38,7 @@ export const BlinkoRightClickMenu = observer(() => { { blinko.isMultiSelectMode = true - blinko.onMultiSelectNote(blinko.curSelectedNote.id) + blinko.onMultiSelectNote(blinko.curSelectedNote?.id!) }}>
@@ -53,32 +49,32 @@ export const BlinkoRightClickMenu = observer(() => { { blinko.upsertNote.call({ - id: blinko.curSelectedNote.id, - type: blinko.curSelectedNote.type == NoteType.NOTE ? NoteType.BLINKO : NoteType.NOTE + id: blinko.curSelectedNote?.id, + type: blinko.curSelectedNote?.type == NoteType.NOTE ? NoteType.BLINKO : NoteType.NOTE }) }}> - {forceUpdate &&
+
{t('convert-to')} {blinko.curSelectedNote?.type == NoteType.NOTE ? {t('blinko')} : {t('note')}}
-
} +
{ blinko.upsertNote.call({ id: blinko.curSelectedNote?.id, isArchived: !blinko.curSelectedNote?.isArchived }) }}> - {forceUpdate &&
+
{blinko.curSelectedNote?.isArchived ? t('recovery') : t('archive')} -
} +
{ - PromiseCall(api.notes.deleteMany.mutate({ ids: [blinko.curSelectedNote?.id] })) - api.ai.embeddingDelete.mutate({ id: blinko.curSelectedNote?.id }) + PromiseCall(api.notes.deleteMany.mutate({ ids: [blinko.curSelectedNote?.id!] })) + api.ai.embeddingDelete.mutate({ id: blinko.curSelectedNote?.id! }) }}>
diff --git a/src/components/Common/Editor/attachmentsRender.tsx b/src/components/Common/Editor/attachmentsRender.tsx index a16bc95..e263414 100644 --- a/src/components/Common/Editor/attachmentsRender.tsx +++ b/src/components/Common/Editor/attachmentsRender.tsx @@ -19,9 +19,10 @@ import { type Attachment } from '@/server/types'; type IProps = { files: FileType[] preview?: boolean + columns?: number } -const AttachmentsRender = observer(({ files, preview = false }: IProps) => { +const AttachmentsRender = observer(({ files, preview = false, columns = 3 }: IProps) => { const { t } = useTranslation() const store = RootStore.Local(() => ({ deleteFile: new PromiseState({ @@ -51,7 +52,7 @@ const AttachmentsRender = observer(({ files, preview = false }: IProps) => { }) return <> -
+
{files?.filter(i => i.isImage).map((file, index) => (
@@ -80,7 +81,7 @@ const AttachmentsRender = observer(({ files, preview = false }: IProps) => {
-
+
{files?.filter(i => !i.isImage).map((file, index) => (
{ if (preview) { @@ -98,12 +99,12 @@ const AttachmentsRender = observer(({ files, preview = false }: IProps) => { }) -const FilesAttachmentRender = observer(({ files, preview }: { files: Attachment[], preview?: boolean }) => { +const FilesAttachmentRender = observer(({ files, preview, columns }: { files: Attachment[], preview?: boolean, columns?: number }) => { const [handledFiles, setFiles] = useState([]) useEffect(() => { setFiles(HandleFileType(files)) }, [files]) - return + return }) export { AttachmentsRender, FilesAttachmentRender } diff --git a/src/components/Layout/index.tsx b/src/components/Layout/index.tsx index be42669..83fab04 100644 --- a/src/components/Layout/index.tsx +++ b/src/components/Layout/index.tsx @@ -1,5 +1,5 @@ import React, { use, useEffect, useRef, useState, useTransition } from "react"; -import { Avatar, Button, ScrollShadow, Spacer, Image, useDisclosure, Input, Popover, PopoverTrigger, PopoverContent, Card } from "@nextui-org/react"; +import { Avatar, Button, ScrollShadow, Spacer, Image, useDisclosure, Input, Popover, PopoverTrigger, PopoverContent, Card, Badge } from "@nextui-org/react"; import { Icon } from "@iconify/react"; import { UserStore } from "@/store/user"; import Link from "next/link"; @@ -103,7 +103,7 @@ export const CommonLayout = observer(({ setisOpen(false)}> {content} -
+
{/* nav bar */}
@@ -130,7 +130,7 @@ export const CommonLayout = observer(({ fullWidth variant="flat" aria-label="search" - className={`ml-auto w-[150px] md:w-[300px]`} + className={`ml-auto w-[200px] md:w-[300px]`} classNames={{ base: "px-1 mr-1 w-[full] md:w-[300px]", inputWrapper: @@ -165,6 +165,12 @@ export const CommonLayout = observer(({
+ {blinkoStore.dailyReviewNoteList.value?.length != 0 && + + + + + }
{header} diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index ac99974..a2d7a1f 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -17,7 +17,10 @@ import { initStore } from '@/store/init'; import { Inspector, InspectParams } from 'react-dev-inspector'; import { CommonLayout } from '@/components/Layout'; import { AppProvider } from '@/store/module/AppProvider'; +import { RootStore } from '@/store'; +import { BlinkoStore } from '@/store/blinkoStore'; const MyApp = ({ Component, pageProps }) => { + console.log('use MyApp') initStore(); useProgressBar(); return ( diff --git a/src/pages/archived.tsx b/src/pages/archived.tsx index 122e198..d81ba74 100644 --- a/src/pages/archived.tsx +++ b/src/pages/archived.tsx @@ -2,7 +2,7 @@ import { observer } from "mobx-react-lite"; import Home from "."; const Page = observer(() => { - return + return }); export default Page \ No newline at end of file diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 98ceb91..cc58dc4 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -4,7 +4,7 @@ import { _ } from '@/lib/lodash'; import { observer } from 'mobx-react-lite'; import Masonry from 'react-masonry-css' import { useTranslation } from 'react-i18next'; -import { useEffect, useState } from 'react'; +import { useEffect } from 'react'; import { RootStore } from '@/store'; import { motion } from "framer-motion" import { FilesAttachmentRender } from '@/components/Common/Editor/attachmentsRender'; @@ -15,25 +15,26 @@ import { useRouter } from 'next/router'; import { BlinkoEditor } from '@/components/BlinkoEditor'; import { BlinkoMultiSelectPop } from '@/components/BlinkoMultiSelectPop'; import dayjs from '@/lib/dayjs'; -import { PromiseCall } from '@/store/standard/PromiseState'; -import { api } from '@/lib/trpc'; import { BlinkoRightClickMenu } from '@/components/BlinkoRightClickMenu'; import { NoteType } from '@/server/types'; import { ScrollArea } from '@/components/Common/ScrollArea'; -const Home = observer(({ type, isArchived }: { type?: number | null, isArchived?: boolean | null }) => { +const Home = observer(() => { const { t } = useTranslation(); const blinko = RootStore.Get(BlinkoStore) const router = useRouter(); const { tagId } = router.query; - useEffect(() => { if (!router.isReady) return - blinko.noteListFilterConfig.type = type ? Number(type) : 0 - blinko.noteTypeDefault = type ? Number(type) : 0 + blinko.noteListFilterConfig.type = NoteType.BLINKO + blinko.noteTypeDefault = NoteType.BLINKO blinko.noteListFilterConfig.tagId = null blinko.noteListFilterConfig.isArchived = false + if (router.pathname == '/notes') { + blinko.noteListFilterConfig.type = NoteType.NOTE + blinko.noteTypeDefault = NoteType.NOTE + } if (tagId) { console.log({ tagId }) blinko.noteListFilterConfig.tagId = Number(tagId) as number @@ -41,17 +42,17 @@ const Home = observer(({ type, isArchived }: { type?: number | null, isArchived? if (router.pathname == '/all') { blinko.noteListFilterConfig.type = -1 } - if (isArchived) { + if (router.pathname == '/archived') { blinko.noteListFilterConfig.type = -1 blinko.noteListFilterConfig.isArchived = true } blinko.noteList.resetAndCall({}) - }, [type, tagId]) + }, [router.isReady]) const store = RootStore.Local(() => ({ editorHeight: 75, get showEditor() { - return !isArchived + return !blinko.noteListFilterConfig.isArchived }, get showLoadAll() { return blinko.noteList.isLoadAll @@ -87,21 +88,17 @@ const Home = observer(({ type, isArchived }: { type?: number | null, isArchived? columnClassName="my-masonry-grid_column"> { blinko.noteList?.value?.map(i => { - return + return -
{ - blinko.curSelectedNote = _.cloneDeep(i) - }} +
{ + blinko.curSelectedNote = _.cloneDeep(i) + }} onClick={() => { if (blinko.isMultiSelectMode) { blinko.onMultiSelectNote(i.id) } }}> - +
{dayjs(i.createdAt).fromNow()}
diff --git a/src/pages/notes.tsx b/src/pages/notes.tsx index 2dc580c..5631616 100644 --- a/src/pages/notes.tsx +++ b/src/pages/notes.tsx @@ -2,7 +2,7 @@ import { observer } from "mobx-react-lite"; import Home from "."; const Page = observer(() => { - return + return }); export default Page \ No newline at end of file diff --git a/src/pages/review.tsx b/src/pages/review.tsx new file mode 100644 index 0000000..bded1a1 --- /dev/null +++ b/src/pages/review.tsx @@ -0,0 +1,158 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { Swiper, SwiperSlide, } from "swiper/react"; +import "swiper/css"; +import "swiper/css/effect-cards"; +import { EffectCards } from 'swiper/modules'; +import '../styles/swiper-cards.css' +import { observer } from 'mobx-react-lite'; +import { RootStore } from '@/store'; +import { BlinkoStore } from '@/store/blinkoStore'; +import { MarkdownRender } from '@/components/Common/MarkdownRender'; +import dayjs from '@/lib/dayjs'; +import { Note, NoteType } from '@/server/types'; +import { Icon } from '@iconify/react'; +import { useTranslation } from 'react-i18next'; +import { Button, Tooltip } from '@nextui-org/react'; +import { LightningIcon, NotesIcon } from '@/components/Common/Icons'; +import { PromiseCall } from '@/store/standard/PromiseState'; +import { api } from '@/lib/trpc'; +import { showTipsDialog } from '@/components/Common/TipsDialog'; +import { DialogStore } from '@/store/module/Dialog'; +import confetti from 'canvas-confetti' +import { useMediaQuery } from 'usehooks-ts'; +import { FilesAttachmentRender } from '@/components/Common/Editor/attachmentsRender'; +const App = observer(() => { + const blinko = RootStore.Get(BlinkoStore) + const swiperRef = useRef(null); + const { t } = useTranslation() + const isPc = useMediaQuery('(min-width: 768px)') + const store = RootStore.Local(() => ({ + currentIndex: 0, + currentNote: null as Note | null, + handleSlideChange: async (_swiper) => { + console.log(_swiper) + store.currentIndex = _swiper.activeIndex + }, + get isBlinko() { + return store.currentNote?.type == NoteType.BLINKO + } + })) + + useEffect(() => { + store.currentNote = blinko.dailyReviewNoteList.value?.[store.currentIndex] ?? null + if (blinko.dailyReviewNoteList.value?.length == 0) { + confetti({ + particleCount: 100, + spread: 70, + origin: { y: 0.6, x: isPc ? 0.6 : 0.5 } + }); + } + }, [blinko.dailyReviewNoteList.value]) + + return ( +
+ + { + blinko.dailyReviewNoteList.value?.length != 0 && <> + { + //@ts-ignore + swiperRef.current = swiper; + }} + onSlideChange={(swiper) => store.handleSlideChange(swiper)} + onReachEnd={e => { }} + effect={"cards"} + grabCursor={true} + modules={[EffectCards]} + className="mt-10 md:mt-4 w-[300px] h-[380px] md:w-[350px] md:h-[520px]" + allowSlideNext={true} + allowSlidePrev={true} + touchRatio={1} + resistance={true} + resistanceRatio={0.5} + slidesPerView={1} + centeredSlides={true} + > + { + blinko.dailyReviewNoteList.value?.map((i, index) => ( + +
+
+
{dayjs(i.createdAt).fromNow()}
+ { + store.isBlinko ? +
+ +
{t('blinko')}
+
: +
+ +
{t('note')}
+
+ } +
+ +
+ +
+
+
+ )) + } +
+ +
+ + + + + { + if (!store.currentNote) return + await blinko.upsertNote.call({ id: store.currentNote.id, isArchived: true }) + await blinko.dailyReviewNoteList.call() + }}> + + + + +
+ + } + + {blinko.dailyReviewNoteList.value?.length == 0 &&
+ +
{t('congratulations-youve-reviewed-everything-today')}
+
} +
+ + ); +}) + +export default App; diff --git a/src/server/routers/note.ts b/src/server/routers/note.ts index bcb821c..5d25c85 100644 --- a/src/server/routers/note.ts +++ b/src/server/routers/note.ts @@ -38,6 +38,20 @@ export const noteRouter = router({ include: { tags: true, attachments: true } }) }), + dailyReviewNoteList: authProcedure + .input(z.void()) + .query(async function () { + return await prisma.notes.findMany({ + where: { createdAt: { gt: new Date(new Date().getTime() - 24 * 60 * 60 * 1000) }, isReviewed: false }, + orderBy: { id: 'desc' }, + include: { attachments: true } + }) + }), + reviewNote: authProcedure + .input(z.number()) + .mutation(async function ({ input }) { + return await prisma.notes.update({ where: { id: input }, data: { isReviewed: true } }) + }), upsert: authProcedure .input(z.object({ content: z.string().default(''), diff --git a/src/server/types.ts b/src/server/types.ts index 8e5eb1f..0fbe8b2 100644 --- a/src/server/types.ts +++ b/src/server/types.ts @@ -2,7 +2,7 @@ import { type Decimal } from "@prisma/client/runtime/library" import { RouterOutput, RouterInput } from "./routers/_app" import { z } from "zod" -export type Note = NonNullable +export type Note = Partial> export type Attachment = NonNullable[0] & { size: any } export type Tag = NonNullable[0] export type Config = NonNullable diff --git a/src/store/aiStore.tsx b/src/store/aiStore.tsx index 6d503bf..9cdec4f 100644 --- a/src/store/aiStore.tsx +++ b/src/store/aiStore.tsx @@ -1,12 +1,12 @@ import { _ } from '@/lib/lodash'; -import { makeAutoObservable } from 'mobx'; import { Store } from './standard/base'; import { ToastPlugin } from './module/Toast/Toast'; import { RootStore } from './root'; import { streamApi } from '@/lib/trpc'; import { StorageListState } from './standard/StorageListState'; import { Note } from '@/server/types'; +import { makeAutoObservable } from 'mobx'; type Chat = { content: string @@ -15,10 +15,10 @@ type Chat = { } export class AiStore implements Store { + sid = 'AiStore'; constructor() { makeAutoObservable(this) } - sid = 'AiStore'; noteContent = ''; aiSearchText = ''; aiSearchResult = { @@ -53,14 +53,6 @@ export class AiStore implements Store { relationNotes: Note[] = [] chatHistory = new StorageListState({ key: 'chatHistory' }) private abortController = new AbortController() - async onBottom() { - // await this.noteList.callNextPage({}) - } - - loadAllData() { - // this.noteList.resetAndCall({}) - // this.tagList.call() - } async completionsStream() { try { diff --git a/src/store/baseStore.ts b/src/store/baseStore.ts index 2229c09..d6f08a5 100644 --- a/src/store/baseStore.ts +++ b/src/store/baseStore.ts @@ -1,10 +1,14 @@ import dayjs from 'dayjs'; import { Store } from './standard/base'; import { StorageState } from './standard/StorageState'; +import { makeAutoObservable } from 'mobx'; export class BaseStore implements Store { sid = 'BaseStore'; - autoObservable = true; + constructor() { + makeAutoObservable(this) + } + locale = new StorageState({ key: 'language', default: 'en' }); locales = [ { value: 'en', label: 'English' }, diff --git a/src/store/blinkoStore.tsx b/src/store/blinkoStore.tsx index 9f14296..1c7fa90 100644 --- a/src/store/blinkoStore.tsx +++ b/src/store/blinkoStore.tsx @@ -1,6 +1,5 @@ import { _ } from '@/lib/lodash'; -import { makeAutoObservable } from 'mobx'; import { useEffect } from 'react'; import { PromisePageState, PromiseState } from './standard/PromiseState'; import { Store } from './standard/base'; @@ -13,6 +12,7 @@ import { api } from '@/lib/trpc'; import { type RouterOutput } from '@/server/routers/_app'; import { Attachment, NoteType, type Note } from '@/server/types'; import { DBBAK_TASK_NAME } from '@/lib/constant'; +import { makeAutoObservable } from 'mobx'; type filterType = { label: string; @@ -21,14 +21,14 @@ type filterType = { } export class BlinkoStore implements Store { - constructor() { - makeAutoObservable(this) - } sid = 'BlinkoStore'; noteContent = ''; - curSelectedNote: RouterOutput['notes']['list'][0]; + curSelectedNote: Note | null = null; curMultiSelectIds: number[] = []; isMultiSelectMode: boolean = false; + constructor() { + makeAutoObservable(this) + } onMultiSelectNote(id: number) { if (this.curMultiSelectIds.includes(id)) { @@ -46,7 +46,6 @@ export class BlinkoStore implements Store { this.updateTicker++ } - autoObservable = true; routerList = [ { title: "blinko", @@ -86,13 +85,6 @@ export class BlinkoStore implements Store { searchText: "" } noteTypeDefault: NoteType = NoteType.BLINKO - commonFilterList: filterType[] = [ - { label: '最热门', sortBy: 'views', direction: "desc" }, - { label: '最新发布', sortBy: "createdAt", direction: "desc" }, - { label: '最年轻', sortBy: "age", direction: "asc" }, - { label: '价格最高', sortBy: "price", direction: "desc" }, - { label: '价格最低', sortBy: "price", direction: "asc" }, - ] currentCommonFilter: filterType | null = null currentRouter = this.routerList[0] updateTicker = 0 @@ -120,12 +112,18 @@ export class BlinkoStore implements Store { noteList = new PromisePageState({ function: async ({ page, size }) => { - // await new Promise(resolve => setTimeout(resolve, 2000)) const notes = await api.notes.list.query({ ...this.noteListFilterConfig, page, size }) + console.log(notes) return notes.map(i => { return { ...i, isExpand: false } }) } }) + dailyReviewNoteList = new PromiseState({ + function: async () => { + return await api.notes.dailyReviewNoteList.query() + } + }) + resourceList = new PromisePageState({ size: 30, function: async ({ page, size }) => { @@ -188,21 +186,30 @@ export class BlinkoStore implements Store { await this.noteList.callNextPage({}) } - loadAllData() { + firstLoad() { + this.tagList.call() + this.config.call() + this.dailyReviewNoteList.call() + this.public.call() + this.task.call() + } + + refreshData() { this.tagList.call() this.noteList.resetAndCall({}) this.config.call() + this.dailyReviewNoteList.call() } use() { useEffect(() => { - this.loadAllData() - this.public.call() - this.task.call() + console.log('running') + this.firstLoad() }, []) useEffect(() => { - this.loadAllData() + if (this.updateTicker == 0) return + this.refreshData() }, [this.updateTicker]) } } diff --git a/src/store/module/Dialog/index.tsx b/src/store/module/Dialog/index.tsx index 9e1397d..c2aba13 100644 --- a/src/store/module/Dialog/index.tsx +++ b/src/store/module/Dialog/index.tsx @@ -1,9 +1,9 @@ import React from "react"; -import { makeAutoObservable } from "mobx"; import Provider from "./Provider"; import { ModalSlots, SlotsToClasses } from "@nextui-org/react"; import { Store } from "@/store/standard/base"; import { RootStore } from "@/store/root"; +import { makeAutoObservable } from "mobx"; export class DialogStore implements Store { sid = "DialogStore"; @@ -26,7 +26,7 @@ export class DialogStore implements Store { ...args?.classNames } Object.assign(this, args, { classNames }); - makeAutoObservable(this); + makeAutoObservable(this) } diff --git a/src/store/standard/PaginationState.ts b/src/store/standard/PaginationState.ts deleted file mode 100644 index 31d8540..0000000 --- a/src/store/standard/PaginationState.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { makeAutoObservable } from 'mobx'; - -type ArgsType = { page: number; limit: number; total: number; onPageChange: (page: number) => void }; - -export class PaginationState { - page = 1; - limit = 10; - total = 0; - - constructor(args: Partial) { - Object.assign(this, args); - makeAutoObservable(this); - } - - get offset() { - return (this.page - 1) * this.limit; - } - - onPageChange(page: number) {} - - setData(v: Partial) { - Object.assign(this, v); - if (v.page) { - this.onPageChange(v.page); - } - } -} diff --git a/src/store/user.ts b/src/store/user.ts index 69981bd..cd25323 100644 --- a/src/store/user.ts +++ b/src/store/user.ts @@ -5,11 +5,13 @@ import { useRouter } from 'next/router'; import { RootStore } from './root'; import { Store } from './standard/base'; import { eventBus } from '@/lib/event'; +import { makeAutoObservable } from 'mobx'; export class UserStore implements User, Store { sid = 'user'; - autoObservable = true; - + constructor() { + makeAutoObservable(this) + } id: string = ''; name?: string = ''; nickname?: string = ''; diff --git a/src/styles/swiper-cards.css b/src/styles/swiper-cards.css new file mode 100644 index 0000000..7849d51 --- /dev/null +++ b/src/styles/swiper-cards.css @@ -0,0 +1,14 @@ + +/* .swiper { + width: 240px; + height: 320px; +} */ + +.swiper-slide { + display: flex; + align-items: center; + justify-content: center; + border-radius: 18px; + font-size: 22px; + font-weight: bold; +}