Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions prisma/migrations/20250511141123_announcement/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "profile" ADD COLUMN "announcement" TEXT NOT NULL DEFAULT '';
2 changes: 1 addition & 1 deletion prisma/migrations/migration_lock.toml
Original file line number Diff line number Diff line change
@@ -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"
1 change: 1 addition & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ model profile {
defaultPostVisibility PostVisibility @default(public)
defaultHideFromTimeline Boolean @default(false)
wordMuteList String[] @default([])
announcement String @default("")
}

model answer {
Expand Down
1 change: 1 addition & 0 deletions src/app/_dto/fetch-profile/Profile.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface userProfileDto {
questionBoxName: string;
hostname: string;
instanceType: $Enums.InstanceType;
announcement: string;
}

export interface userProfileMeDto extends userProfileDto {
Expand Down
5 changes: 5 additions & 0 deletions src/app/_dto/settings/settings.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,9 @@ export class UserSettingsUpdateDto {
@IsArray()
@IsString({ each: true })
wordMuteList: profile['wordMuteList'];

@IsString()
@IsOptional()
@MaxLength(80)
announcement: profile['announcement'];
}
5 changes: 2 additions & 3 deletions src/app/api/_service/following/following-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -70,6 +68,7 @@ export class FollowingService {
questionBoxName: exist.questionBoxName,
hostname: exist.user.hostName,
instanceType: exist.user.server.instanceType,
announcement: exist.announcement,
},
});
});
Expand Down
25 changes: 6 additions & 19 deletions src/app/api/_service/profile/profile-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
Expand All @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions src/app/api/_utils/profileToDto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export function profileToDto(profile: profile, hostName: string, instanceType: $
questionBoxName: profile.questionBoxName,
hostname: hostName,
instanceType: instanceType,
announcement: profile.announcement,
};
return data;
}
20 changes: 16 additions & 4 deletions src/app/main/settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -27,6 +27,7 @@ export type FormValue = {
questionBoxName: string;
visibility: $Enums.PostVisibility;
wordMuteList: string;
announcement: string;
};
async function updateUserSettings(value: FormValue) {
const body: UserSettingsUpdateDto = {
Expand All @@ -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', {
Expand Down Expand Up @@ -93,6 +95,7 @@ export default function Settings() {
questionBoxName: userInfo.questionBoxName,
visibility: userInfo.defaultPostVisibility,
wordMuteList: userInfo.wordMuteList.join('\n'),
announcement: userInfo.announcement,
};
setDefaultFormValue(value);
}
Expand Down Expand Up @@ -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'
}`}
/>
<span className="font-thin">질문함 이름 (10글자 이내)</span>
</div>
</div>
<Divider />
<div className='flex flex-col desktop:w-[24rem] gap-2 items-center p-2'>
<h3 className='text-lg'>질문함 공지</h3>
<span className='font-thin'>질문함에 올릴 공지를 입력합니다.</span>
<textarea
{...register("announcement")}
className='textarea textarea-bordered w-full min-h-[15vh] text-base'
placeholder='최대 80자'
maxLength={80}
/>
</div>
<div className="flex flex-col desktop:w-[24rem] gap-2 items-center p-2">
<div className="text-lg"> 질문 단어 뮤트 </div>
<div className="font-thin">
Expand Down
12 changes: 9 additions & 3 deletions src/app/main/user/[handle]/_profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { SubmitHandler, useForm } from 'react-hook-form';
import { FaEllipsisVertical } from 'react-icons/fa6';
import { getProxyUrl } from '@/utils/getProxyUrl/getProxyUrl';
import { onApiError } from '@/utils/api-error/onApiError';
import { FaInfoCircle } from 'react-icons/fa';

type FormValue = {
question: string;
Expand Down Expand Up @@ -305,16 +306,21 @@ export default function Profile() {
)}
</div>
</div>
{userProfile?.announcement && (
<div className="mb-2 p-2 w-[90%] flex items-center gap-2 border border-indigo-400 dark:border-indigo-200 rounded-lg">
<FaInfoCircle className='flex-shrink-0' />
<span className='grow break-all'>{userProfile.announcement}</span>
</div>
)}
<form className="w-full flex flex-col items-center" onSubmit={handleSubmit(onSubmit)}>
<textarea
{...register('question', {
required: 'required',
maxLength: 1000,
})}
placeholder="질문 내용을 입력해 주세요"
className={`w-[90%] mb-2 font-thin leading-loose textarea ${
errors.question ? 'textarea-error' : 'textarea-bordered'
}`}
className={`w-[90%] mb-2 font-thin leading-loose textarea ${errors.question ? 'textarea-error' : 'textarea-bordered'
}`}
onKeyDown={onCtrlEnter}
disabled={userProfile?.stopNewQuestion === true ? true : false}
style={{ resize: 'none' }}
Expand Down