diff --git a/prisma/migrations/20250511141123_announcement/migration.sql b/prisma/migrations/20250511141123_announcement/migration.sql new file mode 100644 index 0000000..85d5632 --- /dev/null +++ b/prisma/migrations/20250511141123_announcement/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "profile" ADD COLUMN "announcement" TEXT NOT NULL DEFAULT ''; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml index fbffa92..648c57f 100644 --- a/prisma/migrations/migration_lock.toml +++ b/prisma/migrations/migration_lock.toml @@ -1,3 +1,3 @@ # Please do not edit this file manually -# It should be added in your version-control system (i.e. Git) +# It should be added in your version-control system (e.g., Git) provider = "postgresql" \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index c5a4203..0e18a28 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -59,6 +59,7 @@ model profile { defaultPostVisibility PostVisibility @default(public) defaultHideFromTimeline Boolean @default(false) wordMuteList String[] @default([]) + announcement String @default("") } model answer { diff --git a/src/app/_dto/fetch-profile/Profile.dto.ts b/src/app/_dto/fetch-profile/Profile.dto.ts index dcc0552..dd02da8 100644 --- a/src/app/_dto/fetch-profile/Profile.dto.ts +++ b/src/app/_dto/fetch-profile/Profile.dto.ts @@ -9,6 +9,7 @@ export interface userProfileDto { questionBoxName: string; hostname: string; instanceType: $Enums.InstanceType; + announcement: string; } export interface userProfileMeDto extends userProfileDto { diff --git a/src/app/_dto/settings/settings.dto.ts b/src/app/_dto/settings/settings.dto.ts index bc8b193..e5a0743 100644 --- a/src/app/_dto/settings/settings.dto.ts +++ b/src/app/_dto/settings/settings.dto.ts @@ -34,4 +34,9 @@ export class UserSettingsUpdateDto { @IsArray() @IsString({ each: true }) wordMuteList: profile['wordMuteList']; + + @IsString() + @IsOptional() + @MaxLength(80) + announcement: profile['announcement']; } diff --git a/src/app/api/_service/following/following-service.ts b/src/app/api/_service/following/following-service.ts index adcbe2b..324df62 100644 --- a/src/app/api/_service/following/following-service.ts +++ b/src/app/api/_service/following/following-service.ts @@ -27,9 +27,7 @@ export class FollowingService { const user = await prisma.user.findUniqueOrThrow({ where: { handle: tokenBody!.handle } }); const fn = async () => { - const follows = await prisma.following.findMany({ - where: { followerHandle: user.handle }, - }); + const follows = await prisma.following.findMany({ where: { followerHandle: user.handle } }); const filteredList = []; for (const f of follows) { @@ -70,6 +68,7 @@ export class FollowingService { questionBoxName: exist.questionBoxName, hostname: exist.user.hostName, instanceType: exist.user.server.instanceType, + announcement: exist.announcement, }, }); }); diff --git a/src/app/api/_service/profile/profile-service.ts b/src/app/api/_service/profile/profile-service.ts index a517cbe..c0ecce3 100644 --- a/src/app/api/_service/profile/profile-service.ts +++ b/src/app/api/_service/profile/profile-service.ts @@ -37,16 +37,10 @@ export class ProfileService { try { const userProfile = await prisma.profile.findUnique({ include: { - user: { - include: { server: { select: { instanceType: true } } }, - }, - _count: { - select: { answer: true, questions: true }, - }, - }, - where: { - handle: handle, + user: { include: { server: { select: { instanceType: true } } } }, + _count: { select: { answer: true, questions: true } }, }, + where: { handle: handle }, }); if (!userProfile) { return NextResponse.json({ message: `User not found` }, { status: 404 }); @@ -65,21 +59,14 @@ export class ProfileService { stopNotiNewQuestion: userProfile.stopNotiNewQuestion, hostname: userProfile.user.hostName, instanceType: instanceType, + announcement: userProfile.announcement, }; const resMe: userProfileMeDto = { - handle: userProfile.handle, - name: userProfile.name, - stopNewQuestion: userProfile.stopNewQuestion, - stopAnonQuestion: userProfile.stopAnonQuestion, - avatarUrl: userProfile.avatarUrl, - questionBoxName: userProfile.questionBoxName, - stopNotiNewQuestion: userProfile.stopNotiNewQuestion, - stopPostAnswer: userProfile.stopPostAnswer, + ...resNotMe, questions: questionCount, - instanceType: instanceType, + stopPostAnswer: userProfile.stopPostAnswer, defaultPostVisibility: userProfile.defaultPostVisibility, defaultHideFromTimeline: userProfile.defaultHideFromTimeline, - hostname: userProfile.user.hostName, wordMuteList: userProfile.wordMuteList, }; if (isMe) { diff --git a/src/app/api/_utils/profileToDto.ts b/src/app/api/_utils/profileToDto.ts index 495f787..921885e 100644 --- a/src/app/api/_utils/profileToDto.ts +++ b/src/app/api/_utils/profileToDto.ts @@ -12,6 +12,7 @@ export function profileToDto(profile: profile, hostName: string, instanceType: $ questionBoxName: profile.questionBoxName, hostname: hostName, instanceType: instanceType, + announcement: profile.announcement, }; return data; } diff --git a/src/app/main/settings/page.tsx b/src/app/main/settings/page.tsx index 3569e8e..9cb65fa 100644 --- a/src/app/main/settings/page.tsx +++ b/src/app/main/settings/page.tsx @@ -11,7 +11,7 @@ import CollapseMenu from '@/app/_components/collapseMenu'; import DialogModalTwoButton from '@/app/_components/modalTwoButton'; import { AccountCleanReqDto } from '@/app/_dto/account-clean/account-clean.dto'; import { FaLock, FaUserLargeSlash } from 'react-icons/fa6'; -import { MdDeleteForever, MdDeleteSweep, MdInfoOutline, MdOutlineCleaningServices } from 'react-icons/md'; +import { MdDeleteForever, MdDeleteSweep, MdOutlineCleaningServices } from 'react-icons/md'; import { MyProfileContext } from '@/app/main/layout'; import { MyProfileEv } from '@/app/main/_events'; import { getProxyUrl } from '@/utils/getProxyUrl/getProxyUrl'; @@ -27,6 +27,7 @@ export type FormValue = { questionBoxName: string; visibility: $Enums.PostVisibility; wordMuteList: string; + announcement: string; }; async function updateUserSettings(value: FormValue) { const body: UserSettingsUpdateDto = { @@ -42,6 +43,7 @@ async function updateUserSettings(value: FormValue) { .map((v) => v.trim()) .filter((v) => v.length > 0) .map((word) => word.replace(/^\/|\/[igmsuy]{0,6}$/g, '')), + announcement: value.announcement, }; try { const res = await fetch('/api/user/settings', { @@ -93,6 +95,7 @@ export default function Settings() { questionBoxName: userInfo.questionBoxName, visibility: userInfo.defaultPostVisibility, wordMuteList: userInfo.wordMuteList.join('\n'), + announcement: userInfo.announcement, }; setDefaultFormValue(value); } @@ -289,14 +292,23 @@ export default function Settings() { {...register('questionBoxName', { maxLength: 10 })} type="text" placeholder="질문함" - className={`input input-bordered input-sm w-48 ${ - errors.questionBoxName?.type === 'maxLength' && 'input-error' - }`} + className={`input input-bordered input-sm w-48 ${errors.questionBoxName?.type === 'maxLength' && 'input-error' + }`} /> 질문함 이름 (10글자 이내) +
+

질문함 공지

+ 질문함에 올릴 공지를 입력합니다. +