1
1
<script lang="ts" setup>
2
2
import { open } from ' @tauri-apps/api/shell'
3
- import { computed , onScopeDispose , ref } from ' vue'
3
+ import { computed , onScopeDispose , ref , watch } from ' vue'
4
4
import { type ReferenceElement } from ' wowerlay'
5
+ import { whenever } from ' @vueuse/core'
5
6
import { useStore } from ' ../stores/store'
6
7
import NotificationItem from ' ../components/NotificationItem.vue'
7
- import { type MinimalRepository , type Thread , markNotificationAsRead } from ' ../api/notifications'
8
+ import { type MinimalRepository , type Thread , markNotificationAsRead , unsubscribeNotification } from ' ../api/notifications'
8
9
import { toGithubWebURL } from ' ../utils/github'
9
10
import { AppStorage } from ' ../storage'
10
11
import NotificationSkeleton from ' ../components/NotificationSkeleton.vue'
@@ -110,33 +111,17 @@ const popoverTarget = ref<ReferenceElement | null>(null)
110
111
const popoverRef = ref <InstanceType <typeof Popover > | null >(null )
111
112
112
113
async function handleSelectMarkAsRead(triggeredByKeyboard = false ) {
113
- if (triggeredByKeyboard ) {
114
- if (store .checkedItems .length > 0 ) {
115
- store .markCheckedNotificationsAsRead (AppStorage .get (' accessToken' )! )
116
- store .checkedItems = []
117
- return
118
- }
119
-
120
- if (contextMenuThread .value ) {
121
- const thread = contextMenuThread .value
122
- markNotificationAsRead (contextMenuThread .value .id , AppStorage .get (' accessToken' )! )
123
- .then (() => {
124
- store .removeNotificationById (thread .id )
125
- })
126
-
127
- return
128
- }
129
- }
130
-
131
- if (! contextMenuThread .value )
132
- return
133
-
134
- if (isChecked (contextMenuThread .value )) {
114
+ if (
115
+ (triggeredByKeyboard && store .checkedItems .length > 0 )
116
+ || (contextMenuThread .value && isChecked (contextMenuThread .value ))) {
135
117
store .markCheckedNotificationsAsRead (AppStorage .get (' accessToken' )! )
136
118
store .checkedItems = []
137
119
return
138
120
}
139
121
122
+ if (! contextMenuThread .value )
123
+ return
124
+
140
125
const thread = contextMenuThread .value
141
126
markNotificationAsRead (thread .id , AppStorage .get (' accessToken' )! )
142
127
.then (() => {
@@ -179,27 +164,61 @@ useKey('o', () => {
179
164
popoverRef .value ?.hide ()
180
165
})
181
166
167
+ async function handleSelectUnsubscribe(triggeredByKeyboard = false ) {
168
+ if (
169
+ (triggeredByKeyboard && store .checkedItems .length > 0 )
170
+ || (contextMenuThread .value && isChecked (contextMenuThread .value ))) {
171
+ store .unsubscribeCheckedNotifications (AppStorage .get (' accessToken' )! )
172
+ store .checkedItems = []
173
+ return
174
+ }
175
+
176
+ if (! contextMenuThread .value )
177
+ return
178
+
179
+ const thread = contextMenuThread .value
180
+ unsubscribeNotification (thread .id , AppStorage .get (' accessToken' )! )
181
+ .then (() => {
182
+ store .removeNotificationById (thread .id )
183
+ })
184
+ }
185
+
186
+ useKey (' u' , () => {
187
+ handleSelectUnsubscribe (true )
188
+ popoverRef .value ?.hide ()
189
+ })
190
+
182
191
const contextMenuItems = computed (() => [
183
192
menuItem ({
184
193
key: ' read' ,
185
194
meta: { text: ' Mark as read' , icon: Icons .Check16 , key: ' M' },
186
- onSelect : () => handleSelectMarkAsRead (),
195
+ onSelect() {
196
+ handleSelectMarkAsRead ()
197
+ contextMenuThread .value = null
198
+ },
187
199
}),
188
200
menuItem ({
189
201
key: ' open' ,
190
202
meta: { text: ' Open' , icon: Icons .LinkExternal16 , key: ' O' },
191
- onSelect : () => handleSelectOpen (),
203
+ onSelect() {
204
+ handleSelectOpen ()
205
+ contextMenuThread .value = null
206
+ },
207
+ }),
208
+ menuItem ({
209
+ key: ' unsubscribe' ,
210
+ meta: { text: ' Unsubscribe' , icon: Icons .BellSlash16 , key: ' U' },
211
+ onSelect() {
212
+ handleSelectUnsubscribe ()
213
+ contextMenuThread .value = null
214
+ },
192
215
}),
193
- // menuItem({
194
- // key: 'unsubscribe',
195
- // meta: { text: 'Unsubscribe', icon: Icons.BellSlash16, key: 'U' },
196
- // disabled: true,
197
- // }),
198
216
isChecked (contextMenuThread .value ! ) && menuItem ({
199
217
key: ' clear' ,
200
218
meta: { text: ' Clear selections' , icon: Icons .Circle , key: ' ESC' },
201
219
onSelect : () => {
202
220
store .checkedItems = []
221
+ contextMenuThread .value = null
203
222
},
204
223
}),
205
224
])
@@ -223,6 +242,27 @@ function handleThreadContextmenu(thread: Thread, event: MouseEvent) {
223
242
}
224
243
popoverRef .value ?.show ()
225
244
}
245
+
246
+ // Edge-Case
247
+ // If notifications are reloaded and the context menu target thread is deleted, close the context menu
248
+ watch (() => store .notifications , (notifications ) => {
249
+ if (! contextMenuThread .value )
250
+ return
251
+
252
+ const exists = notifications .some (notification => notification .id === contextMenuThread .value ! .id )
253
+ if (! exists ) {
254
+ contextMenuThread .value = null
255
+ popoverRef .value ?.hide ()
256
+ }
257
+ })
258
+
259
+ // Edge-Case
260
+ // If user reloaded in the middle of selecting notifications, clear the selection
261
+ whenever (() => store .skeletonVisible , () => {
262
+ popoverRef .value ?.hide ()
263
+ store .checkedItems = []
264
+ popoverTarget .value = null
265
+ })
226
266
</script >
227
267
228
268
<template >
0 commit comments