1
1
<template >
2
2
<main ref =" mainEl" class =" admin-mod-queue" >
3
- <div class =" mod-queue-category-item q-floating" :disabled =" query.loading.value" @click =" refetch" >
3
+ <div class =" mod-queue-category-item q-floating" :disabled =" query.loading.value" @click =" query. refetch() " >
4
4
{{ query.loading.value ? "Loading..." : "Refetch" }}
5
5
</div >
6
6
<div class =" mod-queue-stats" >
19
19
:error =" country !== '' && !CISO2.has(country.toUpperCase())"
20
20
/>
21
21
</div >
22
-
23
- <div >
24
- <label for =" limit" >Limit: {{ _limit }}</label >
25
- <RangeInput v-model.number =" _limit" :min =" 1" :max =" 250" width =" 8em" />
26
- </div >
27
- </section >
28
- <section class =" mod-queue-categories" >
29
- <div class =" mod-queue-category-item" :disabled =" query.loading.value" @click =" refetch" >
30
- {{ query.loading.value ? "Loading..." : "Refetch " }}
31
- </div >
32
22
<div ref =" groupDropdown" class =" mod-queue-dropdown mod-queue-category-item" >
33
23
<div class =" mod-queue-category-item" :active =" dropdownOpen" @click =" dropdownOpen = !dropdownOpen" >
34
24
<Icon icon =" language" />
53
43
</div >
54
44
</div >
55
45
</div >
46
+ <div >
47
+ <label for =" limit" >Limit: {{ _limit }}</label >
48
+ <RangeInput v-model.number =" _limit" :min =" 1" :max =" 250" width =" 8em" />
49
+ </div >
50
+ </section >
51
+ <section class =" mod-queue-categories" >
52
+ <div class =" mod-queue-category-item" :disabled =" query.loading.value" @click =" query.refetch()" >
53
+ {{ query.loading.value ? "Loading..." : "Refetch " }}
54
+ </div >
55
+ <div class =" mod-queue-category-item" :active =" autoSync" @click =" autoSync = !autoSync" >AutoSync</div >
56
56
</section >
57
57
<section class =" mod-queue-categories" >
58
58
<div class =" mod-queue-category-item" :active =" bigMode" @click =" bigMode = !bigMode" >Zoomed</div >
111
111
<script setup lang="ts">
112
112
import { computed , nextTick , ref , toRaw , watch } from " vue" ;
113
113
import { useQuery } from " @vue/apollo-composable" ;
114
- import { debouncedRef , onClickOutside , useElementVisibility } from " @vueuse/core" ;
114
+ import { debouncedRef , onClickOutside , useElementVisibility , useIntervalFn } from " @vueuse/core" ;
115
115
import { useActor } from " @/store/actor" ;
116
116
import { useDataLoaders } from " @/store/dataloader" ;
117
117
import { useModal } from " @/store/modal" ;
118
118
import { useMutationStore } from " @/store/mutation" ;
119
119
import { GetModRequests } from " @/apollo/query/mod-queue.query" ;
120
+ import { NetworkStatus } from " @apollo/client/core/networkStatus" ;
120
121
import { ObjectKind } from " @/structures/Common" ;
121
122
import { Emote } from " @/structures/Emote" ;
122
123
import { Message } from " @/structures/Message" ;
@@ -152,22 +153,20 @@ const filter = computed(
152
153
);
153
154
const filterType = ref (false );
154
155
155
- const query = useQuery <GetModRequests .Result , GetModRequests .Variables >(
156
- GetModRequests ,
157
- () => ({
158
- page: 0 ,
159
- limit: limit .value ,
160
- wish: activeTab .value ,
161
- country: CISO2 .has (country .value .toUpperCase ())
162
- ? country .value
163
- : filter .value .size && ! filterType .value
164
- ? [... filter .value ]
165
- : undefined ,
166
- }),
167
- {
168
- fetchPolicy: " cache-and-network" ,
169
- },
170
- );
156
+ const vars = computed (() => ({
157
+ page: 0 ,
158
+ limit: limit .value ,
159
+ wish: activeTab .value ,
160
+ country: CISO2 .has (country .value .toUpperCase ())
161
+ ? country .value
162
+ : filter .value .size && ! filterType .value
163
+ ? [... filter .value ]
164
+ : undefined ,
165
+ }));
166
+
167
+ const query = useQuery <GetModRequests .Result , GetModRequests .Variables >(GetModRequests , vars , {
168
+ fetchPolicy: " cache-and-network" ,
169
+ });
171
170
172
171
const visible = ref (24 );
173
172
const end = ref <HTMLElement | null >(null );
@@ -180,7 +179,15 @@ watch(isAtEnd, (v) => {
180
179
}
181
180
});
182
181
183
- const refetch = () => nextTick (query .refetch );
182
+ const autoSync = ref (false );
183
+ const isSyncing = ref (false );
184
+ const autoSyncer = useIntervalFn (async () => {
185
+ isSyncing .value = true ;
186
+ await query .refetch ();
187
+ isSyncing .value = false ;
188
+ }, 10000 );
189
+ autoSyncer .pause ();
190
+ watch (autoSync , (v ) => (v ? autoSyncer .resume () : autoSyncer .pause (), { immediate: true }));
184
191
185
192
const addMore = async () => {
186
193
if (! canViewMore .value ) return ;
@@ -223,11 +230,13 @@ const searchQuery = ref("");
223
230
224
231
const debouncedSearch = debouncedRef (searchQuery , 500 );
225
232
226
- const targetMap = new Map <string , Emote >();
233
+ const targetMap = new Map <string , Emote | undefined >();
227
234
228
235
const loadEmotes = async (ids : string []) => {
229
236
const toLoad = ids .filter ((id ) => ! targetMap .has (id ));
230
237
if (! toLoad .length ) return ;
238
+ toLoad .forEach ((id ) => targetMap .set (id , undefined ));
239
+
231
240
const emotes = await dataloaders .loadEmotes (toLoad );
232
241
233
242
emotes .forEach ((emote ) => {
@@ -241,8 +250,16 @@ const loadEmotes = async (ids: string[]) => {
241
250
});
242
251
};
243
252
244
- query .onResult (({ data }) => {
253
+ const readCards = ref (new Set <string >());
254
+ let block = false ;
255
+ query .onResult (({ data , networkStatus }) => {
256
+ if (networkStatus !== NetworkStatus .ready ) return ;
245
257
if (! data ) return ;
258
+ if (block ) return ;
259
+
260
+ block = true ;
261
+ nextTick (() => (block = false ));
262
+
246
263
const d = structuredClone (toRaw (data )) as typeof data ;
247
264
if (! d ?.modRequests ?.messages ) return ;
248
265
@@ -259,11 +276,21 @@ query.onResult(({ data }) => {
259
276
r .target = emote ;
260
277
r .author = emote .owner ! ;
261
278
});
262
- visible .value = BASE_VISIBLE ;
263
- canViewMore .value = rs .length > BASE_VISIBLE ;
264
- requests .value = rs ;
265
279
266
- loadEmotes (rs .slice (0 , BASE_VISIBLE ).map ((r ) => r .target_id ));
280
+ if (isSyncing .value ) {
281
+ const new_ids = new Set (rs .map ((r ) => r .id ));
282
+
283
+ requests .value .forEach ((r ) => {
284
+ if (! new_ids .has (r .id )) {
285
+ readCards .value .add (r .id );
286
+ }
287
+ });
288
+ } else {
289
+ requests .value = rs ;
290
+ visible .value = BASE_VISIBLE ;
291
+ canViewMore .value = rs .length > BASE_VISIBLE ;
292
+ loadEmotes (rs .slice (0 , BASE_VISIBLE ).map ((r ) => r .target_id ));
293
+ }
267
294
});
268
295
269
296
watch (debouncedSearch , (s ) => {
@@ -292,8 +319,6 @@ const modal = useModal();
292
319
293
320
const m = useMutationStore ();
294
321
295
- const readCards = ref (new Set <string >());
296
-
297
322
// Called when a decision on a request is made
298
323
const onDecision = async (req : Message .ModRequest , t : string , isUndo ? : boolean ) => {
299
324
readCards .value .delete (req .id );
@@ -338,6 +363,7 @@ const onDecision = async (req: Message.ModRequest, t: string, isUndo?: boolean)
338
363
component: EmoteDeleteModal ,
339
364
props: {
340
365
emote: req .target as Emote ,
366
+ id: req .target_id ,
341
367
},
342
368
events: {
343
369
delete : (reason : string ) => resolve ([true , reason ]),
0 commit comments