Skip to content

Commit d08509d

Browse files
committed
Merge remote-tracking branch 'origin/feature/add-noticed' into hotfix/go-back
2 parents 5979cf5 + 523c67c commit d08509d

File tree

19 files changed

+246
-112
lines changed

19 files changed

+246
-112
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- AlterTable
2+
ALTER TABLE "notifications" ADD COLUMN "is_noticed" BOOLEAN NOT NULL DEFAULT false;

packages/velog-prisma/prisma/schema.prisma

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -628,8 +628,9 @@ model Notification {
628628
action Json @default("{}")
629629
action_id String? @db.Uuid
630630
is_read Boolean @default(false)
631-
is_deleted Boolean @default(false)
632631
read_at DateTime?
632+
is_deleted Boolean @default(false)
633+
is_noticed Boolean @default(false)
633634
created_at DateTime @default(now()) @db.Timestamptz(6)
634635
user User @relation(fields: [fk_user_id], references: [id], onDelete: Cascade, onUpdate: Restrict)
635636

packages/velog-server/src/graphql/Notification.gql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@ enum NotificationType {
1919

2020
type Query {
2121
notifications(input: NotificationsInput!): [Notification!]!
22-
notificationCount: Int!
22+
notNoticeNotificationCount: Int!
2323
}
2424

2525
type Mutation {
2626
createNotification(input: CreateNotificationInput!): Notification!
27+
updateNotNoticeNotification: Void
2728
readNotification(input: ReadNotificationInput!): Void
2829
readAllNotifications: Void
2930
removeAllNotifications: Void

packages/velog-server/src/graphql/helpers/generated.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ export type Mutation = {
205205
unregister?: Maybe<Scalars['Void']['output']>
206206
updateAbout?: Maybe<UserProfile>
207207
updateEmailRules?: Maybe<UserMeta>
208+
updateNotNoticeNotification?: Maybe<Scalars['Void']['output']>
208209
updateProfile?: Maybe<UserProfile>
209210
updateSocialInfo?: Maybe<UserProfile>
210211
updateThumbnail?: Maybe<UserProfile>
@@ -359,7 +360,7 @@ export type Query = {
359360
followers: Array<FollowResult>
360361
followings: Array<FollowResult>
361362
isLogged?: Maybe<Scalars['Boolean']['output']>
362-
notificationCount: Scalars['Int']['output']
363+
notNoticeNotificationCount: Scalars['Int']['output']
363364
notifications: Array<Notification>
364365
post?: Maybe<Post>
365366
posts: Array<Post>
@@ -1055,6 +1056,7 @@ export type MutationResolvers<
10551056
ContextType,
10561057
RequireFields<MutationUpdateEmailRulesArgs, 'input'>
10571058
>
1059+
updateNotNoticeNotification?: Resolver<Maybe<ResolversTypes['Void']>, ParentType, ContextType>
10581060
updateProfile?: Resolver<
10591061
Maybe<ResolversTypes['UserProfile']>,
10601062
ParentType,
@@ -1190,7 +1192,7 @@ export type QueryResolvers<
11901192
RequireFields<QueryFollowingsArgs, 'input'>
11911193
>
11921194
isLogged?: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>
1193-
notificationCount?: Resolver<ResolversTypes['Int'], ParentType, ContextType>
1195+
notNoticeNotificationCount?: Resolver<ResolversTypes['Int'], ParentType, ContextType>
11941196
notifications?: Resolver<
11951197
Array<ResolversTypes['Notification']>,
11961198
ParentType,

packages/velog-server/src/graphql/resolvers/notificationResolvers.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ const notificationResolvers: Resolvers = {
99
const notificationService = container.resolve(NotificationService)
1010
return await notificationService.list(input, ctx.user?.id)
1111
},
12-
notificationCount: async (_, __, ctx) => {
12+
notNoticeNotificationCount: async (_, __, ctx) => {
1313
const notificationService = container.resolve(NotificationService)
14-
return await notificationService.getCount(ctx.user?.id)
14+
return await notificationService.getNotNoticeCount(ctx.user?.id)
1515
},
1616
},
1717
Mutation: {
@@ -28,6 +28,10 @@ const notificationResolvers: Resolvers = {
2828
signedUserId: ctx.user?.id,
2929
})
3030
},
31+
updateNotNoticeNotification: async (_, __, ctx) => {
32+
const notificationService = container.resolve(NotificationService)
33+
return await notificationService.updateNotNotice(ctx.user?.id)
34+
},
3135
readNotification: async (_, { input }, ctx) => {
3236
const notificationService = container.resolve(NotificationService)
3337
return await notificationService.read(input.notification_ids, ctx.user?.id)

packages/velog-server/src/services/NotificationService/index.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ import { z } from 'zod'
1717

1818
interface Service {
1919
list(query?: NotificationsInput, signedUserId?: string): Promise<Notification[]>
20-
getCount(signedUserId?: string): Promise<number>
20+
getNotNoticeCount(signedUserId?: string): Promise<number>
21+
updateNotNotice(signedUserId?: string): Promise<void>
2122
create(args: CreateArgs): Promise<Notification>
2223
read(notificationIds: string[], signedUserId?: string): Promise<void>
2324
readAll(signedUserId?: string): Promise<void>
@@ -65,7 +66,7 @@ export class NotificationService implements Service {
6566
})
6667
return notifications as unknown as Notification[]
6768
}
68-
public async getCount(signedUserId?: string): Promise<number> {
69+
public async getNotNoticeCount(signedUserId?: string): Promise<number> {
6970
if (!signedUserId) {
7071
throw new UnauthorizedError('Not logged in')
7172
}
@@ -80,7 +81,28 @@ export class NotificationService implements Service {
8081
where: {
8182
fk_user_id: signedUserId,
8283
is_deleted: false,
83-
is_read: false,
84+
is_noticed: false,
85+
},
86+
})
87+
}
88+
public async updateNotNotice(signedUserId?: string): Promise<void> {
89+
if (!signedUserId) {
90+
throw new UnauthorizedError('Not logged in')
91+
}
92+
93+
const user = await this.userService.findById(signedUserId)
94+
95+
if (!user) {
96+
throw new NotFoundError('Not found user')
97+
}
98+
99+
await this.db.notification.updateMany({
100+
where: {
101+
fk_user_id: signedUserId,
102+
is_noticed: false,
103+
},
104+
data: {
105+
is_noticed: true,
84106
},
85107
})
86108
}

packages/velog-web/src/app/(list)/recent/page.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ export const metadata: Metadata = {
1111
export default async function RecentHome() {
1212
const data = await getRecentPosts({ limit: 50 })
1313

14-
// if (!data) {
15-
// notFound()
16-
// }
14+
if (!data) {
15+
notFound()
16+
}
1717

18-
return <RecentPosts data={[]} />
18+
return <RecentPosts data={data} />
1919
}

packages/velog-web/src/components/Header/Header.module.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
margin-right: 4px;
1616
}
1717

18-
.isNotificationPage {
18+
.active {
1919
background-color: rgba(0, 0, 0, 0.1);
2020
}
2121

packages/velog-web/src/components/Header/Header.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ import useToggle from '@/hooks/useToggle'
1010
import HeaderUserIcon from '@/components/Header/HeaderUserIcon'
1111
import HeaderUserMenu from '@/components/Header/HeaderUserMenu'
1212
import HeaderSkeleton from '@/components/Header/HeaderSkeleton'
13-
import { useCurrentUserQuery, useNotificationCountQuery } from '@/graphql/helpers/generated'
13+
import {
14+
useCurrentUserQuery,
15+
useNotNoticeNotificationCountQuery,
16+
} from '@/graphql/helpers/generated'
1417
import HeaderLogo from './HeaderLogo'
1518
import { useParams, usePathname, useRouter } from 'next/navigation'
1619
import { getUsernameFromParams } from '@/lib/utils'
@@ -38,7 +41,10 @@ function Header({ logo }: Props) {
3841
const { data, isLoading } = useCurrentUserQuery()
3942

4043
const user = data?.currentUser ?? null
41-
const { data: notificationCountData } = useNotificationCountQuery({}, { enabled: !!user })
44+
const { data: notificationCountData } = useNotNoticeNotificationCountQuery(
45+
{},
46+
{ enabled: !!user },
47+
)
4248

4349
useEffect(() => {
4450
update(user)
@@ -47,7 +53,7 @@ function Header({ logo }: Props) {
4753
const username = getUsernameFromParams(params)
4854
const urlForSearch = username ? `/search?username=${username}` : '/search'
4955
const isNotificationPage = pathname.includes('/notification')
50-
const notificationCount = notificationCountData?.notificationCount ?? 0
56+
const notificationCount = notificationCountData?.notNoticeNotificationCount ?? 0
5157

5258
const onClickNotification = (e: MouseEvent<HTMLAnchorElement>) => {
5359
e.preventDefault()
@@ -65,11 +71,12 @@ function Header({ logo }: Props) {
6571
{logo || <HeaderLogo />}
6672
<div className={cx('right')}>
6773
<Link className={cx('notification')} href="/notifications" onClick={onClickNotification}>
68-
<HeaderIcon className={cx({ isNotificationPage })}>
74+
<HeaderIcon className={cx({ active: isNotificationPage })}>
6975
{user && notificationCount !== 0 && (
7076
<div
7177
className={cx('notificationCounter', {
7278
isSingle: Math.floor(notificationCount / 10) === 0,
79+
hide: isNotificationPage,
7380
})}
7481
>
7582
{Math.min(99, notificationCount)}

packages/velog-web/src/features/home/components/RecentPosts/RecentPosts.tsx

Lines changed: 43 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -13,57 +13,56 @@ function RecentPosts({ data }: Props) {
1313
const hasEffectRun = useRef<boolean>(false)
1414

1515
const [initialData, setInitialData] = useState<Post[]>([])
16-
// const { posts, isFetching, fetchMore, isLoading } = useRecentPosts(initialData)
16+
const { posts, isFetching, fetchMore, isLoading } = useRecentPosts(initialData)
1717

18-
// useEffect(() => {
19-
// if (hasEffectRun.current) return
20-
// hasEffectRun.current = true
18+
useEffect(() => {
19+
if (hasEffectRun.current) return
20+
hasEffectRun.current = true
2121

22-
// const storageKey = 'recentPosts'
23-
// let timeout: NodeJS.Timeout
24-
// try {
25-
// const infiniteData = localStorage.getItem(storageKey)
22+
const storageKey = 'recentPosts'
23+
let timeout: NodeJS.Timeout
24+
try {
25+
const infiniteData = localStorage.getItem(storageKey)
2626

27-
// if (!infiniteData) {
28-
// setInitialData(data)
29-
// return
30-
// }
27+
if (!infiniteData) {
28+
setInitialData(data)
29+
return
30+
}
3131

32-
// const parsed: Post[] = JSON.parse(infiniteData) || []
33-
// const savedPosts = parsed?.slice(data.length) || []
34-
// setInitialData([...data, ...savedPosts])
32+
const parsed: Post[] = JSON.parse(infiniteData) || []
33+
const savedPosts = parsed?.slice(data.length) || []
34+
setInitialData([...data, ...savedPosts])
3535

36-
// const position = Number(localStorage.getItem(`${storageKey}/scrollPosition`))
37-
// if (!position) return
38-
// timeout = setTimeout(() => {
39-
// window.scrollTo({
40-
// top: position,
41-
// behavior: 'instant',
42-
// })
43-
// }, 1000)
44-
// } catch (error) {
45-
// console.log('getRecentPosts from storage error', error)
46-
// } finally {
47-
// localStorage.removeItem(storageKey)
48-
// localStorage.removeItem(`${storageKey}/scrollPosition`)
49-
// }
36+
const position = Number(localStorage.getItem(`${storageKey}/scrollPosition`))
37+
if (!position) return
38+
timeout = setTimeout(() => {
39+
window.scrollTo({
40+
top: position,
41+
behavior: 'instant',
42+
})
43+
}, 1000)
44+
} catch (error) {
45+
console.log('getRecentPosts from storage error', error)
46+
} finally {
47+
localStorage.removeItem(storageKey)
48+
localStorage.removeItem(`${storageKey}/scrollPosition`)
49+
}
5050

51-
// return () => {
52-
// clearTimeout(timeout)
53-
// }
54-
// }, [data])
51+
return () => {
52+
clearTimeout(timeout)
53+
}
54+
}, [data])
5555

56-
return <div>hello3</div>
57-
// return (
58-
// <PostCardGrid
59-
// posts={posts}
60-
// forHome={true}
61-
// forPost={false}
62-
// isFetching={isFetching}
63-
// isLoading={isLoading}
64-
// fetchMore={fetchMore}
65-
// />
66-
// )
56+
return (
57+
<PostCardGrid
58+
posts={posts}
59+
forHome={true}
60+
forPost={false}
61+
isFetching={isFetching}
62+
isLoading={isLoading}
63+
fetchMore={fetchMore}
64+
/>
65+
)
6766
}
6867

6968
export default RecentPosts

packages/velog-web/src/features/notification/components/NotificationList/NotificationList.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import useNotificationReorder from '../../hooks/useNotificationReorder'
1010
import { useEffect, useRef, useState } from 'react'
1111
import { useInfiniteScroll } from '@/hooks/useInfiniteScroll'
1212
import NotificationSkeletonList from './NotificationSkeletonList'
13+
import { CurrentUser } from '@/types/user'
1314

1415
const cx = bindClassNames(styles)
1516

@@ -20,8 +21,13 @@ function NotificationList() {
2021
Object.assign(input, { is_read: false })
2122
}
2223

24+
const [user, setUser] = useState<CurrentUser>()
2325
const { data: currentUserData, isLoading: currentUserIsLoading } = useCurrentUserQuery()
24-
const user = currentUserData?.currentUser
26+
27+
useEffect(() => {
28+
if (!currentUserData?.currentUser) return
29+
setUser(currentUserData.currentUser)
30+
}, [currentUserData])
2531

2632
const { data: notificationData, isLoading: notificationIsLoading } = useNotificationQuery(
2733
{ input },

packages/velog-web/src/features/notification/components/NotificationSelector/NotificationSelector.tsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ import { bindClassNames } from '@/lib/styles/bindClassNames'
66
import { usePathname } from 'next/navigation'
77
import {
88
useCurrentUserQuery,
9-
useNotificationCountQuery,
9+
useNotNoticeNotificationCountQuery,
1010
useNotificationQuery,
1111
useReadAllNotificationsMutation,
1212
useRemoveAllNotificationsMutation,
1313
} from '@/graphql/helpers/generated'
1414
import PopupOKCancel from '@/components/PopupOKCancel'
15-
import { useState } from 'react'
15+
import { useEffect, useState } from 'react'
1616
import { useQueries } from '@tanstack/react-query'
17+
import { CurrentUser } from '@/types/user'
1718

1819
const cx = bindClassNames(styles)
1920

@@ -24,8 +25,13 @@ function NotificationSelector() {
2425
Object.assign(input, { is_read: false })
2526
}
2627

28+
const [user, setUser] = useState<CurrentUser>()
2729
const { data } = useCurrentUserQuery()
28-
const user = data?.currentUser
30+
31+
useEffect(() => {
32+
if (!data?.currentUser) return
33+
setUser(data.currentUser)
34+
}, [data])
2935

3036
const [{ data: notificationQueryData, refetch }, { data: notificationCountData }] = useQueries({
3137
queries: [
@@ -35,8 +41,8 @@ function NotificationSelector() {
3541
enabled: !user,
3642
},
3743
{
38-
queryKey: useNotificationCountQuery.getKey(),
39-
queryFn: useNotificationCountQuery.fetcher(),
44+
queryKey: useNotNoticeNotificationCountQuery.getKey(),
45+
queryFn: useNotNoticeNotificationCountQuery.fetcher(),
4046
enabled: !user,
4147
},
4248
],
@@ -52,7 +58,7 @@ function NotificationSelector() {
5258
if (notificationQueryData?.notifications.length === 0) return
5359

5460
if (type === 'read') {
55-
if (notificationCountData?.notificationCount === 0) return
61+
if (!notificationCountData || notificationCountData.notNoticeNotificationCount === 0) return
5662
setReadAsk(true)
5763
} else {
5864
setRemoveAsk(true)

0 commit comments

Comments
 (0)