Bluesky app fork with some witchin' additions 馃挮
witchsky.app
bluesky
fork
client
1import {XRPCError} from '@atproto/api'
2import {t} from '@lingui/core/macro'
3
4export function cleanError(str: any): string {
5 if (!str) {
6 return ''
7 }
8 if (typeof str !== 'string') {
9 str = str.toString()
10 }
11 if (isNetworkError(str)) {
12 return t`Unable to connect. Please check your internet connection and try again.`
13 }
14 if (
15 str.includes('Upstream Failure') ||
16 str.includes('NotEnoughResources') ||
17 str.includes('pipethrough network error')
18 ) {
19 return t`The server appears to be experiencing issues. Please try again in a few moments.`
20 }
21 /**
22 * @see https://github.com/bluesky-social/atproto/blob/255cfcebb54332a7129af768a93004e22c6858e3/packages/pds/src/actor-store/preference/transactor.ts#L24
23 */
24 if (
25 str.includes('Do not have authorization to set preferences') &&
26 str.includes('app.bsky.actor.defs#personalDetailsPref')
27 ) {
28 return t`You cannot update your birthdate while using an app password. Please sign in with your main password to update your birthdate.`
29 }
30 if (str.includes('Bad token scope') || str.includes('Bad token method')) {
31 return t`This feature is not available while using an App Password. Please sign in with your main password.`
32 }
33 if (str.includes('OAuth credentials are not supported')) {
34 return t`This feature is not available when signed in with OAuth. Please manage your account through your hosting provider's website.`
35 }
36 if (
37 str.includes('ScopeMissingError') ||
38 str.includes('Missing required scope')
39 ) {
40 return t`This feature is not available with your current session. Please manage your account through your hosting provider's website, or sign out and sign back in to refresh your permissions.`
41 }
42 if (str.includes('Account has been suspended')) {
43 return t`Account has been suspended`
44 }
45 if (str.includes('Account is deactivated')) {
46 return t`Account is deactivated`
47 }
48 if (str.includes('Profile not found')) {
49 return t`Profile not found`
50 }
51 if (str.includes('Unable to resolve handle')) {
52 return t`Unable to resolve handle`
53 }
54 if (str.startsWith('Error: ')) {
55 return str.slice('Error: '.length)
56 }
57 return str
58}
59
60const NETWORK_ERRORS = [
61 'Abort',
62 'Network request failed',
63 'Failed to fetch',
64 'Load failed',
65 'Upstream service unreachable',
66 'NetworkError when attempting to fetch resource',
67]
68
69export function isNetworkError(e: unknown) {
70 const str = String(e)
71 for (const err of NETWORK_ERRORS) {
72 if (str.includes(err)) {
73 return true
74 }
75 }
76 return false
77}
78
79export function isErrorMaybeAppPasswordPermissions(e: unknown) {
80 if (e instanceof XRPCError && e.error === 'TokenInvalid') {
81 return true
82 }
83 const str = String(e)
84 return str.includes('Bad token scope') || str.includes('Bad token method')
85}
86
87/**
88 * Intended to capture "User cancelled" or "Crop cancelled" errors
89 * that we often get from expo modules such @bsky.app/expo-image-crop-tool
90 *
91 * The exact name has changed in the past so let's just see if the string
92 * contains "cancel"
93 */
94export function isCancelledError(e: unknown) {
95 const str = String(e).toLowerCase()
96 return str.includes('cancel')
97}