Skip to content

Commit 16d10b0

Browse files
committed
Refactor notification list data
1 parent b5c25b1 commit 16d10b0

File tree

5 files changed

+69
-36
lines changed

5 files changed

+69
-36
lines changed

src/components/NotificationItem.vue renamed to src/components/NotificationList.vue

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script lang="ts" setup>
22
import type { Thread } from '../api/notifications'
3-
import type { NotificationList } from '../types'
3+
import type { NotificationListData } from '../types'
44
import { formatReason, notificationSubjectIcon } from '../utils/notification'
55
66
interface Emits {
@@ -9,7 +9,7 @@ interface Emits {
99
}
1010
1111
interface Props {
12-
data: NotificationList
12+
data: NotificationListData
1313
}
1414
1515
const props = defineProps<Props>()
@@ -43,20 +43,20 @@ function handleRepoClick() {
4343
</span>
4444
</button>
4545
<button
46-
v-for="item of props.data.threads"
46+
v-for="item of props.data.items"
4747
:key="item.id"
4848
class="notification-item"
4949
:class="{ 'notification-item-read': !item.unread }"
50-
@click="handleNotificationClick(item)"
50+
@click="handleNotificationClick(item.raw)"
5151
>
5252
<Component
53-
:is="notificationSubjectIcon(item.subject.type)"
53+
:is="notificationSubjectIcon(item.type)"
5454
class="notification-item-icon"
5555
/>
5656

5757
<div class="notification-item-content">
5858
<div class="notification-item-content-title">
59-
{{ item.subject.title }}
59+
{{ item.title }}
6060
</div>
6161

6262
<div class="notification-item-content-subtitle">

src/pages/HomePage.vue

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
<script lang="ts" setup>
22
import { open } from '@tauri-apps/api/shell'
33
import { useStore } from '../stores/store'
4-
import Separator from '../components/Separator.vue'
5-
import NotificationItem from '../components/NotificationItem.vue'
4+
import NotificationList from '../components/NotificationList.vue'
65
import { useInterval } from '../composables/useInterval'
76
import type { Thread } from '../api/notifications'
87
import { toGithubWebURL } from '../utils/github'
@@ -30,7 +29,7 @@ function handleRepoClick(repoFullName: string) {
3029
<NotificationSkeleton v-if="store.skeletonVisible" />
3130

3231
<template v-else>
33-
<NotificationItem
32+
<NotificationList
3433
v-for="notification of store.notifications"
3534
:key="notification.repoFullName"
3635
:data="notification"

src/stores/store.ts

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { defineStore } from 'pinia'
22
import { readonly, ref } from 'vue'
3-
import type { Thread } from '../api/notifications'
43
import { getNotifications } from '../api/notifications'
54
import { Page } from '../constants'
65
import { AppStorage } from '../storage'
7-
import type { NotificationList, Option } from '../types'
6+
import type { NotificationListData, Option } from '../types'
7+
import { notificationListFromThreads } from '../utils/notification'
88

99
export const useStore = defineStore('store', () => {
10-
const notifications = ref<NotificationList[]>([])
10+
const notifications = ref<NotificationListData[]>([])
1111
const loadingNotifications = ref(false)
1212
const skeletonVisible = ref(false)
1313

@@ -30,31 +30,12 @@ export const useStore = defineStore('store', () => {
3030
showReadNotifications: AppStorage.get('showReadNotifications'),
3131
})
3232

33-
notifications.value = []
34-
const notificationsByRepo = new Map<Thread['repository']['id'], Thread[]>()
35-
36-
data.forEach((notification) => {
37-
const { repository } = notification
38-
39-
if (!(notificationsByRepo.has(repository.id)))
40-
notificationsByRepo.set(repository.id, [])
41-
42-
const thread = notificationsByRepo.get(repository.id)!
43-
44-
thread.push(notification)
45-
})
46-
47-
for (const [, threads] of notificationsByRepo) {
48-
notifications.value.push({
49-
repoFullName: threads[0].repository.full_name,
50-
threads,
51-
})
52-
}
53-
33+
notifications.value = notificationListFromThreads(data)
5434
skeletonVisible.value = false
5535
}
5636
catch (error) {
5737
console.error('NotificationError: ', error)
38+
notifications.value = []
5839
}
5940
}
6041

src/types.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
1+
import type { Raw } from 'vue'
12
import type { Thread } from './api/notifications'
23
import type { User } from './api/user'
34

45
export type Option<T> = T | null
56

6-
export interface NotificationList {
7+
export interface NotificationListData {
78
repoFullName: string
8-
threads: Thread[]
9+
repoAvatarURL: string
10+
items: NotificationListDataItem[]
11+
}
12+
13+
export interface NotificationListDataItem {
14+
id: Thread['id']
15+
unread: Thread['unread']
16+
title: Thread['subject']['title']
17+
reason: Thread['reason']
18+
type: Thread['subject']['type']
19+
raw: Raw<Thread>
920
}
1021

1122
export interface AppStorageContext {
@@ -14,6 +25,5 @@ export interface AppStorageContext {
1425
showOnlyParticipating: boolean
1526
openAtStartup: boolean
1627
soundsEnabled: boolean
17-
markAsReadOnClick: boolean
1828
showReadNotifications: boolean
1929
}

src/utils/notification.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import { invoke } from '@tauri-apps/api/tauri'
2+
import { markRaw } from 'vue'
3+
import type { Thread } from '../api/notifications'
24
import { Icons } from '../components/Icons'
35
import type { NotificationReason, NotificationSubject } from '../constants'
46
import { reasonFormatMap, subjectIconMap } from '../constants'
7+
import type { NotificationListData, NotificationListDataItem } from '../types'
58

69
export function notificationSubjectIcon(subject: NotificationSubject) {
710
return subjectIconMap[subject] || Icons.Question
@@ -14,3 +17,43 @@ export function formatReason(reason: NotificationReason) {
1417
export function playNotificationSound() {
1518
invoke('play_notification_sound')
1619
}
20+
21+
function notificationListItemFromThread(thread: Thread): NotificationListDataItem {
22+
return {
23+
id: thread.id,
24+
reason: thread.reason,
25+
title: thread.subject.title,
26+
type: thread.subject.type,
27+
unread: thread.unread,
28+
raw: markRaw(thread),
29+
}
30+
}
31+
32+
export function notificationListFromThreads(threads: Thread[]) {
33+
const repoIdIndexMap: Record<Thread['repository']['id'], number> = Object.create(null)
34+
const notifications: NotificationListData[] = []
35+
36+
for (const thread of threads) {
37+
const { repository } = thread
38+
39+
if (repository.id in repoIdIndexMap) {
40+
const notification = notifications[repoIdIndexMap[repository.id]]
41+
notification.items.push(notificationListItemFromThread(thread))
42+
}
43+
else {
44+
const nextIndex = notifications.length
45+
const notification: NotificationListData = {
46+
repoAvatarURL: `${repository.owner.avatar_url}&s=40`,
47+
repoFullName: repository.full_name,
48+
items: [
49+
notificationListItemFromThread(thread),
50+
],
51+
}
52+
53+
repoIdIndexMap[repository.id] = nextIndex
54+
notifications.push(notification)
55+
}
56+
}
57+
58+
return notifications
59+
}

0 commit comments

Comments
 (0)