A personal media tracker built on the AT Protocol
opnshelf.xyz
1const SAFE_ONBOARDING_MESSAGES = new Set([
2 "This watch was already imported.",
3 "We couldn't fetch details for this title right now.",
4 "We couldn't save this watch right now. Please try again.",
5 "We couldn't import this item.",
6 "Trakt user not found",
7 "Trakt profile is private or unavailable. Try CSV import instead.",
8 "Trakt rate limit reached. We will retry in the background shortly.",
9 "Trakt is temporarily unavailable. Please retry later or use CSV import.",
10]);
11
12const MAX_VISIBLE_IMPORT_ERRORS = 10;
13
14export function getSafeOnboardingErrorMessage(
15 error: unknown,
16 fallback: string,
17): string {
18 const message = getNestedStringMessage(error);
19 if (message && SAFE_ONBOARDING_MESSAGES.has(message)) {
20 return message;
21 }
22
23 return fallback;
24}
25
26export function getSafeImportErrorMessage(message: string): string {
27 return SAFE_ONBOARDING_MESSAGES.has(message)
28 ? message
29 : "We couldn't import this item.";
30}
31
32export function buildImportErrorList(
33 localMessages: string[],
34 serverMessages: string[],
35): string[] {
36 const uniqueMessages = Array.from(
37 new Set([
38 ...localMessages,
39 ...serverMessages.map((message) => getSafeImportErrorMessage(message)),
40 ]),
41 );
42
43 if (uniqueMessages.length <= MAX_VISIBLE_IMPORT_ERRORS) {
44 return uniqueMessages;
45 }
46
47 const remainingCount = uniqueMessages.length - MAX_VISIBLE_IMPORT_ERRORS;
48 return [
49 ...uniqueMessages.slice(0, MAX_VISIBLE_IMPORT_ERRORS),
50 `...and ${remainingCount} more`,
51 ];
52}
53
54function getNestedStringMessage(error: unknown): string | null {
55 if (typeof error === "string" && error.trim() !== "") {
56 return error;
57 }
58
59 if (!error || typeof error !== "object") {
60 return null;
61 }
62
63 if ("message" in error) {
64 const { message } = error as { message?: unknown };
65 if (typeof message === "string" && message.trim() !== "") {
66 return message;
67 }
68 }
69
70 if ("body" in error) {
71 const nested = getNestedStringMessage((error as { body?: unknown }).body);
72 if (nested) {
73 return nested;
74 }
75 }
76
77 if ("error" in error) {
78 const nested = getNestedStringMessage((error as { error?: unknown }).error);
79 if (nested) {
80 return nested;
81 }
82 }
83
84 return null;
85}