nfx-ui 0.35.0 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.en.md +1 -1
  2. package/README.md +1 -1
  3. package/dist/hooks/auth.d.ts +30 -29
  4. package/dist/hooks/auth.d.ts.map +1 -1
  5. package/dist/hooks/auth.js +225 -226
  6. package/dist/hooks/auth.js.map +1 -1
  7. package/dist/hooks/index.d.ts +1 -0
  8. package/dist/hooks/index.d.ts.map +1 -1
  9. package/dist/hooks/index.js +9 -8
  10. package/dist/hooks/toast.d.ts +12 -0
  11. package/dist/hooks/toast.d.ts.map +1 -0
  12. package/dist/hooks/toast.js +13 -0
  13. package/dist/hooks/toast.js.map +1 -0
  14. package/dist/languages/hooks/useLanguageLabel.d.ts.map +1 -1
  15. package/dist/languages/hooks/useLanguageLabel.js +11 -10
  16. package/dist/languages/hooks/useLanguageLabel.js.map +1 -1
  17. package/dist/languages/index.d.ts +0 -1
  18. package/dist/languages/index.d.ts.map +1 -1
  19. package/dist/languages/index.js +9 -10
  20. package/dist/languages/languages/i18n.d.ts.map +1 -1
  21. package/dist/languages/languages/i18n.js +30 -41
  22. package/dist/languages/languages/i18n.js.map +1 -1
  23. package/dist/schemas/loginSchema.d.ts +3 -3
  24. package/dist/schemas/loginSchema.d.ts.map +1 -1
  25. package/dist/schemas/loginSchema.js +22 -23
  26. package/dist/schemas/loginSchema.js.map +1 -1
  27. package/dist/schemas/profileEditSchema.d.ts +1 -1
  28. package/dist/schemas/profileEditSchema.d.ts.map +1 -1
  29. package/dist/schemas/profileEditSchema.js +18 -19
  30. package/dist/schemas/profileEditSchema.js.map +1 -1
  31. package/dist/schemas/signupSchema.d.ts +2 -2
  32. package/dist/schemas/signupSchema.d.ts.map +1 -1
  33. package/dist/schemas/signupSchema.js +16 -17
  34. package/dist/schemas/signupSchema.js.map +1 -1
  35. package/package.json +1 -1
  36. package/src/hooks/auth.ts +79 -98
  37. package/src/hooks/index.ts +1 -0
  38. package/src/hooks/toast.ts +23 -0
  39. package/src/languages/hooks/useLanguageLabel.ts +2 -3
  40. package/src/languages/index.ts +0 -1
  41. package/src/languages/languages/i18n.ts +3 -17
  42. package/src/schemas/loginSchema.ts +3 -6
  43. package/src/schemas/profileEditSchema.ts +1 -3
  44. package/src/schemas/signupSchema.ts +2 -4
  45. package/dist/languages/resources/en/language.js +0 -52
  46. package/dist/languages/resources/en/language.js.map +0 -1
  47. package/dist/languages/resources/en/language.json.d.ts +0 -49
  48. package/dist/languages/resources/en/layout.js +0 -9
  49. package/dist/languages/resources/en/layout.js.map +0 -1
  50. package/dist/languages/resources/en/layout.json.d.ts +0 -9
  51. package/dist/languages/resources/en/preference.js +0 -50
  52. package/dist/languages/resources/en/preference.js.map +0 -1
  53. package/dist/languages/resources/en/preference.json.d.ts +0 -51
  54. package/dist/languages/resources/en/theme.js +0 -19
  55. package/dist/languages/resources/en/theme.js.map +0 -1
  56. package/dist/languages/resources/en/theme.json.d.ts +0 -17
  57. package/dist/languages/resources/fr/language.js +0 -52
  58. package/dist/languages/resources/fr/language.js.map +0 -1
  59. package/dist/languages/resources/fr/language.json.d.ts +0 -49
  60. package/dist/languages/resources/fr/layout.js +0 -9
  61. package/dist/languages/resources/fr/layout.js.map +0 -1
  62. package/dist/languages/resources/fr/layout.json.d.ts +0 -9
  63. package/dist/languages/resources/fr/preference.js +0 -50
  64. package/dist/languages/resources/fr/preference.js.map +0 -1
  65. package/dist/languages/resources/fr/preference.json.d.ts +0 -51
  66. package/dist/languages/resources/fr/theme.js +0 -19
  67. package/dist/languages/resources/fr/theme.js.map +0 -1
  68. package/dist/languages/resources/fr/theme.json.d.ts +0 -17
  69. package/dist/languages/resources/index.d.ts +0 -9
  70. package/dist/languages/resources/index.d.ts.map +0 -1
  71. package/dist/languages/resources/index.js +0 -54
  72. package/dist/languages/resources/index.js.map +0 -1
  73. package/dist/languages/resources/zh/language.js +0 -52
  74. package/dist/languages/resources/zh/language.js.map +0 -1
  75. package/dist/languages/resources/zh/language.json.d.ts +0 -49
  76. package/dist/languages/resources/zh/layout.js +0 -9
  77. package/dist/languages/resources/zh/layout.js.map +0 -1
  78. package/dist/languages/resources/zh/layout.json.d.ts +0 -9
  79. package/dist/languages/resources/zh/preference.js +0 -50
  80. package/dist/languages/resources/zh/preference.js.map +0 -1
  81. package/dist/languages/resources/zh/preference.json.d.ts +0 -51
  82. package/dist/languages/resources/zh/theme.js +0 -19
  83. package/dist/languages/resources/zh/theme.js.map +0 -1
  84. package/dist/languages/resources/zh/theme.json.d.ts +0 -17
  85. package/src/languages/resources/en/language.json +0 -47
  86. package/src/languages/resources/en/layout.json +0 -6
  87. package/src/languages/resources/en/preference.json +0 -49
  88. package/src/languages/resources/en/theme.json +0 -14
  89. package/src/languages/resources/fr/language.json +0 -47
  90. package/src/languages/resources/fr/layout.json +0 -6
  91. package/src/languages/resources/fr/preference.json +0 -49
  92. package/src/languages/resources/fr/theme.json +0 -14
  93. package/src/languages/resources/index.ts +0 -62
  94. package/src/languages/resources/zh/language.json +0 -47
  95. package/src/languages/resources/zh/layout.json +0 -6
  96. package/src/languages/resources/zh/preference.json +0 -49
  97. package/src/languages/resources/zh/theme.json +0 -14
package/src/hooks/auth.ts CHANGED
@@ -25,11 +25,11 @@ import {
25
25
  useAuthStore,
26
26
  } from "nfx-ui/stores/auth";
27
27
  import { ensureDeviceIdStorage } from "nfx-ui/stores/system";
28
- import { getApiErrorMessage } from "nfx-ui/utils/apiError";
29
28
  import { pickProfile } from "nfx-ui/utils/domain/account";
30
29
  import { useUnifiedQuery } from "nfx-ui/utils/factory";
31
30
  import { safeOr, safeStringable } from "nfx-ui/utils/safe";
32
- import { useTranslation } from "react-i18next";
31
+
32
+ import { emitHookError, emitHookSuccess, type HookToastMessages } from "./toast";
33
33
 
34
34
  export function useAuthQueryScope() {
35
35
  const aID = useAuthStore((s) => s.currentAccountId);
@@ -68,21 +68,20 @@ export function useCurrentProfile(): CurrentProfileResult {
68
68
  } as CurrentProfileResult;
69
69
  }
70
70
 
71
- export const useSendVerificationCode = () => {
71
+ export const useSendVerificationCode = (options?: HookToastMessages) => {
72
72
  const auth = useAuthRepository();
73
- const { t } = useTranslation("hooks", { keyPrefix: "account" });
74
73
  return useMutation({
75
74
  mutationFn: (params: Signup.Request.SendVerificationCode) => auth.SendVerificationCode(params),
76
75
  onSuccess: () => {
77
- systemEventEmitter.showSuccess(t("sendVerificationCodeSuccess", { defaultValue: "Code sent" }));
76
+ emitHookSuccess(options);
78
77
  },
79
78
  onError: (error: AxiosError) => {
80
- systemEventEmitter.showError(getApiErrorMessage(error, "[useSendVerificationCode] error"));
79
+ emitHookError(error, options, "[useSendVerificationCode] error");
81
80
  },
82
81
  });
83
82
  };
84
83
 
85
- export const useSignupWithEmail = () => {
84
+ export const useSignupWithEmail = (options?: HookToastMessages) => {
86
85
  const auth = useAuthRepository();
87
86
  return useMutation({
88
87
  mutationFn: async ({
@@ -117,12 +116,12 @@ export const useSignupWithEmail = () => {
117
116
  }
118
117
  },
119
118
  onError: (error: AxiosError) => {
120
- systemEventEmitter.showError(getApiErrorMessage(error, "[useSignup] error"));
119
+ emitHookError(error, options, "[useSignup] error");
121
120
  },
122
121
  });
123
122
  };
124
123
 
125
- export const useLoginWithEmail = () => {
124
+ export const useLoginWithEmail = (options?: HookToastMessages) => {
126
125
  const auth = useAuthRepository();
127
126
  return useMutation({
128
127
  mutationFn: async ({ rememberMe, ...params }: { email: string; password: string; rememberMe: boolean }) =>
@@ -144,12 +143,12 @@ export const useLoginWithEmail = () => {
144
143
  if (result.accountId) setCurrentAccountId(result.accountId);
145
144
  },
146
145
  onError: (error: AxiosError) => {
147
- systemEventEmitter.showError(getApiErrorMessage(error, "[useLogin] error"));
146
+ emitHookError(error, options, "[useLogin] error");
148
147
  },
149
148
  });
150
149
  };
151
150
 
152
- export const useLoginWithPhone = () => {
151
+ export const useLoginWithPhone = (options?: HookToastMessages) => {
153
152
  const auth = useAuthRepository();
154
153
  return useMutation({
155
154
  mutationFn: async ({ rememberMe, ...params }: { phone: string; password: string; rememberMe: boolean }) =>
@@ -171,12 +170,12 @@ export const useLoginWithPhone = () => {
171
170
  if (result.accountId) setCurrentAccountId(result.accountId);
172
171
  },
173
172
  onError: (error: AxiosError) => {
174
- systemEventEmitter.showError(getApiErrorMessage(error, "[useLoginWithPhone] error"));
173
+ emitHookError(error, options, "[useLoginWithPhone] error");
175
174
  },
176
175
  });
177
176
  };
178
177
 
179
- export type SelectProfileOptions = {
178
+ export type SelectProfileOptions = HookToastMessages & {
180
179
  /** 已翻译的加载文案。Host-translated loading copy. */
181
180
  switchingMsg?: string;
182
181
  /** 与 token、档案同一次提交里执行(例如跳到该档案的首页)。Runs in the same commit as the token and profile. */
@@ -216,53 +215,51 @@ export const useSelectProfile = (options?: SelectProfileOptions) => {
216
215
  },
217
216
  onError: (error: AxiosError) => {
218
217
  systemEventEmitter.hideLoading();
219
- systemEventEmitter.showError(getApiErrorMessage(error, "[useSelectProfile] error"));
218
+ emitHookError(error, options, "[useSelectProfile] error");
220
219
  },
221
220
  });
222
221
  };
223
222
 
224
- export const usePatchProfile = (options?: { silent?: boolean }) => {
223
+ export const usePatchProfile = (options?: { silent?: boolean } & HookToastMessages) => {
225
224
  const auth = useAuthRepository();
226
225
  const { kind } = useAuthQueryScope();
227
226
  const silent = safeOr(options?.silent, false);
228
- const { t } = useTranslation("pages.User.Profile.Edit");
229
227
  return useMutation({
230
228
  mutationFn: (body: Profile.Request.PatchProfile) => auth.PatchProfile(kind, body),
231
229
  onSuccess: () => {
232
- if (!silent) systemEventEmitter.showSuccess(t("saveSuccess", { defaultValue: "Saved" }));
230
+ if (!silent) emitHookSuccess(options);
233
231
  const aID = AuthStore.getState().currentAccountId;
234
232
  authEventEmitter.invalidateProfiles({ aID, kind });
235
233
  },
236
234
  onError: (error: AxiosError) => {
237
- if (!silent) systemEventEmitter.showError(getApiErrorMessage(error, "[usePatchProfile] error"));
235
+ emitHookError(error, options, "[usePatchProfile] error", silent);
238
236
  },
239
237
  });
240
238
  };
241
239
 
242
- export const useUpdateProfileSettings = () => {
240
+ export const useUpdateProfileSettings = (options?: HookToastMessages) => {
243
241
  const auth = useAuthRepository();
244
242
  const { kind } = useAuthQueryScope();
245
- const { t } = useTranslation("hooks", { keyPrefix: "account" });
246
243
  return useMutation({
247
244
  mutationFn: (body: Profile.Request.PatchProfileSettings) => auth.PatchProfileSettings(kind, body),
248
245
  onSuccess: () => {
249
246
  const aID = AuthStore.getState().currentAccountId;
250
247
  authEventEmitter.emit(authEvents.UPDATE_ACCOUNT_SUCCESS, aID);
251
- systemEventEmitter.showSuccess(t("updateSettingsSuccess", { defaultValue: "Settings updated." }));
248
+ emitHookSuccess(options);
252
249
  },
253
250
  onError: (error: AxiosError) => {
254
- systemEventEmitter.showError(getApiErrorMessage(error, "[useUpdateProfileSettings] error"));
251
+ emitHookError(error, options, "[useUpdateProfileSettings] error");
255
252
  },
256
253
  });
257
254
  };
258
255
 
259
- export const useUpdatePreference = () => {
256
+ export const useUpdatePreference = (options?: HookToastMessages) => {
260
257
  const auth = useAuthRepository();
261
258
  const { kind } = useAuthQueryScope();
262
259
  return useMutation({
263
260
  mutationFn: (preference: string) => auth.UpdatePreference(kind, preference),
264
261
  onError: (error: AxiosError) => {
265
- systemEventEmitter.showError(getApiErrorMessage(error, "[useUpdatePreference] error"));
262
+ emitHookError(error, options, "[useUpdatePreference] error");
266
263
  },
267
264
  });
268
265
  };
@@ -278,7 +275,7 @@ export const useListProfiles = <K extends ProfileKindEnum>(kind: K) => {
278
275
  );
279
276
  };
280
277
 
281
- export const useCreateForgerProfile = () => {
278
+ export const useCreateForgerProfile = (options?: HookToastMessages) => {
282
279
  const auth = useAuthRepository();
283
280
  return useMutation({
284
281
  mutationFn: (body: Login.Request.CreateForgerProfile) => auth.CreateForgerProfile(body),
@@ -287,12 +284,12 @@ export const useCreateForgerProfile = () => {
287
284
  authEventEmitter.invalidateProfiles({ aID, kind: ProfileKindEnum.COMMUNITY });
288
285
  },
289
286
  onError: (error: AxiosError) => {
290
- systemEventEmitter.showError(getApiErrorMessage(error, "[useCreateForgerProfile] error"));
287
+ emitHookError(error, options, "[useCreateForgerProfile] error");
291
288
  },
292
289
  });
293
290
  };
294
291
 
295
- export const useCreateAuthorityProfile = () => {
292
+ export const useCreateAuthorityProfile = (options?: HookToastMessages) => {
296
293
  const auth = useAuthRepository();
297
294
  return useMutation({
298
295
  mutationFn: (body: Login.Request.CreateAuthorityProfile) => auth.CreateAuthorityProfile(body),
@@ -301,12 +298,12 @@ export const useCreateAuthorityProfile = () => {
301
298
  authEventEmitter.invalidateProfiles({ aID, kind: ProfileKindEnum.AUTHORITY });
302
299
  },
303
300
  onError: (error: AxiosError) => {
304
- systemEventEmitter.showError(getApiErrorMessage(error, "[useCreateAuthorityProfile] error"));
301
+ emitHookError(error, options, "[useCreateAuthorityProfile] error");
305
302
  },
306
303
  });
307
304
  };
308
305
 
309
- export const useDeleteProfile = () => {
306
+ export const useDeleteProfile = (options?: HookToastMessages) => {
310
307
  const auth = useAuthRepository();
311
308
  return useMutation({
312
309
  mutationFn: ({ kind, profileId }: { kind: ProfileKindEnum; profileId: string }) => auth.DeleteProfile(kind, profileId),
@@ -315,12 +312,12 @@ export const useDeleteProfile = () => {
315
312
  authEventEmitter.invalidateProfiles({ aID, kind: variables.kind });
316
313
  },
317
314
  onError: (error: AxiosError) => {
318
- systemEventEmitter.showError(getApiErrorMessage(error, "[useDeleteProfile] error"));
315
+ emitHookError(error, options, "[useDeleteProfile] error");
319
316
  },
320
317
  });
321
318
  };
322
319
 
323
- export const useConfirmProfileAvatar = () => {
320
+ export const useConfirmProfileAvatar = (options?: HookToastMessages) => {
324
321
  const auth = useAuthRepository();
325
322
  const { kind } = useAuthQueryScope();
326
323
  return useMutation({
@@ -330,24 +327,23 @@ export const useConfirmProfileAvatar = () => {
330
327
  authEventEmitter.emit(authEvents.UPDATE_ACCOUNT_SUCCESS, aID);
331
328
  },
332
329
  onError: (error: AxiosError) => {
333
- systemEventEmitter.showError(getApiErrorMessage(error, "[useConfirmProfileAvatar] error"));
330
+ emitHookError(error, options, "[useConfirmProfileAvatar] error");
334
331
  },
335
332
  });
336
333
  };
337
334
 
338
- export const useClearProfileAvatar = () => {
335
+ export const useClearProfileAvatar = (options?: HookToastMessages) => {
339
336
  const auth = useAuthRepository();
340
337
  const { kind } = useAuthQueryScope();
341
- const { t } = useTranslation("hooks", { keyPrefix: "account" });
342
338
  return useMutation({
343
339
  mutationFn: () => auth.ClearProfileAvatar(kind),
344
340
  onSuccess: () => {
345
341
  const aID = AuthStore.getState().currentAccountId;
346
342
  authEventEmitter.emit(authEvents.UPDATE_ACCOUNT_SUCCESS, aID);
347
- systemEventEmitter.showSuccess(t("clearAvatarSuccess", { defaultValue: "Avatar removed." }));
343
+ emitHookSuccess(options);
348
344
  },
349
345
  onError: (error: AxiosError) => {
350
- systemEventEmitter.showError(getApiErrorMessage(error, "[useClearProfileAvatar] error"));
346
+ emitHookError(error, options, "[useClearProfileAvatar] error");
351
347
  },
352
348
  });
353
349
  };
@@ -368,131 +364,123 @@ export const useListPhones = () => {
368
364
  });
369
365
  };
370
366
 
371
- export const useCreateEmail = () => {
367
+ export const useCreateEmail = (options?: HookToastMessages) => {
372
368
  const auth = useAuthRepository();
373
369
  const accountId = AuthStore.getState().currentAccountId;
374
- const { t } = useTranslation("hooks", { keyPrefix: "account" });
375
370
  return useMutation({
376
371
  mutationFn: (body: Login.Request.CreateEmail) => auth.CreateEmail(body),
377
372
  onSuccess: () => {
378
373
  authEventEmitter.invalidateEmails(accountId);
379
- systemEventEmitter.showSuccess(t("createEmailSuccess", { defaultValue: "Email added." }));
374
+ emitHookSuccess(options);
380
375
  },
381
376
  onError: (error: AxiosError) => {
382
- systemEventEmitter.showError(getApiErrorMessage(error, "[useCreateEmail] error"));
377
+ emitHookError(error, options, "[useCreateEmail] error");
383
378
  },
384
379
  });
385
380
  };
386
381
 
387
- export const useSendEmailVerificationCode = () => {
382
+ export const useSendEmailVerificationCode = (options?: HookToastMessages) => {
388
383
  const auth = useAuthRepository();
389
- const { t } = useTranslation("hooks", { keyPrefix: "account" });
390
384
  return useMutation({
391
385
  mutationFn: ({ emailId, lang }: { emailId: string; lang?: LanguageEnum }) => auth.SendEmailVerificationCode(emailId, { lang }),
392
386
  onSuccess: () => {
393
- systemEventEmitter.showSuccess(t("sendVerificationCodeSuccess", { defaultValue: "Code sent" }));
387
+ emitHookSuccess(options);
394
388
  },
395
389
  onError: (error: AxiosError) => {
396
- systemEventEmitter.showError(getApiErrorMessage(error, "[useSendEmailVerificationCode] error"));
390
+ emitHookError(error, options, "[useSendEmailVerificationCode] error");
397
391
  },
398
392
  });
399
393
  };
400
394
 
401
- export const useVerifyEmail = () => {
395
+ export const useVerifyEmail = (options?: HookToastMessages) => {
402
396
  const auth = useAuthRepository();
403
397
  const accountId = AuthStore.getState().currentAccountId;
404
- const { t } = useTranslation("hooks", { keyPrefix: "account" });
405
398
  return useMutation({
406
399
  mutationFn: ({ emailId, verificationCode }: { emailId: string; verificationCode: string }) =>
407
400
  auth.VerifyEmail(emailId, { verificationCode }),
408
401
  onSuccess: () => {
409
402
  authEventEmitter.invalidateEmails(accountId);
410
- systemEventEmitter.showSuccess(t("verifyEmailSuccess", { defaultValue: "Email verified." }));
403
+ emitHookSuccess(options);
411
404
  },
412
405
  onError: (error: AxiosError) => {
413
- systemEventEmitter.showError(getApiErrorMessage(error, "[useVerifyEmail] error"));
406
+ emitHookError(error, options, "[useVerifyEmail] error");
414
407
  },
415
408
  });
416
409
  };
417
410
 
418
- export const useUpdateEmail = () => {
411
+ export const useUpdateEmail = (options?: HookToastMessages) => {
419
412
  const auth = useAuthRepository();
420
413
  const accountId = AuthStore.getState().currentAccountId;
421
- const { t } = useTranslation("hooks", { keyPrefix: "account" });
422
414
  return useMutation({
423
415
  mutationFn: ({ emailId, email }: { emailId: string; email: string }) => auth.UpdateEmail(emailId, { email }),
424
416
  onSuccess: () => {
425
417
  authEventEmitter.invalidateEmails(accountId);
426
- systemEventEmitter.showSuccess(t("updateEmailSuccess", { defaultValue: "Email updated." }));
418
+ emitHookSuccess(options);
427
419
  },
428
420
  onError: (error: AxiosError) => {
429
- systemEventEmitter.showError(getApiErrorMessage(error, "[useUpdateEmail] error"));
421
+ emitHookError(error, options, "[useUpdateEmail] error");
430
422
  },
431
423
  });
432
424
  };
433
425
 
434
- export const useSetPrimaryEmail = () => {
426
+ export const useSetPrimaryEmail = (options?: HookToastMessages) => {
435
427
  const auth = useAuthRepository();
436
428
  const accountId = AuthStore.getState().currentAccountId;
437
- const { t } = useTranslation("hooks", { keyPrefix: "account" });
438
429
  return useMutation({
439
430
  mutationFn: (emailId: string) => auth.SetPrimaryEmail(emailId),
440
431
  onSuccess: () => {
441
432
  authEventEmitter.invalidateEmails(accountId);
442
433
  authEventEmitter.emit(authEvents.UPDATE_ACCOUNT_SUCCESS, accountId);
443
- systemEventEmitter.showSuccess(t("setPrimaryEmailSuccess", { defaultValue: "Primary email updated." }));
434
+ emitHookSuccess(options);
444
435
  },
445
436
  onError: (error: AxiosError) => {
446
- systemEventEmitter.showError(getApiErrorMessage(error, "[useSetPrimaryEmail] error"));
437
+ emitHookError(error, options, "[useSetPrimaryEmail] error");
447
438
  },
448
439
  });
449
440
  };
450
441
 
451
- export const useDeleteEmail = () => {
442
+ export const useDeleteEmail = (options?: HookToastMessages) => {
452
443
  const auth = useAuthRepository();
453
444
  const accountId = AuthStore.getState().currentAccountId;
454
- const { t } = useTranslation("hooks", { keyPrefix: "account" });
455
445
  return useMutation({
456
446
  mutationFn: (emailId: string) => auth.DeleteEmail(emailId),
457
447
  onSuccess: () => {
458
448
  authEventEmitter.invalidateEmails(accountId);
459
- systemEventEmitter.showSuccess(t("deleteEmailSuccess", { defaultValue: "Email removed." }));
449
+ emitHookSuccess(options);
460
450
  },
461
451
  onError: (error: AxiosError) => {
462
- systemEventEmitter.showError(getApiErrorMessage(error, "[useDeleteEmail] error"));
452
+ emitHookError(error, options, "[useDeleteEmail] error");
463
453
  },
464
454
  });
465
455
  };
466
456
 
467
- export const useSendChangePasswordVerificationCode = () => {
457
+ export const useSendChangePasswordVerificationCode = (options?: HookToastMessages) => {
468
458
  const auth = useAuthRepository();
469
- const { t } = useTranslation("hooks", { keyPrefix: "account" });
470
459
  return useMutation({
471
460
  mutationFn: (body: Login.Request.SendChangePasswordVerificationCode = {}) => auth.SendChangePasswordVerificationCode(body),
472
461
  onSuccess: () => {
473
- systemEventEmitter.showSuccess(t("sendVerificationCodeSuccess", { defaultValue: "Code sent" }));
462
+ emitHookSuccess(options);
474
463
  },
475
464
  onError: (error: AxiosError) => {
476
- systemEventEmitter.showError(getApiErrorMessage(error, "[useSendChangePasswordVerificationCode] error"));
465
+ emitHookError(error, options, "[useSendChangePasswordVerificationCode] error");
477
466
  },
478
467
  });
479
468
  };
480
469
 
481
- export const useChangePassword = () => {
470
+ export const useChangePassword = (options?: HookToastMessages) => {
482
471
  const auth = useAuthRepository();
483
- const { t } = useTranslation("hooks", { keyPrefix: "account" });
484
472
  return useMutation({
485
473
  mutationFn: (body: Login.Request.ChangePassword) => auth.ChangePassword(body),
486
474
  onSuccess: () => {
487
- systemEventEmitter.showSuccess(t("changePasswordSuccess", { defaultValue: "Password updated." }));
475
+ emitHookSuccess(options);
488
476
  },
489
477
  onError: (error: AxiosError) => {
490
- systemEventEmitter.showError(getApiErrorMessage(error, "[useChangePassword] error"));
478
+ emitHookError(error, options, "[useChangePassword] error");
491
479
  },
492
480
  });
493
481
  };
494
482
 
495
- export const useConfirmProfileBackgrounds = () => {
483
+ export const useConfirmProfileBackgrounds = (options?: HookToastMessages) => {
496
484
  const auth = useAuthRepository();
497
485
  const { kind } = useAuthQueryScope();
498
486
  return useMutation({
@@ -502,103 +490,97 @@ export const useConfirmProfileBackgrounds = () => {
502
490
  authEventEmitter.emit(authEvents.UPDATE_ACCOUNT_SUCCESS, aID);
503
491
  },
504
492
  onError: (error: AxiosError) => {
505
- systemEventEmitter.showError(getApiErrorMessage(error, "[useConfirmProfileBackgrounds] error"));
493
+ emitHookError(error, options, "[useConfirmProfileBackgrounds] error");
506
494
  },
507
495
  });
508
496
  };
509
497
 
510
- export const useCreatePhone = () => {
498
+ export const useCreatePhone = (options?: HookToastMessages) => {
511
499
  const auth = useAuthRepository();
512
500
  const accountId = AuthStore.getState().currentAccountId;
513
- const { t } = useTranslation("hooks", { keyPrefix: "account" });
514
501
  return useMutation({
515
502
  mutationFn: (body: Login.Request.CreatePhone) => auth.CreatePhone(body),
516
503
  onSuccess: () => {
517
504
  authEventEmitter.invalidatePhones(accountId);
518
- systemEventEmitter.showSuccess(t("createPhoneSuccess", { defaultValue: "Phone added." }));
505
+ emitHookSuccess(options);
519
506
  },
520
507
  onError: (error: AxiosError) => {
521
- systemEventEmitter.showError(getApiErrorMessage(error, "[useCreatePhone] error"));
508
+ emitHookError(error, options, "[useCreatePhone] error");
522
509
  },
523
510
  });
524
511
  };
525
512
 
526
- export const useSendPhoneVerificationCode = () => {
513
+ export const useSendPhoneVerificationCode = (options?: HookToastMessages) => {
527
514
  const auth = useAuthRepository();
528
- const { t } = useTranslation("hooks", { keyPrefix: "account" });
529
515
  return useMutation({
530
516
  mutationFn: (phoneId: string) => auth.SendPhoneVerificationCode(phoneId),
531
517
  onSuccess: () => {
532
- systemEventEmitter.showSuccess(t("sendVerificationCodeSuccess", { defaultValue: "Code sent" }));
518
+ emitHookSuccess(options);
533
519
  },
534
520
  onError: (error: AxiosError) => {
535
- systemEventEmitter.showError(getApiErrorMessage(error, "[useSendPhoneVerificationCode] error"));
521
+ emitHookError(error, options, "[useSendPhoneVerificationCode] error");
536
522
  },
537
523
  });
538
524
  };
539
525
 
540
- export const useVerifyPhone = () => {
526
+ export const useVerifyPhone = (options?: HookToastMessages) => {
541
527
  const auth = useAuthRepository();
542
528
  const accountId = AuthStore.getState().currentAccountId;
543
- const { t } = useTranslation("hooks", { keyPrefix: "account" });
544
529
  return useMutation({
545
530
  mutationFn: ({ phoneId, verificationCode }: { phoneId: string; verificationCode: string }) =>
546
531
  auth.VerifyPhone(phoneId, { verificationCode }),
547
532
  onSuccess: () => {
548
533
  authEventEmitter.invalidatePhones(accountId);
549
- systemEventEmitter.showSuccess(t("verifyPhoneSuccess", { defaultValue: "Phone verified." }));
534
+ emitHookSuccess(options);
550
535
  },
551
536
  onError: (error: AxiosError) => {
552
- systemEventEmitter.showError(getApiErrorMessage(error, "[useVerifyPhone] error"));
537
+ emitHookError(error, options, "[useVerifyPhone] error");
553
538
  },
554
539
  });
555
540
  };
556
541
 
557
- export const useUpdatePhone = () => {
542
+ export const useUpdatePhone = (options?: HookToastMessages) => {
558
543
  const auth = useAuthRepository();
559
544
  const accountId = AuthStore.getState().currentAccountId;
560
- const { t } = useTranslation("hooks", { keyPrefix: "account" });
561
545
  return useMutation({
562
546
  mutationFn: ({ phoneId, phone }: { phoneId: string; phone: string }) => auth.UpdatePhone(phoneId, { phone }),
563
547
  onSuccess: () => {
564
548
  authEventEmitter.invalidatePhones(accountId);
565
- systemEventEmitter.showSuccess(t("updatePhoneSuccess", { defaultValue: "Phone updated." }));
549
+ emitHookSuccess(options);
566
550
  },
567
551
  onError: (error: AxiosError) => {
568
- systemEventEmitter.showError(getApiErrorMessage(error, "[useUpdatePhone] error"));
552
+ emitHookError(error, options, "[useUpdatePhone] error");
569
553
  },
570
554
  });
571
555
  };
572
556
 
573
- export const useSetPrimaryPhone = () => {
557
+ export const useSetPrimaryPhone = (options?: HookToastMessages) => {
574
558
  const auth = useAuthRepository();
575
559
  const accountId = AuthStore.getState().currentAccountId;
576
- const { t } = useTranslation("hooks", { keyPrefix: "account" });
577
560
  return useMutation({
578
561
  mutationFn: (phoneId: string) => auth.SetPrimaryPhone(phoneId),
579
562
  onSuccess: () => {
580
563
  authEventEmitter.invalidatePhones(accountId);
581
564
  authEventEmitter.emit(authEvents.UPDATE_ACCOUNT_SUCCESS, accountId);
582
- systemEventEmitter.showSuccess(t("setPrimaryPhoneSuccess", { defaultValue: "Primary phone updated." }));
565
+ emitHookSuccess(options);
583
566
  },
584
567
  onError: (error: AxiosError) => {
585
- systemEventEmitter.showError(getApiErrorMessage(error, "[useSetPrimaryPhone] error"));
568
+ emitHookError(error, options, "[useSetPrimaryPhone] error");
586
569
  },
587
570
  });
588
571
  };
589
572
 
590
- export const useDeletePhone = () => {
573
+ export const useDeletePhone = (options?: HookToastMessages) => {
591
574
  const auth = useAuthRepository();
592
575
  const accountId = AuthStore.getState().currentAccountId;
593
- const { t } = useTranslation("hooks", { keyPrefix: "account" });
594
576
  return useMutation({
595
577
  mutationFn: (phoneId: string) => auth.DeletePhone(phoneId),
596
578
  onSuccess: () => {
597
579
  authEventEmitter.invalidatePhones(accountId);
598
- systemEventEmitter.showSuccess(t("deletePhoneSuccess", { defaultValue: "Phone removed." }));
580
+ emitHookSuccess(options);
599
581
  },
600
582
  onError: (error: AxiosError) => {
601
- systemEventEmitter.showError(getApiErrorMessage(error, "[useDeletePhone] error"));
583
+ emitHookError(error, options, "[useDeletePhone] error");
602
584
  },
603
585
  });
604
586
  };
@@ -662,19 +644,18 @@ export const useListOwnerAuthorityProfiles = (query = "") => {
662
644
  );
663
645
  };
664
646
 
665
- export const useUpdateAuthorityProfileRoles = () => {
647
+ export const useUpdateAuthorityProfileRoles = (options?: HookToastMessages) => {
666
648
  const auth = useAuthRepository();
667
649
  const accountId = AuthStore.getState().currentAccountId;
668
- const { t } = useTranslation("hooks", { keyPrefix: "account" });
669
650
  return useMutation({
670
651
  mutationFn: ({ profileId, authorityRoles }: { profileId: string; authorityRoles: Profile.Request.UpdateAuthorityProfileRoles["authorityRoles"] }) =>
671
652
  auth.UpdateAuthorityProfileRoles(profileId, { authorityRoles }),
672
653
  onSuccess: () => {
673
654
  authEventEmitter.invalidateOwner(accountId);
674
- systemEventEmitter.showSuccess(t("updateRolesSuccess", { defaultValue: "Roles updated." }));
655
+ emitHookSuccess(options);
675
656
  },
676
657
  onError: (error: AxiosError) => {
677
- systemEventEmitter.showError(getApiErrorMessage(error, "[useUpdateAuthorityProfileRoles] error"));
658
+ emitHookError(error, options, "[useUpdateAuthorityProfileRoles] error");
678
659
  },
679
660
  });
680
661
  };
@@ -1,4 +1,5 @@
1
1
  export * from "./preference";
2
+ export * from "./toast";
2
3
  export * from "../utils/factory/hooks";
3
4
  export * from "./auth";
4
5
  export * from "./asset";
@@ -0,0 +1,23 @@
1
+ import type { AxiosError } from "axios";
2
+
3
+ import { systemEventEmitter } from "nfx-ui/events/system";
4
+ import { getApiErrorMessage } from "nfx-ui/utils/apiError";
5
+
6
+ /**
7
+ * Host resolves copy with its own `t`, then passes ready-to-show strings.
8
+ * Package mutation hooks never call `useTranslation`.
9
+ */
10
+ export type HookToastMessages = {
11
+ successMsg?: string;
12
+ failMsg?: string;
13
+ };
14
+
15
+ export function emitHookSuccess(toasts?: HookToastMessages, silent = false) {
16
+ if (silent || !toasts?.successMsg) return;
17
+ systemEventEmitter.showSuccess(toasts.successMsg);
18
+ }
19
+
20
+ export function emitHookError(error: AxiosError, toasts?: HookToastMessages, fallback = "Request failed", silent = false) {
21
+ if (silent) return;
22
+ systemEventEmitter.showError(getApiErrorMessage(error, toasts?.failMsg ?? fallback));
23
+ }
@@ -1,5 +1,4 @@
1
1
  import i18n from "i18next";
2
- import { useTranslation } from "react-i18next";
3
2
 
4
3
  import { BaseEnum } from "nfx-ui/enums/theme";
5
4
  import { LanguageEnum } from "../types";
@@ -16,9 +15,9 @@ function getLanguageI18nKey(lang: LanguageEnum): string {
16
15
  export function useLanguageLabel(): {
17
16
  getLanguageDisplayName: (lang: LanguageEnum) => string;
18
17
  } {
19
- const { t } = useTranslation("language");
20
18
  const getLanguageDisplayName = (lang: LanguageEnum): string =>
21
- t(getLanguageI18nKey(lang), {
19
+ i18n.t(getLanguageI18nKey(lang), {
20
+ ns: "language",
22
21
  defaultValue: lang,
23
22
  });
24
23
 
@@ -4,6 +4,5 @@
4
4
  export * from "./hooks";
5
5
  export * from "./languages";
6
6
  export { default as i18n } from "./languages/i18n";
7
- export * from "./resources";
8
7
  export * from "./types";
9
8
  export * from "./utils";
@@ -1,6 +1,6 @@
1
1
  /**
2
- * i18n 初始化与实例:initI18n 由 LanguageProvider 调用。
3
- * 默认合并 NFX-UI 自带的 theme / language / layout / preference 命名空间。
2
+ * Shared i18n init — host injects builtin bundles via `getBuiltinBundles`.
3
+ * Product JSON stays in each console's language folder.
4
4
  */
5
5
  import type { CreateI18nResourcesResult, ExtraBundleItem, InitI18nOptions } from "../types";
6
6
 
@@ -12,8 +12,6 @@ import { LanguageEnum, LANGUAGE_VALUES } from "nfx-ui/enums/language";
12
12
  import { PreferenceStore, setPreference } from "nfx-ui/stores/preference";
13
13
  import { safeEnum, safeStringable } from "nfx-ui/utils/safe";
14
14
 
15
- import { getDefaultNfxBundles } from "../resources";
16
-
17
15
  export function toSupportedLanguage(lng: string, fallbackLng: LanguageEnum): LanguageEnum {
18
16
  const prefix = safeStringable(lng.split("-")[0]?.toLowerCase());
19
17
  return safeEnum<LanguageEnum>(prefix || undefined, LANGUAGE_VALUES, fallbackLng);
@@ -43,19 +41,7 @@ export function initI18n(options: InitI18nOptions): void {
43
41
  return;
44
42
  }
45
43
 
46
- const nfx = getDefaultNfxBundles();
47
- const user: CreateI18nResourcesResult = options.getBuiltinBundles();
48
- const RESOURCES = ([LanguageEnum.EN, LanguageEnum.ZH, LanguageEnum.FR] as const).reduce(
49
- (acc, lang) => ({
50
- ...acc,
51
- [lang]: {
52
- ...(nfx.RESOURCES[lang] ?? {}),
53
- ...(user.RESOURCES[lang] ?? {}),
54
- },
55
- }),
56
- {} as typeof user.RESOURCES,
57
- );
58
- const NAME_SPACES = [...user.NAME_SPACES, ...nfx.NAME_SPACES.filter((n) => !user.NAME_SPACES.includes(n))];
44
+ const { RESOURCES, NAME_SPACES }: CreateI18nResourcesResult = options.getBuiltinBundles();
59
45
  const initialLng = PreferenceStore.getState().language;
60
46
 
61
47
  const persistLanguage = (lng: string) => {
@@ -2,10 +2,9 @@ import type { TFunction } from "i18next";
2
2
 
3
3
  import { zodResolver } from "@hookform/resolvers/zod";
4
4
  import { useForm } from "react-hook-form";
5
- import { useTranslation } from "react-i18next";
6
5
  import { z } from "zod";
7
6
 
8
- /** 登录表单;校验文案来自 host `pages.Account.Login` → `form.validation.*` */
7
+ /** 登录表单;校验文案由 host 页面传入的 `t` 提供(`form.validation.*`)。 */
9
8
  export function createLoginSchema(t: TFunction) {
10
9
  return z.object({
11
10
  email: z.string().min(1, t("form.validation.emailRequired")).email(t("form.validation.emailInvalid")),
@@ -16,8 +15,7 @@ export function createLoginSchema(t: TFunction) {
16
15
 
17
16
  export type LoginFormData = z.infer<ReturnType<typeof createLoginSchema>>;
18
17
 
19
- export const useInitLoginForm = () => {
20
- const { t } = useTranslation("pages.Account.Login");
18
+ export const useInitLoginForm = (t: TFunction) => {
21
19
  const schema = createLoginSchema(t);
22
20
 
23
21
  return useForm<LoginFormData>({
@@ -41,8 +39,7 @@ export function createLoginWithPhoneSchema(t: TFunction) {
41
39
 
42
40
  export type LoginWithPhoneFormData = z.infer<ReturnType<typeof createLoginWithPhoneSchema>>;
43
41
 
44
- export const useInitLoginWithPhoneForm = () => {
45
- const { t } = useTranslation("pages.Account.Login");
42
+ export const useInitLoginWithPhoneForm = (t: TFunction) => {
46
43
  const schema = createLoginWithPhoneSchema(t);
47
44
 
48
45
  return useForm<LoginWithPhoneFormData>({