add 0125-2239

This commit is contained in:
ditiqi 2025-01-25 22:39:22 +08:00
parent 8f43808ea2
commit b8a0721358
11 changed files with 258 additions and 227 deletions

View File

@ -30,12 +30,13 @@
"@floating-ui/react": "^0.26.25",
"@heroicons/react": "^2.2.0",
"@hookform/resolvers": "^3.9.1",
"@multiavatar/multiavatar": "^1.0.7",
"@nice/client": "workspace:^",
"@nice/common": "workspace:^",
"@nice/iconer": "workspace:^",
"@nice/theme": "workspace:^",
"@nice/utils": "workspace:^",
"@nice/ui": "workspace:^",
"@nice/utils": "workspace:^",
"@tanstack/query-async-storage-persister": "^5.51.9",
"@tanstack/react-query": "^5.51.21",
"@tanstack/react-query-persist-client": "^5.51.9",

View File

@ -1,4 +1,4 @@
import React, { useCallback, useState } from "react";
import { useCallback, useState } from "react";
import {
UploadOutlined,
CheckCircleOutlined,
@ -23,13 +23,14 @@ interface UploadingFile {
export const TusUploader = ({ value = [], onChange }: TusUploaderProps) => {
const { handleFileUpload } = useTusUpload();
const [uploadingFiles, setUploadingFiles] = useState<UploadingFile[]>([]);
const [completedFiles, setCompletedFiles] = useState<UploadingFile[]>(() =>
value?.map(fileId => ({
name: `File ${fileId}`, // We could fetch the actual filename if needed
progress: 1,
status: 'done' as const,
fileId
})) || []
const [completedFiles, setCompletedFiles] = useState<UploadingFile[]>(
() =>
value?.map((fileId) => ({
name: `文件 ${fileId}`, // 可以根据需要获取实际文件名
progress: 1,
status: "done" as const,
fileId,
})) || []
);
const [uploadResults, setUploadResults] = useState<string[]>(value || []);
@ -38,7 +39,7 @@ export const TusUploader = ({ value = [], onChange }: TusUploaderProps) => {
setCompletedFiles((prev) =>
prev.filter((f) => f.fileId !== fileId)
);
const newResults = uploadResults.filter(id => id !== fileId);
const newResults = uploadResults.filter((id) => id !== fileId);
setUploadResults(newResults);
onChange?.(newResults);
},
@ -48,10 +49,10 @@ export const TusUploader = ({ value = [], onChange }: TusUploaderProps) => {
const handleChange = useCallback(
async (fileList: UploadFile | UploadFile[]) => {
const files = Array.isArray(fileList) ? fileList : [fileList];
console.log("files", files);
// 验证文件对象
console.log("文件", files);
if (!files.every((f) => f instanceof File)) {
message.error("Invalid file format");
message.error("无效的文件格式");
return false;
}
@ -61,20 +62,19 @@ export const TusUploader = ({ value = [], onChange }: TusUploaderProps) => {
status: "uploading" as const,
}));
setUploadingFiles((prev) => [...prev, ...newFiles]);
const newUploadResults: string[] = [];
const newUploadResults: string[] = [];
try {
for (const [index, f] of files.entries()) {
if (!f) {
throw new Error(`File ${f.name} is invalid`);
throw new Error(`文件 ${f.name} 无效`);
}
const fileId = await new Promise<string>(
(resolve, reject) => {
handleFileUpload(
f as File,
(result) => {
console.log("Upload success:", result);
console.log("上传成功:", result);
const completedFile = {
name: f.name,
progress: 1,
@ -91,7 +91,7 @@ export const TusUploader = ({ value = [], onChange }: TusUploaderProps) => {
resolve(result.fileId);
},
(error) => {
console.error("Upload error:", error);
console.error("上传错误:", error);
reject(error);
}
);
@ -100,97 +100,93 @@ export const TusUploader = ({ value = [], onChange }: TusUploaderProps) => {
newUploadResults.push(fileId);
}
// Update with all uploaded files
const newValue = Array.from(new Set([...uploadResults, ...newUploadResults]));
const newValue = Array.from(
new Set([...uploadResults, ...newUploadResults])
);
setUploadResults(newValue);
onChange?.(newValue);
message.success(`${files.length} files uploaded successfully`);
message.success(`${files.length} 个文件上传成功`);
} catch (error) {
console.error("Upload error details:", error);
console.error("上传错误详情:", error);
message.error(
`Upload failed: ${error instanceof Error ? error.message : "Unknown error"}`
`上传失败: ${error instanceof Error ? error.message : "未知错误"}`
);
setUploadingFiles((prev) =>
prev.map((f) => ({ ...f, status: "error" }))
);
}
return false;
},
[uploadResults, onChange, handleFileUpload]
);
return (
<div className="space-y-4">
<div className="space-y-1">
<Upload.Dragger
name="files"
multiple
showUploadList={false}
beforeUpload={handleChange}
style={{
border: "2px dashed #1677ff",
borderRadius: "8px",
backgroundColor: "#f0f8ff",
}}>
style={{ background: "white" }}
beforeUpload={handleChange}>
<p className="ant-upload-drag-icon">
<UploadOutlined />
</p>
<p className="ant-upload-text">
Click or drag file to this area to upload
</p>
<p className="ant-upload-hint">
Support for a single or bulk upload of files
</p>
<p className="ant-upload-hint"></p>
{/* 正在上传的文件 */}
{(uploadingFiles.length > 0 || completedFiles.length > 0) && (
<div className=" p-2 border rounded bg-white mt-1">
{uploadingFiles.length > 0 &&
uploadingFiles.map((file, index) => (
<div
key={index}
className="flex flex-col gap-1">
<div className="flex items-center gap-2">
<div className="text-sm">
{file.name}
</div>
</div>
<Progress
percent={Math.round(
file.progress * 100
)}
status={
file.status === "error"
? "exception"
: file.status === "done"
? "success"
: "active"
}
/>
</div>
))}
{completedFiles.length > 0 &&
completedFiles.map((file, index) => (
<div
key={index}
className="flex items-center justify-between gap-2 py-2">
<div className="flex items-center gap-2">
<CheckCircleOutlined className="text-green-500" />
<div className="text-sm">
{file.name}
</div>
</div>
<Button
type="text"
danger
icon={<DeleteOutlined />}
onClick={() =>
file.fileId &&
handleRemoveFile(file.fileId)
}
/>
</div>
))}
</div>
)}
</Upload.Dragger>
{/* Uploading Files */}
{uploadingFiles.length > 0 && (
<div className="space-y-2 p-4 border rounded">
<div className="font-medium">Uploading Files</div>
{uploadingFiles.map((file, index) => (
<div key={index} className="flex flex-col gap-1">
<div className="flex items-center gap-2">
<div className="text-sm">{file.name}</div>
</div>
<Progress
percent={Math.round(file.progress * 100)}
status={
file.status === "error"
? "exception"
: file.status === "done"
? "success"
: "active"
}
/>
</div>
))}
</div>
)}
{/* Completed Files */}
{completedFiles.length > 0 && (
<div className="space-y-2 p-4 border rounded">
<div className="font-medium">Uploaded Files</div>
{completedFiles.map((file, index) => (
<div
key={index}
className="flex items-center justify-between gap-2 py-2">
<div className="flex items-center gap-2">
<CheckCircleOutlined className="text-green-500" />
<div className="text-sm">{file.name}</div>
</div>
<Button
type="text"
danger
icon={<DeleteOutlined />}
onClick={() =>
file.fileId && handleRemoveFile(file.fileId)
}
/>
</div>
))}
</div>
)}
</div>
);
};

View File

@ -4,7 +4,7 @@ import dayjs from "dayjs";
import { Avatar } from "antd";
import { useVisitor } from "@nice/client";
import { useContext, useState } from "react";
import { useContext, useEffect, useRef, useState } from "react";
import { PostDetailContext } from "./context/PostDetailContext";
import { LikeFilled, LikeOutlined } from "@ant-design/icons";
import PostLikeButton from "./PostHeader/PostLikeButton";
@ -24,51 +24,55 @@ export default function PostCommentCard({
<motion.div
className="bg-white rounded-lg shadow-sm border border-slate-200 p-4"
layout>
<div className="flex items-start space-x-3 gap-4">
<div className="flex items-start space-x-2 gap-2">
<div className="flex-shrink-0">
<CustomAvatar
src={post.author?.avatar}
size={40}
name={
!post.author?.avatar && post.author?.showname
}></CustomAvatar>
size={50}
name={!post.author?.avatar && post.author?.showname}
ip={post?.meta?.ip}></CustomAvatar>
</div>
<div className="flex-1 min-w-0">
<div className="flex flex-1 justify-between ">
<div className="flex space-x-2" style={{ height: 40 }}>
<span className="flex font-medium text-slate-900">
{post.author?.showname || "匿名用户"}
</span>
<span className="flex text-sm text-slate-500">
{dayjs(post?.createdAt).format(
"YYYY-MM-DD HH:mm"
<div className="flex-1">
<div className={`flex-1 min-w-0 `}>
<div className="flex flex-1 justify-between ">
<div className="flex space-x-2">
<span className="flex font-medium text-slate-900">
{post.author?.showname || "匿名用户"}
</span>
<span className="flex text-sm text-slate-500 ">
{dayjs(post?.createdAt).format(
"YYYY-MM-DD HH:mm"
)}
</span>
{isReceiverComment && (
<div className=" ">
<span className=" py-0.5 px-2 text-xs rounded-full bg-blue-100 text-blue-800">
</span>
</div>
)}
</span>
{isReceiverComment && (
<div className=" ">
<span className=" px-2 text-sm rounded-full bg-blue-100 text-blue-800">
</span>
</div>
{/* 添加有帮助按钮 */}
<div>
<div className="flex justify-center items-center gap-2">
<span className=" text-sm text-slate-500">{`#${index + 1}`}</span>
<PostLikeButton
post={post}></PostLikeButton>
</div>
)}
</div>
{/* 添加有帮助按钮 */}
<div>
<div className="flex justify-center items-center gap-2">
<span className=" text-sm text-slate-500">{`#${index + 1}`}</span>
<PostLikeButton post={post}></PostLikeButton>
</div>
</div>
</div>
<div
className="ql-editor text-slate-800"
style={{
padding: 0,
}}
dangerouslySetInnerHTML={{ __html: post.content || "" }}
/>
<PostResources post={post}></PostResources>
<div
className="ql-editor text-slate-800 mt-1"
style={{
padding: 0,
}}
dangerouslySetInnerHTML={{
__html: post.content || "",
}}
/>
<PostResources post={post}></PostResources>
</div>
</div>
</div>
</motion.div>

View File

@ -1,7 +1,6 @@
import React, { useContext, useState } from "react";
import { motion } from "framer-motion";
import { Button } from "antd";
import { Button, Tabs } from "antd";
import QuillEditor from "@web/src/components/common/editor/quill/QuillEditor";
import { PostDetailContext } from "./context/PostDetailContext";
import { usePost } from "@nice/client";
@ -10,12 +9,16 @@ import toast from "react-hot-toast";
import { isContentEmpty } from "./utils";
import { SendOutlined } from "@ant-design/icons";
import { TusUploader } from "@web/src/components/common/uploader/TusUploader";
const { TabPane } = Tabs;
export default function PostCommentEditor() {
const { post } = useContext(PostDetailContext);
const [content, setContent] = useState("");
const [isPreview, setIsPreview] = useState(false);
const [fileIds, setFileIds] = useState<string[]>([]);
const { create } = usePost();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (isContentEmpty(content)) {
@ -49,56 +52,51 @@ export default function PostCommentEditor() {
};
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="w-full mx-auto mt-4">
<form onSubmit={handleSubmit} className="space-y-3">
<div className="relative rounded-lg border border-slate-200 bg-white shadow-sm">
{!isPreview ? (
<QuillEditor
value={content}
onChange={setContent}
placeholder="写下你的回复..."
className="bg-transparent"
theme="snow"
minRows={6}
maxRows={12}
modules={{
toolbar: [
["bold", "italic", "strike"],
["blockquote", "code-block"],
[{ list: "ordered" }, { list: "bullet" }],
["link"],
["clean"],
],
<div className="w-full mx-auto mt-1">
<form onSubmit={handleSubmit} className="space-y-2">
<Tabs defaultActiveKey="1">
<TabPane tab="回复" key="1">
<div className="relative rounded-lg border border-slate-200 bg-white shadow-sm">
<QuillEditor
value={content}
onChange={setContent}
placeholder="写下你的回复..."
className="bg-transparent"
theme="snow"
minRows={6}
maxRows={12}
modules={{
toolbar: [
["bold", "italic", "strike"],
["blockquote", "code-block"],
[
{ list: "ordered" },
{ list: "bullet" },
],
["link"],
["clean"],
],
}}
style={
{
"--ql-border-color": "transparent",
"--ql-toolbar-bg": "rgb(248, 250, 252)", // slate-50
} as React.CSSProperties
}
/>
</div>
</TabPane>
<TabPane tab="附件" key="2">
<TusUploader
onChange={(value) => {
setFileIds(value);
}}
style={
{
"--ql-border-color": "transparent",
"--ql-toolbar-bg": "rgb(248, 250, 252)", // slate-50
} as React.CSSProperties
}
/>
) : (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className="ql-editor p-4 min-h-[120px] text-slate-800 prose prose-slate max-w-none quill-editor-container"
dangerouslySetInnerHTML={{ __html: content }}
/>
)}
</div>
<TusUploader
onChange={(value) => {
setFileIds(value);
}}></TusUploader>
</TabPane>
</Tabs>
<div className="flex items-center justify-end">
<motion.div
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}>
<div>
<Button
type="primary"
htmlType="submit"
@ -107,9 +105,9 @@ export default function PostCommentEditor() {
icon={<SendOutlined />}>
</Button>
</motion.div>
</div>
</div>
</form>
</motion.div>
</div>
);
}

View File

@ -1,10 +1,4 @@
import React, {
useContext,
useMemo,
useEffect,
useRef,
useCallback,
} from "react";
import { useContext, useMemo, useEffect } from "react";
import { PostDetailContext } from "./context/PostDetailContext";
import { api, useVisitor } from "@nice/client";
import { postDetailSelect, PostDto, PostType, Prisma } from "@nice/common";
@ -12,8 +6,6 @@ import { motion, AnimatePresence } from "framer-motion";
import PostCommentCard from "./PostCommentCard";
import { useInView } from "react-intersection-observer";
import { LoadingCard } from "@web/src/components/models/post/detail/LoadingCard";
import { Button } from "antd";
export default function PostCommentList() {
const { post } = useContext(PostDetailContext);
const { ref: loadMoreRef, inView } = useInView();
@ -155,7 +147,7 @@ export default function PostCommentList() {
}
return (
<div className="space-y-4 mt-6">
<div className="space-y-2 mt-2">
<AnimatePresence mode="popLayout">
{items.map((comment, index) => (
<motion.div

View File

@ -2,48 +2,59 @@ import { useContext } from "react";
import { useState, useRef, useEffect } from "react";
import { PostDetailContext } from "../context/PostDetailContext";
import { motion } from "framer-motion";
import { StatsSection } from "./StatsSection";
import PostResources from "../PostResources";
export default function Content() {
const { post, user } = useContext(PostDetailContext);
const [isExpanded, setIsExpanded] = useState(false);
const contentRef = useRef(null);
const contentWrapperRef = useRef(null);
const [shouldCollapse, setShouldCollapse] = useState(false);
useEffect(() => {
if (contentRef.current) {
const shouldCollapse = contentRef.current.scrollHeight > 300; // 300px threshold
if (contentWrapperRef.current) {
const shouldCollapse = contentWrapperRef.current.scrollHeight > 100;
setShouldCollapse(shouldCollapse);
}
}, [post?.content]);
return (
<div className="relative bg-white rounded-b-xl p-6 pt-2 shadow-lg border border-[#97A9C4]/30">
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.6 }}
className="text-secondary-700">
<div className="relative bg-white rounded-b-xl p-4 pt-2 shadow-lg border border-[#97A9C4]/30">
<div className="text-secondary-700">
{/* 包装整个内容区域的容器 */}
<div
ref={contentRef}
className={`ql-editor p-0 space-y-1 leading-relaxed duration-300 ${
ref={contentWrapperRef}
className={`duration-300 ${
shouldCollapse && !isExpanded
? "max-h-[300px] overflow-hidden"
? "max-h-[100px] overflow-hidden relative"
: ""
}`}
dangerouslySetInnerHTML={{
__html: post?.content || "",
}}
/>
}`}>
{/* 内容区域 */}
<div
className="ql-editor p-0 space-y-1 leading-relaxed"
dangerouslySetInnerHTML={{
__html: post?.content || "",
}}
/>
{/* PostResources 组件 */}
<PostResources post={post} />
{/* 渐变遮罩 */}
{shouldCollapse && !isExpanded && (
<div className="absolute bottom-0 left-0 right-0 h-20 bg-gradient-to-t from-white to-transparent" />
)}
</div>
{/* 展开/收起按钮 */}
{shouldCollapse && (
<button
onClick={() => setIsExpanded(!isExpanded)}
className="mt-2 text-blue-500 hover:text-blue-700">
{isExpanded ? "Collapse" : "Expand"}
{isExpanded ? "收起" : "展开"}
</button>
)}
<PostResources post={post} />
</motion.div>
</div>
{/* Stats Section */}
<StatsSection />

View File

@ -17,7 +17,7 @@ const { Title, Paragraph, Text } = Typography;
export default function Header() {
const { post, user } = useContext(PostDetailContext);
return (
<header className="rounded-t-xl bg-gradient-to-r from-primary to-primary-400 text-white p-6 relative">
<header className="rounded-t-xl bg-gradient-to-r from-primary to-primary-400 text-white p-4 relative">
{/* 右上角标签 */}
{/* <CornerBadge type="state" value={post?.state}></CornerBadge> */}

View File

@ -28,12 +28,12 @@ export default function PostResources({ post }: { post: PostDto }) {
}, [post]);
return (
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-4">
<div className="flex flex-col ">
<div className="flex flex-col ">
{resources
?.filter((resource) => resource.isImage)
.map((resource) => (
<div key={resource.url} className="mt-2">
<div key={resource.url} className="mt-0.5">
<Image
src={resource.url}
alt={resource.title}

View File

@ -40,11 +40,11 @@ export function LetterBasicForm() {
placeholder="请输入信件标题"
/>
</Form.Item>
{/* Tags Input */}
<Form.Item name={["meta", "tags"]} className="mb-6">
<Select
mode="tags"
showSearch={false}
placeholder="输入标签后按回车添加"
value={form.getFieldValue(["meta", "tags"]) || []}
onChange={(value) =>
@ -93,10 +93,7 @@ export function LetterBasicForm() {
</Form.Item>
{/* Footer Actions */}
<div className="flex flex-col-reverse sm:flex-row items-center justify-between gap-4 mt-6">
<Form.Item
name="isPublic"
valuePropName="checked"
>
<Form.Item name="isPublic" valuePropName="checked">
<Checkbox className="text-gray-600 hover:text-gray-900 transition-colors text-sm">
</Checkbox>

View File

@ -1,33 +1,57 @@
import React, { useMemo } from "react";
import { Avatar } from "antd";
import { AvatarProps } from "antd/lib/avatar";
import multiavatar from "@multiavatar/multiavatar";
interface CustomAvatarProps extends Omit<AvatarProps, "children"> {
src?: string;
name?: string;
ip?: string;
}
export function CustomAvatar({
src,
name,
className = "",
ip,
...props
}: CustomAvatarProps) {
// 获取名字的第一个字符,如果没有名字则显示"匿"
const firstChar = name ? name.charAt(0) : "匿";
// 如果没有 src且 name 不存在或为 "匿名用户",则使用 ip 生成随机头像
const generateAvatarFromIp = (ip: string) => {
// 使用 multiavatar 生成 SVG 字符串
const svgString = multiavatar(ip);
return `data:image/svg+xml;utf8,${encodeURIComponent(svgString)}`;
};
// 判断头像模式
const avatarMode = useMemo(() => {
if (src) {
return "avatar"; // 使用 src 提供的头像
}
if (name && name !== "匿名用户") {
return "name"; // 使用名称的首字母
}
return "random"; // 使用随机头像(基于 ip
}, [src, name]);
// 判断是否需要使用 ip 生成头像
const avatarSrc =
src || (name && name !== "匿名用户")
? src
: generateAvatarFromIp(ip || "default");
return (
<Avatar
className={`ring-2 ring-primary/50
bg-primary-300
text-white
className={`
${avatarMode ? "bg-primary-300text-white" : ""}
transition-all duration-200 ease-in-out shadow-md
hover:shadow-lg
${className}`}
shape="square"
src={src}
size={40}
src={avatarSrc}
{...props}>
{!src && firstChar}
{!avatarSrc && firstChar}
</Avatar>
);
}

View File

@ -12,7 +12,7 @@ importers:
dependencies:
'@nestjs/bullmq':
specifier: ^10.2.0
version: 10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)(bullmq@5.34.8)
version: 10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))(bullmq@5.34.8)
'@nestjs/common':
specifier: ^10.3.10
version: 10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1)
@ -33,7 +33,7 @@ importers:
version: 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.15)(rxjs@7.8.1)
'@nestjs/schedule':
specifier: ^4.1.0
version: 4.1.2(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)
version: 4.1.2(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))
'@nestjs/websockets':
specifier: ^10.3.10
version: 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)(@nestjs/platform-socket.io@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1)
@ -148,7 +148,7 @@ importers:
version: 10.2.3(chokidar@3.6.0)(typescript@5.7.2)
'@nestjs/testing':
specifier: ^10.0.0
version: 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)(@nestjs/platform-express@10.4.15)
version: 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15))
'@types/exceljs':
specifier: ^1.3.0
version: 1.3.2
@ -284,6 +284,9 @@ importers:
'@hookform/resolvers':
specifier: ^3.9.1
version: 3.10.0(react-hook-form@7.54.2(react@18.2.0))
'@multiavatar/multiavatar':
specifier: ^1.0.7
version: 1.0.7
'@nice/client':
specifier: workspace:^
version: link:../../packages/client
@ -1979,6 +1982,9 @@ packages:
cpu: [x64]
os: [win32]
'@multiavatar/multiavatar@1.0.7':
resolution: {integrity: sha512-Yg9Uw57bmlErsWL0CSv4d6D4ZqVBE00OZmYr9MRgygoXZdboNtsEI6FbBRw1AY8l88Sm1ARcyojtlm2uwUn0Zg==}
'@nestjs/bull-shared@10.2.3':
resolution: {integrity: sha512-XcgAjNOgq6b5DVCytxhR5BKiwWo7hsusVeyE7sfFnlXRHeEtIuC2hYWBr/ZAtvL/RH0/O0tqtq0rVl972nbhJw==}
peerDependencies:
@ -8638,15 +8644,17 @@ snapshots:
'@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3':
optional: true
'@nestjs/bull-shared@10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)':
'@multiavatar/multiavatar@1.0.7': {}
'@nestjs/bull-shared@10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))':
dependencies:
'@nestjs/common': 10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1)
'@nestjs/core': 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1)
tslib: 2.8.1
'@nestjs/bullmq@10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)(bullmq@5.34.8)':
'@nestjs/bullmq@10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))(bullmq@5.34.8)':
dependencies:
'@nestjs/bull-shared': 10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)
'@nestjs/bull-shared': 10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))
'@nestjs/common': 10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1)
'@nestjs/core': 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1)
bullmq: 5.34.8
@ -8743,7 +8751,7 @@ snapshots:
- supports-color
- utf-8-validate
'@nestjs/schedule@4.1.2(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)':
'@nestjs/schedule@4.1.2(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))':
dependencies:
'@nestjs/common': 10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1)
'@nestjs/core': 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1)
@ -8761,7 +8769,7 @@ snapshots:
transitivePeerDependencies:
- chokidar
'@nestjs/testing@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)(@nestjs/platform-express@10.4.15)':
'@nestjs/testing@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15))':
dependencies:
'@nestjs/common': 10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1)
'@nestjs/core': 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1)