Skip to content

Commit e3e503b

Browse files
authored
Integrate Tanstack Query for handling errors and retries for the opinion/analysis tab (#366)
* Fix cluster size and prevent image drag * Add y offset for cluster label * Initial integration with TanStack for handling data errors * Fix bug where opinions not shown if watcher misses it * Update delete comment API to use the new query system * Add proper typing to all translation files * Move composable unrelated to the api * Add basic error UI for comments * Create AsyncDataHandler so we can reuse the loading state management for other parts of the code * Fixed the loading initialization bug * Move composable file to the utils folder * Delete the old composable api folder * Update to the new analysis API endpoint * Rename reusable component to ZKLoadingState * Move to another folder * Adapt the analysis page to use the new state component * Simplify the async state handler's props * Fix i18n translation * Fix the loading delay for comment section * Clear tanstack data upon logout * Update all the comment APIs to throw errors for Tanstack * Clear tanstack data when user logs in * Add test page for the state handler * Switch the loading indicator * Simplify the props for AsyncStateHandler * Fix locked post check * Fix moderation permission * Fix the props for loading handler * Cleanup old i18n key * Add i18n translation for the analysis page * Update some packages * Update a few more packages * More packages updated * Update vue-tsc * Apply refresh data fixes * Fix reactivity on tab switching * Fix data reactivity * Remove unused imports * Simplify conversation data composable * Improve accessibility * Fixed opinion draft clearing bug * Reset the highlighting when user changes the filter * Update typescript to 5.9.2 * Update maz-ui to v4 * Improve the opinion pagination * Move opinion not found to its own component * Split the code into composables * Fix the optimistic vote update * Fix maz ui config * Revert maz-ui * Add transition animation * Move to always mount the two tabs for conversations * Align terms to tanstack states * Add clarification comment on the staleness * Fix bug where user votes are fetched when unauthenticated * Fix loading bug when entering a conversation * Always refresh the cache when loading a conversation * Add loading indicator when comments or analysis tab is loading * Cleanup i18n old key * Add a cache flashing fix * Prevent immediate refetch when user switches tabs * Add local vote tracking to delay the update of the analysis tab * Switch to mount each tab one at a time to simplify the design * Move all the cachable queries to the PostDetails * Force refresh analysis cache when a comment is submitted * Fix bug where no opinion message wasn't showing properly
1 parent bf5b831 commit e3e503b

219 files changed

Lines changed: 5217 additions & 2131 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

services/agora/package.json

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -34,64 +34,65 @@
3434
"dependencies": {
3535
"@aparajita/capacitor-biometric-auth": "^8.0.0",
3636
"@aparajita/capacitor-secure-storage": "^6.0.1",
37-
"@dicebear/collection": "^9.2.2",
38-
"@dicebear/core": "^9.2.2",
39-
"@primeuix/themes": "^1.2.1",
37+
"@dicebear/collection": "^9.2.4",
38+
"@dicebear/core": "^9.2.4",
39+
"@primeuix/themes": "^1.2.3",
4040
"@quasar/extras": "^1.17.0",
4141
"@sentry/vite-plugin": "^4.3.0",
4242
"@sentry/vue": "^10.13.0",
43+
"@tanstack/vue-query": "^5.89.0",
4344
"@ucans/ucans": "^0.12.0",
44-
"@vueuse/core": "^13.1.0",
45-
"@vueuse/integrations": "^13.1.0",
46-
"@vueuse/router": "^13.1.0",
45+
"@vueuse/core": "^13.9.0",
46+
"@vueuse/integrations": "^13.9.0",
47+
"@vueuse/router": "^13.9.0",
4748
"@zkorum/capacitor-secure-signing": "^1.4.0",
4849
"@zkorum/keystore-idb": "^0.18.1",
49-
"axios": "^1.11.0",
50+
"axios": "^1.12.2",
5051
"buffer": "^6.0.3",
51-
"cborg": "^4.2.12",
52-
"libphonenumber-js": "^1.12.7",
53-
"linkify-html": "^4.3.1",
54-
"linkifyjs": "^4.3.1",
52+
"cborg": "^4.2.15",
53+
"libphonenumber-js": "^1.12.17",
54+
"linkify-html": "^4.3.2",
55+
"linkifyjs": "^4.3.2",
5556
"maz-ui": "^3.50.1",
56-
"multiformats": "^13.3.0",
57+
"multiformats": "^13.4.1",
5758
"pinia": "^3.0.2",
5859
"primeicons": "^7.0.0",
59-
"primevue": "^4.3.6",
60+
"primevue": "^4.3.9",
6061
"qrcode": "^1",
61-
"quasar": "^2.18.2",
62+
"quasar": "^2.18.5",
6263
"sanitize-html": "^2.16.0",
63-
"swiper": "^11.2.6",
64+
"swiper": "^12.0.2",
6465
"tweetnacl": "^1.0.3",
6566
"uint8arrays": "^5.1.0",
66-
"vue": "^3.5.18",
67-
"vue-i18n": "^11.1.11",
67+
"vue": "^3.5.21",
68+
"vue-i18n": "^11.1.12",
6869
"vue-router": "^4.5.1",
69-
"zod": "^4.0.9"
70+
"zod": "^4.1.9"
7071
},
7172
"devDependencies": {
72-
"@eslint/js": "^9.14.0",
73-
"@eslint/json": "^0.13.1",
73+
"@eslint/js": "^9.35.0",
74+
"@eslint/json": "^0.13.2",
7475
"@iconify/vue": "^5.0.0",
75-
"@quasar/app-vite": "^2.3.0",
76-
"@types/node": "^24.1.0",
76+
"@quasar/app-vite": "2.3.0",
77+
"@types/node": "^24.5.2",
7778
"@types/sanitize-html": "^2.13.0",
7879
"@vitejs/plugin-basic-ssl": "^2.0.0",
7980
"@vue/eslint-config-prettier": "^10.2.0",
8081
"@vue/eslint-config-typescript": "^14.6.0",
8182
"autoprefixer": "^10.4.21",
82-
"dotenv": "^17.2.0",
83-
"env-cmd": "^10.1.0",
84-
"eslint": "^9.14.0",
85-
"eslint-plugin-vue": "^10.3.0",
86-
"globals": "^16.3.0",
83+
"dotenv": "^17.2.2",
84+
"env-cmd": "^11.0.0",
85+
"eslint": "^9.35.0",
86+
"eslint-plugin-vue": "^10.4.0",
87+
"globals": "^16.4.0",
8788
"prettier": "^3.4.2",
88-
"typescript": "5.5.4",
89+
"typescript": "5.9.2",
8990
"unplugin-vue-router": "^0.15.0",
90-
"vite-plugin-checker": "^0.10.1",
91-
"vue-tsc": "^2.2.12"
91+
"vite-plugin-checker": "^0.10.3",
92+
"vue-tsc": "^3.0.7"
9293
},
9394
"engines": {
94-
"node": "^24 || ^22 || ^20 || ^18",
95+
"node": "^24 || ^22 || ^20",
9596
"npm": ">= 6.13.4",
9697
"yarn": ">= 1.21.1"
9798
}

services/agora/quasar.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export default defineConfig((ctx) => {
1414
if (ctx.prod && process.env.VITE_STAGING !== "true") {
1515
boot.push("sentry");
1616
}
17-
boot.push(...["i18n", "axios", "primevue", "maz-ui"]);
17+
boot.push(...["i18n", "axios", "primevue", "maz-ui", "vue-query"]);
1818
console.log("Loaded boot files", boot);
1919

2020
return {
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { boot } from "quasar/wrappers";
2+
import { VueQueryPlugin, QueryClient } from "@tanstack/vue-query";
3+
import type { AxiosErrorResponse } from "src/utils/api/common";
4+
import { isTimeoutError, shouldRetryError } from "src/utils/api/common";
5+
6+
// Create a client with custom configuration for our timeout and retry needs
7+
const queryClient = new QueryClient({
8+
defaultOptions: {
9+
queries: {
10+
// Default stale time - data considered fresh for 5 minutes
11+
staleTime: 1000 * 60 * 5,
12+
// Cache time - keep unused data for 10 minutes
13+
gcTime: 1000 * 60 * 10,
14+
// Retry configuration based on our error handling logic
15+
retry: (failureCount, error) => {
16+
// Don't retry more than 3 times
17+
if (failureCount >= 3) return false;
18+
19+
// Check if it's an Axios error and should be retried
20+
const axiosError = error as AxiosErrorResponse;
21+
if (axiosError.code) {
22+
return shouldRetryError(axiosError.code);
23+
}
24+
25+
// For non-Axios errors, retry network and server errors
26+
return (
27+
error instanceof Error &&
28+
(error.message.includes("Network") ||
29+
error.message.includes("timeout") ||
30+
error.message.includes("fetch"))
31+
);
32+
},
33+
// Exponential backoff with jitter
34+
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
35+
// Keep data fresh while refetching in background
36+
refetchOnWindowFocus: false,
37+
// Background refetch interval for important data
38+
refetchInterval: false,
39+
},
40+
mutations: {
41+
// Retry mutations for timeout/network errors
42+
retry: (failureCount, error) => {
43+
if (failureCount >= 2) return false;
44+
45+
const axiosError = error as AxiosErrorResponse;
46+
if (axiosError.code) {
47+
return (
48+
isTimeoutError(axiosError.code) || axiosError.code === "ERR_NETWORK"
49+
);
50+
}
51+
52+
return false;
53+
},
54+
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 10000),
55+
},
56+
},
57+
});
58+
59+
export default boot(({ app }) => {
60+
app.use(VueQueryPlugin, {
61+
queryClient,
62+
});
63+
});
64+
65+
export { queryClient };

services/agora/src/components/account/EmbedAccountWidget.i18n.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import type { SupportedDisplayLanguageCodes } from "src/shared/languages";
2+
13
export interface EmbedAccountWidgetTranslations {
24
logoutButton: string;
35
logoutTooltip: string;
46
}
57

68
export const embedAccountWidgetTranslations: Record<
7-
string,
9+
SupportedDisplayLanguageCodes,
810
EmbedAccountWidgetTranslations
911
> = {
1012
en: {

services/agora/src/components/account/EmbedAccountWidget.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131
<script setup lang="ts">
3232
import { storeToRefs } from "pinia";
33-
import { useComponentI18n } from "src/composables/useComponentI18n";
33+
import { useComponentI18n } from "src/composables/ui/useComponentI18n";
3434
import { useAuthenticationStore } from "src/stores/authentication";
3535
import { useUserStore } from "src/stores/user";
3636
import { useAuthSetup } from "src/utils/auth/setup";

services/agora/src/components/account/UsernameChange.i18n.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1+
import type { SupportedDisplayLanguageCodes } from "src/shared/languages";
2+
13
export interface UsernameChangeTranslations {
24
usernameLabel: string;
35
updateButton: string;
46
usernameChanged: string;
57
usernameAlreadyInUse: string;
68
usernameCurrentlyInUse: string;
79
submitError: string;
8-
[key: string]: string;
910
}
1011

1112
export const usernameChangeTranslations: Record<
12-
string,
13+
SupportedDisplayLanguageCodes,
1314
UsernameChangeTranslations
1415
> = {
1516
en: {

services/agora/src/components/account/UsernameChange.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import { useUserStore } from "src/stores/user";
4848
import { storeToRefs } from "pinia";
4949
import { useNotify } from "src/utils/ui/notify";
5050
import { useCommonApi } from "src/utils/api/common";
51-
import { useComponentI18n } from "src/composables/useComponentI18n";
51+
import { useComponentI18n } from "src/composables/ui/useComponentI18n";
5252
import {
5353
usernameChangeTranslations,
5454
type UsernameChangeTranslations,

services/agora/src/components/administrator/organization/CreateOrganizationForm.i18n.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { SupportedDisplayLanguageCodes } from "src/shared/languages";
2+
13
export interface CreateOrganizationFormTranslations {
24
nameLabel: string;
35
descriptionLabel: string;
@@ -9,7 +11,7 @@ export interface CreateOrganizationFormTranslations {
911
}
1012

1113
export const createOrganizationFormTranslations: Record<
12-
string,
14+
SupportedDisplayLanguageCodes,
1315
CreateOrganizationFormTranslations
1416
> = {
1517
en: {

services/agora/src/components/administrator/organization/CreateOrganizationForm.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
</template>
2121

2222
<script setup lang="ts">
23-
import { useComponentI18n } from "src/composables/useComponentI18n";
23+
import { useComponentI18n } from "src/composables/ui/useComponentI18n";
2424
import ZKButton from "src/components/ui-library/ZKButton.vue";
2525
import { useBackendAdministratorOrganizationApi } from "src/utils/api/administrator/organization";
2626
import { ref } from "vue";

services/agora/src/components/administrator/organization/DeleteOrganizationForm.i18n.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
import type { SupportedDisplayLanguageCodes } from "src/shared/languages";
2+
13
export interface DeleteOrganizationFormTranslations {
24
noOrganizationsMessage: string;
3-
[key: string]: string;
45
}
56

67
export const deleteOrganizationFormTranslations: Record<
7-
string,
8+
SupportedDisplayLanguageCodes,
89
DeleteOrganizationFormTranslations
910
> = {
1011
en: {

0 commit comments

Comments
 (0)