Skip to content

Commit 516685c

Browse files
committed
Fix react-native package lint issues
1 parent fa056e9 commit 516685c

27 files changed

Lines changed: 192 additions & 138 deletions

packages/react-native/src/components/formbricks.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1+
import React, { useCallback, useEffect, useSyncExternalStore } from "react";
2+
import { View } from "react-native";
13
import { SurveyWebView } from "@/components/survey-web-view";
24
import { Logger } from "@/lib/common/logger";
35
import { setup } from "@/lib/common/setup";
46
import { SurveyStore } from "@/lib/survey/store";
5-
import React, { useCallback, useEffect, useSyncExternalStore } from "react";
6-
import { View } from "react-native";
77

88
interface FormbricksProps {
99
appUrl: string;

packages/react-native/src/components/survey-web-view.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
/* eslint-disable no-console -- debugging*/
2+
import React, { type JSX, useEffect, useRef, useState } from "react";
3+
import { KeyboardAvoidingView, Modal, View, StyleSheet } from "react-native";
4+
import { WebView, type WebViewMessageEvent } from "react-native-webview";
25
import { RNConfig } from "@/lib/common/config";
36
import { Logger } from "@/lib/common/logger";
47
import { filterSurveys, getLanguageCode, getStyling } from "@/lib/common/utils";
58
import { SurveyStore } from "@/lib/survey/store";
69
import { type TUserState, ZJsRNWebViewOnMessageData } from "@/types/config";
710
import type { TSurvey, SurveyContainerProps } from "@/types/survey";
8-
import React, { type JSX, useEffect, useRef, useState } from "react";
9-
import { KeyboardAvoidingView, Modal, View, StyleSheet } from "react-native";
10-
import { WebView, type WebViewMessageEvent } from "react-native-webview";
1111

1212
const logger = Logger.getInstance();
1313
logger.configure({ logLevel: "debug" });
@@ -21,15 +21,15 @@ interface SurveyWebViewProps {
2121

2222
export function SurveyWebView(
2323
props: SurveyWebViewProps
24-
): JSX.Element | undefined {
24+
): JSX.Element | null {
2525
const webViewRef = useRef(null);
2626
const [isSurveyRunning, setIsSurveyRunning] = useState(false);
2727
const [showSurvey, setShowSurvey] = useState(false);
2828
const [appConfig, setAppConfig] = useState<RNConfig | null>(null);
2929
const [languageCode, setLanguageCode] = useState("default");
3030

3131
useEffect(() => {
32-
const fetchConfig = async () => {
32+
const fetchConfig = async (): Promise<void> => {
3333
const config = await RNConfig.getInstance();
3434
setAppConfig(config);
3535
};
@@ -87,7 +87,7 @@ export function SurveyWebView(
8787
}, [props.survey.delay, isSurveyRunning, props.survey.name]);
8888

8989
if (!appConfig) {
90-
return;
90+
return null;
9191
}
9292

9393
const project = appConfig.get().environment.data.project;

packages/react-native/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,4 @@ export const logout = async (): Promise<void> => {
3838
await queue.wait();
3939
};
4040

41-
export { Formbricks as default } from "@/components/formbricks";
41+
export { Formbricks } from "@/components/formbricks";

packages/react-native/src/lib/common/api.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { wrapThrowsAsync } from "@/lib/common/utils";
22
import {
3-
ApiResponse,
4-
ApiSuccessResponse,
5-
CreateOrUpdateUserResponse,
3+
type ApiResponse,
4+
type ApiSuccessResponse,
5+
type CreateOrUpdateUserResponse,
66
} from "@/types/api";
7-
import { TEnvironmentState } from "@/types/config";
8-
import { ApiErrorResponse, Result, err, ok } from "@/types/error";
7+
import { type TEnvironmentState } from "@/types/config";
8+
import { type ApiErrorResponse, type Result, err, ok } from "@/types/error";
99

1010
export const makeRequest = async <T>(
1111
appUrl: string,

packages/react-native/src/lib/common/config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export class RNConfig {
1111

1212
private config: TConfig | null = null;
1313

14+
// eslint-disable-next-line @typescript-eslint/no-empty-function -- singleton constructor
1415
private constructor() {}
1516

1617
public async init(): Promise<void> {
@@ -24,7 +25,7 @@ export class RNConfig {
2425
}
2526
}
2627

27-
static async getInstance(): Promise<RNConfig> {
28+
public static async getInstance(): Promise<RNConfig> {
2829
RNConfig.instance ??= new RNConfig();
2930
await RNConfig.instance.init();
3031
return RNConfig.instance;

packages/react-native/src/lib/common/event-listeners.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import { addUserStateExpiryCheckListener, clearUserStateExpiryCheckListener } fr
77
let areRemoveEventListenersAdded = false;
88

99
export const addEventListeners = (): void => {
10-
addEnvironmentStateExpiryCheckListener();
11-
addUserStateExpiryCheckListener();
10+
void addEnvironmentStateExpiryCheckListener();
11+
void addUserStateExpiryCheckListener();
1212
};
1313

1414
export const addCleanupEventListeners = (): void => {

packages/react-native/src/lib/common/setup.ts

Lines changed: 89 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ export const migrateUserStateAddContactId = async (): Promise<{
5050
return { changed: false };
5151
}
5252

53-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- data could be undefined
53+
5454
if (
55-
!existingConfig.user?.data?.contactId &&
56-
existingConfig.user?.data?.userId
55+
!existingConfig.user?.data.contactId &&
56+
existingConfig.user?.data.userId
5757
) {
5858
return { changed: true };
5959
}
@@ -63,7 +63,9 @@ export const migrateUserStateAddContactId = async (): Promise<{
6363
};
6464

6565
// Helper: Handle missing field error
66-
function handleMissingField(field: string) {
66+
function handleMissingField(
67+
field: string
68+
): Result<void, MissingFieldError> {
6769
const logger = Logger.getInstance();
6870
logger.debug(`No ${field} provided`);
6971
return err({
@@ -91,21 +93,21 @@ async function syncEnvironmentStateIfExpired(
9193

9294
if (environmentStateResponse.ok) {
9395
return ok(environmentStateResponse.data);
94-
} else {
95-
logger.error(
96-
`Error fetching environment state: ${environmentStateResponse.error.code} - ${environmentStateResponse.error.responseMessage ?? ""}`
97-
);
98-
99-
return err({
100-
code: "network_error",
101-
message: "Error fetching environment state",
102-
status: 500,
103-
url: new URL(
104-
`${configInput.appUrl}/api/v1/client/${configInput.environmentId}/environment`
105-
),
106-
responseMessage: environmentStateResponse.error.message,
107-
});
10896
}
97+
98+
logger.error(
99+
`Error fetching environment state: ${environmentStateResponse.error.code} - ${environmentStateResponse.error.responseMessage ?? ""}`
100+
);
101+
102+
return err({
103+
code: "network_error",
104+
message: "Error fetching environment state",
105+
status: 500,
106+
url: new URL(
107+
`${configInput.appUrl}/api/v1/client/${configInput.environmentId}/environment`
108+
),
109+
responseMessage: environmentStateResponse.error.message,
110+
});
109111
}
110112

111113
// Helper: Sync user state if expired
@@ -125,7 +127,7 @@ async function syncUserStateIfExpired(
125127

126128
logger.debug("Person state expired. Syncing.");
127129

128-
if (userState?.data?.userId) {
130+
if (userState?.data.userId) {
129131
const updatesResponse = await sendUpdatesToBackend({
130132
appUrl: configInput.appUrl,
131133
environmentId: configInput.environmentId,
@@ -135,23 +137,23 @@ async function syncUserStateIfExpired(
135137
});
136138
if (updatesResponse.ok) {
137139
return ok(updatesResponse.data.state);
138-
} else {
139-
logger.error(
140-
`Error updating user state: ${updatesResponse.error.code} - ${updatesResponse.error.responseMessage ?? ""}`
141-
);
142-
return err({
143-
code: "network_error",
144-
message: "Error updating user state",
145-
status: 500,
146-
url: new URL(
147-
`${configInput.appUrl}/api/v1/client/${configInput.environmentId}/update/contacts/${userState.data.userId}`
148-
),
149-
responseMessage: "Unknown error",
150-
} as const);
151140
}
152-
} else {
153-
return ok(DEFAULT_USER_STATE_NO_USER_ID);
141+
142+
logger.error(
143+
`Error updating user state: ${updatesResponse.error.code} - ${updatesResponse.error.responseMessage ?? ""}`
144+
);
145+
return err({
146+
code: "network_error",
147+
message: "Error updating user state",
148+
status: 500,
149+
url: new URL(
150+
`${configInput.appUrl}/api/v1/client/${configInput.environmentId}/update/contacts/${userState.data.userId}`
151+
),
152+
responseMessage: "Unknown error",
153+
} as const);
154154
}
155+
156+
return ok(DEFAULT_USER_STATE_NO_USER_ID);
155157
}
156158

157159
// Helper: Update app config with synced states
@@ -199,22 +201,35 @@ const createNewConfigAndSync = async (
199201
appUrl: configInput.appUrl,
200202
environmentId: configInput.environmentId,
201203
});
202-
if (!environmentStateResponse.ok) {
203-
throw environmentStateResponse.error;
204+
205+
if (environmentStateResponse.ok) {
206+
const personState = DEFAULT_USER_STATE_NO_USER_ID;
207+
const environmentState = environmentStateResponse.data;
208+
const filteredSurveys = filterSurveys(environmentState, personState);
209+
appConfig.update({
210+
appUrl: configInput.appUrl,
211+
environmentId: configInput.environmentId,
212+
user: personState,
213+
environment: environmentState,
214+
filteredSurveys,
215+
});
216+
return;
204217
}
205-
const personState = DEFAULT_USER_STATE_NO_USER_ID;
206-
const environmentState = environmentStateResponse.data;
207-
const filteredSurveys = filterSurveys(environmentState, personState);
208-
appConfig.update({
209-
appUrl: configInput.appUrl,
210-
environmentId: configInput.environmentId,
211-
user: personState,
212-
environment: environmentState,
213-
filteredSurveys,
218+
219+
await handleErrorOnFirstSetup({
220+
code: environmentStateResponse.error.code,
221+
responseMessage:
222+
environmentStateResponse.error.responseMessage ??
223+
environmentStateResponse.error.message,
214224
});
215-
} catch (e) {
225+
} catch (e: unknown) {
226+
const setupError = normalizeSetupError(e);
216227
await handleErrorOnFirstSetup(
217-
e as { code: string; responseMessage: string }
228+
{
229+
code: setupError.code ?? "network_error",
230+
responseMessage:
231+
setupError.responseMessage ?? setupError.message ?? "Unknown error",
232+
}
218233
);
219234
}
220235
};
@@ -260,10 +275,10 @@ const finalizeSetup = (): void => {
260275
};
261276

262277
// Helper: Load existing config
263-
const loadExistingConfig = async (
278+
const loadExistingConfig = (
264279
appConfig: RNConfig,
265280
logger: ReturnType<typeof Logger.getInstance>
266-
): Promise<TConfig | undefined> => {
281+
): TConfig | undefined => {
267282
let existingConfig: TConfig | undefined;
268283
try {
269284
existingConfig = appConfig.get();
@@ -294,7 +309,7 @@ export const setup = async (
294309
return okVoid();
295310
}
296311

297-
const existingConfig = await loadExistingConfig(appConfig, logger);
312+
const existingConfig = loadExistingConfig(appConfig, logger);
298313
if (shouldReturnEarlyForErrorState(existingConfig, logger)) {
299314
return okVoid();
300315
}
@@ -369,8 +384,6 @@ export const checkSetup = (): Result<void, NotSetupError> => {
369384

370385
return okVoid();
371386
};
372-
373-
// eslint-disable-next-line @typescript-eslint/require-await -- disabled for now
374387
export const tearDown = async (): Promise<void> => {
375388
const logger = Logger.getInstance();
376389
const appConfig = await RNConfig.getInstance();
@@ -425,3 +438,27 @@ export const handleErrorOnFirstSetup = async (e: {
425438

426439
throw new Error("Could not set up formbricks");
427440
};
441+
442+
const normalizeSetupError = (
443+
error: unknown
444+
): Partial<{
445+
code: string;
446+
responseMessage: string;
447+
message: string;
448+
}> => {
449+
if (typeof error !== "object" || error === null) {
450+
return {};
451+
}
452+
453+
const candidate = error as Record<string, unknown>;
454+
455+
return {
456+
code: typeof candidate.code === "string" ? candidate.code : undefined,
457+
responseMessage:
458+
typeof candidate.responseMessage === "string"
459+
? candidate.responseMessage
460+
: undefined,
461+
message:
462+
typeof candidate.message === "string" ? candidate.message : undefined,
463+
};
464+
};
Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1-
import AsyncStorageModule from "@react-native-async-storage/async-storage";
1+
import AsyncStorageModule, {
2+
type AsyncStorageStatic,
3+
} from "@react-native-async-storage/async-storage";
24

3-
const AsyncStorage =
4-
// @ts-expect-error: Some bundlers put the module on .default
5-
AsyncStorageModule.default ?? AsyncStorageModule;
5+
type AsyncStorageModuleWithDefault = AsyncStorageStatic & {
6+
default?: AsyncStorageStatic;
7+
};
8+
9+
const asyncStorageModule = AsyncStorageModule as AsyncStorageModuleWithDefault;
10+
11+
const AsyncStorage: AsyncStorageStatic =
12+
asyncStorageModule.default ?? asyncStorageModule;
613

714
export { AsyncStorage };

packages/react-native/src/lib/common/tests/api.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// api.test.ts
2+
import { beforeEach, describe, expect, test, vi } from "vitest";
23
import { ApiClient, makeRequest } from "@/lib/common/api";
34
import type { TEnvironmentState } from "@/types/config";
4-
import { beforeEach, describe, expect, test, vi } from "vitest";
55

66
// Mock fetch
77
const mockFetch = vi.fn();

packages/react-native/src/lib/common/tests/config.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// config.test.ts
22
import AsyncStorage from "@react-native-async-storage/async-storage";
33
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
4-
import { mockConfig } from "./__mocks__/config.mock";
54
import { RNConfig, RN_ASYNC_STORAGE_KEY } from "@/lib/common/config";
65
import type { TConfig, TConfigUpdateInput } from "@/types/config";
6+
import { mockConfig } from "./__mocks__/config.mock";
77

88
// Define mocks outside of any describe block
99

0 commit comments

Comments
 (0)