Skip to content

Commit

Permalink
chore: fix types
Browse files Browse the repository at this point in the history
  • Loading branch information
technoplato committed Oct 22, 2024
1 parent daf32c2 commit 46ae8ca
Show file tree
Hide file tree
Showing 15 changed files with 269 additions and 82 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ builds

# iOS
ios/Converse.xcworkspace/xcshareddata/swiftpm/Package.resolved

/.idea
.aider*

# Reassure output directory
.reassure
2 changes: 1 addition & 1 deletion data/db/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export const initDb = async (account: string): Promise<void> => {
await waitUntilAppActive(1500);
const migrationsResult = await dataSource.runMigrations();
logger.debug(`Migrations done for ${account}`);
console.log(migrationsResult);
logger.debug(migrationsResult);
repositories[account] = {
conversation: dataSource.getRepository(Conversation),
message: dataSource.getRepository(Message),
Expand Down
2 changes: 1 addition & 1 deletion data/helpers/messages/handleGroupUpdatedMessage.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { invalidateGroupMembersQuery } from "@queries/useGroupMembersQuery";
import { invalidateGroupNameQuery } from "@queries/useGroupNameQuery";
import { invalidateGroupPhotoQuery } from "@queries/useGroupPhotoQuery";
import { DecodedMessageWithCodecsType } from "@utils/xmtpRN/client.types";

import { handleGroupUpdatedMessage } from "./handleGroupUpdatedMessage";
import { DecodedMessageWithCodecsType } from "../../../utils/xmtpRN/client";

jest.mock("@queries/useGroupMembersQuery", () => ({
invalidateGroupMembersQuery: jest.fn(),
Expand Down
2 changes: 1 addition & 1 deletion data/helpers/messages/handleGroupUpdatedMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { invalidateGroupIsActiveQuery } from "@queries/useGroupIsActive";
import { invalidateGroupMembersQuery } from "@queries/useGroupMembersQuery";
import { invalidateGroupNameQuery } from "@queries/useGroupNameQuery";
import { invalidateGroupPhotoQuery } from "@queries/useGroupPhotoQuery";
import { DecodedMessageWithCodecsType } from "@utils/xmtpRN/client";
import { DecodedMessageWithCodecsType } from "@utils/xmtpRN/client.types";
import { refreshGroup } from "@utils/xmtpRN/conversations";
import { GroupUpdatedContent } from "@xmtp/react-native-sdk";

Expand Down
3 changes: 1 addition & 2 deletions utils/events.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { MessageToDisplay } from "@components/Chat/Message/Message";
import { MediaPreview } from "@data/store/chatStore";
import { GroupWithCodecsType } from "@utils/xmtpRN/client.types";
import EventEmitter from "eventemitter3";
import { Account, Wallet } from "thirdweb/wallets";

import { GroupWithCodecsType } from "./xmtpRN/client";

type ShowActionSheetEvent<T extends string> = `showActionSheetForTxRef-${T}`;
type OpenAttachmentMessage<T extends string> = `openAttachmentForMessage-${T}`;
type AttachmentMessageProcessed<T extends string> =
Expand Down
2 changes: 1 addition & 1 deletion utils/evm/xmtp.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCurrentAccount } from "@data/store/accountsStore";
import { translate } from "@i18n";
import { ConverseXmtpClientType } from "@utils/xmtpRN/client";
import { ConverseXmtpClientType } from "@utils/xmtpRN/client.types";
import { getXmtpClient } from "@utils/xmtpRN/sync";
import { useCallback } from "react";
import { Alert } from "react-native";
Expand Down
3 changes: 2 additions & 1 deletion utils/logout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { deleteLibXmtpDatabaseForInboxId } from "@utils/fileSystem";
import logger from "@utils/logger";
import { ConverseXmtpClientType, dropXmtpClient } from "@utils/xmtpRN/client";
import { dropXmtpClient } from "@utils/xmtpRN/client";
import { ConverseXmtpClientType } from "@utils/xmtpRN/client.types";
import { getInboxId } from "@utils/xmtpRN/signIn";
import { useCallback } from "react";

Expand Down
32 changes: 16 additions & 16 deletions utils/notifications.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,20 @@
import {
saveConversations,
saveConversationsLastNotificationSubscribePeriod,
} from "@data/helpers/conversations/upsertConversations";
import { saveMessages } from "@data/helpers/messages";
import {
currentAccount,
getAccountsList,
getChatStore,
getProfilesStore,
getSettingsStore,
useAccountsStore,
} from "@data/store/accountsStore";
import { useAppStore } from "@data/store/appStore";
import { XmtpMessage } from "@data/store/chatStore";
import { createHash } from "@mfellner/react-native-fast-create-hash";
import { ConverseXmtpClientType } from "@utils/xmtpRN/client.types";
import { keystore } from "@xmtp/proto";
import { buildUserInviteTopic } from "@xmtp/xmtp-js";
import * as Notifications from "expo-notifications";
Expand Down Expand Up @@ -29,24 +45,8 @@ import {
loadSavedNotificationsConversations,
loadSavedNotificationsMessages,
} from "./sharedData";
import { ConverseXmtpClientType } from "./xmtpRN/client";
import { loadConversationsHmacKeys } from "./xmtpRN/conversations";
import { getXmtpClient } from "./xmtpRN/sync";
import {
saveConversations,
saveConversationsLastNotificationSubscribePeriod,
} from "../data/helpers/conversations/upsertConversations";
import { saveMessages } from "../data/helpers/messages";
import {
currentAccount,
getAccountsList,
getChatStore,
getProfilesStore,
getSettingsStore,
useAccountsStore,
} from "../data/store/accountsStore";
import { useAppStore } from "../data/store/appStore";
import { XmtpMessage } from "../data/store/chatStore";

let nativePushToken: string | null;

Expand Down
203 changes: 203 additions & 0 deletions utils/result.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
/**
* A generic Result class that represents either a success value or an error.
* This class is useful for handling operations that can fail, providing a type-safe
* way to represent and handle both successful and failed outcomes.
*
* @typeparam T The type of the success value.
*/
// TODO: add an idea of asyncResult where
// we have "unasked", error, success to more accurately
// represent that data isnt "null", we just
// haven't tried to get it yet
export class Result<T> {
private constructor(
private readonly value: T | null,
private readonly error: Error | null
) {}

/**
* Creates a successful Result containing the given value.
*
* @param value The value to be wrapped in a successful Result.
* @returns A new Result instance representing a success.
*
* @example
* const successResult = Result.success(42);
* console.log(successResult.isSuccess()); // true
* console.log(successResult.getValue()); // 42
*/
static success<U>(value: U): Result<U> {
return new Result(value, null);
}

/**
* Creates a failed Result containing the given error.
*
* @param error The error to be wrapped in a failed Result.
* @returns A new Result instance representing a failure.
*
* @example
* const failureResult = Result.failure(new Error("Something went wrong"));
* console.log(failureResult.isFailure()); // true
* console.log(failureResult.getError().message); // "Something went wrong"
*/
static failure<U>(error: Error): Result<U> {
return new Result<U>(null, error);
}

/**
* Checks if the Result is successful.
*
* @returns true if the Result represents a success, false otherwise.
*
* @example
* const result = Result.success("Hello");
* console.log(result.isSuccess()); // true
*/
isSuccess(): boolean {
return this.error === null;
}

/**
* Checks if the Result is a failure.
*
* @returns true if the Result represents a failure, false otherwise.
*
* @example
* const result = Result.failure(new Error("Oops"));
* console.log(result.isFailure()); // true
*/
isFailure(): boolean {
return this.error !== null;
}

/**
* Gets the success value if the Result is successful.
*
* @returns The success value.
* @throws Error if the Result represents a failure.
*
* @example
* const result = Result.success("Hello, World!");
* if (result.isSuccess()) {
* console.log(result.getValue()); // "Hello, World!"
* }
*/
getValue(): T {
if (this.isFailure()) {
throw new Error("Cannot get value from a failed Result");
}
return this.value as T;
}

/**
* Gets the error if the Result is a failure.
*
* @returns The error.
* @throws Error if the Result represents a success.
*
* @example
* const result = Result.failure(new Error("File not found"));
* if (result.isFailure()) {
* console.log(result.getError().message); // "File not found"
* }
*/
getError(): Error {
if (this.isSuccess()) {
throw new Error("Cannot get error from a successful Result");
}
return this.error as Error;
}

/**
* Applies a function to the contained value if the Result is a success.
* If the Result is a failure, it returns a new Result with the same error.
* Use map when your transformation always succeeds and returns a regular value.
*
* @param fn The function to apply to the success value.
* @returns A new Result containing the result of the function application, or the original error.
*
* @example
* // Success case
* const successResult = Result.success(5);
* const doubled = successResult.map(x => x * 2);
* console.log(doubled.getValue()); // 10
*
* // Failure case
* const failureResult = Result.failure<number>(new Error("Invalid input"));
* const shouldNotDouble = failureResult.map(x => x * 2);
* console.log(shouldNotDouble.isFailure()); // true
* console.log(shouldNotDouble.getError().message); // "Invalid input"
*/
map<U>(fn: (value: T) => U): Result<U> {
if (this.isSuccess()) {
return Result.success(fn(this.getValue()));
}
return Result.failure(this.getError());
}

/**
* Applies a function that returns a Result to the contained value if this Result is a success.
* If this Result is a failure, it returns a new Result with the same error.
* Use flatMap when your transformation might fail or when it returns another Result.
*
* The key difference between map and flatMap:
* - map is for simple transformations that always succeed.
* - flatMap is for operations that might fail or that return a Result themselves.
*
* @param fn The function to apply to the success value.
* @returns The Result returned by the function, or this Result if it's a failure.
*
* @example
* function divide(a: number, b: number): Result<number> {
* if (b === 0) return Result.failure(new Error("Division by zero"));
* return Result.success(a / b);
* }
*
* // Success case
* const successResult = Result.success(10)
* .flatMap(x => divide(x, 2))
* .flatMap(x => divide(x, 2));
* console.log(successResult.getValue()); // 2.5
*
* // Failure case: division by zero
* const failureResult = Result.success(10)
* .flatMap(x => divide(x, 2))
* .flatMap(x => divide(x, 0));
* console.log(failureResult.isFailure()); // true
* console.log(failureResult.getError().message); // "Division by zero"
*
* // Failure case: starting with a failure
* const initialFailure = Result.failure<number>(new Error("Initial error"))
* .flatMap(x => divide(x, 2));
* console.log(initialFailure.isFailure()); // true
* console.log(initialFailure.getError().message); // "Initial error"
*
* // Example demonstrating the need for flatMap:
* function getUser(id: number): Result<User> {
* // Imagine this fetches a user from a database
* // It might fail, so it returns a Result
* }
*
* function getUserEmail(user: User): Result<string> {
* // This might fail if the user doesn't have an email
* // So it also returns a Result
* }
*
* // Using flatMap to chain operations that return Results
* const userEmailResult = getUser(123)
* .flatMap(user => getUserEmail(user));
*
* // If we tried to use map, it wouldn't work correctly:
* const incorrectResult = getUser(123)
* .map(user => getUserEmail(user)); // This would be Result<Result<string>>, not what we want!
*
* // flatMap "flattens" the nested Result, giving us a Result<string> as desired.
*/
flatMap<U>(fn: (value: T) => Result<U>): Result<U> {
if (this.isSuccess()) {
return fn(this.getValue());
}
return Result.failure(this.getError());
}
}
4 changes: 2 additions & 2 deletions utils/xmtpRN/attachments.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { XmtpMessage } from "@data/store/chatStore";
import { ConverseXmtpClientType } from "@utils/xmtpRN/client.types";
import {
DecryptedLocalAttachment,
RemoteAttachmentContent,
} from "@xmtp/react-native-sdk";
import RNFS from "react-native-fs";

import { ConverseXmtpClientType } from "./client";
import { getXmtpClient } from "./sync";
import { XmtpMessage } from "../../data/store/chatStore";

export const encryptRemoteAttachment = async (
account: string,
Expand Down
21 changes: 2 additions & 19 deletions utils/xmtpRN/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { awaitableAlert } from "@utils/alert";
import { getDbEncryptionKey } from "@utils/keychain/helpers";
import logger from "@utils/logger";
import { useLogoutFromConverse } from "@utils/logout";
import { XmtpClientByAccount } from "@utils/xmtpRN/client.types";
import { TransactionReferenceCodec } from "@xmtp/content-type-transaction-reference";
import {
Client,
Expand Down Expand Up @@ -48,30 +49,12 @@ export const getXmtpClientFromBase64Key = async (base64Key: string) => {
});
};

export type ConverseXmtpClientType = Awaited<
ReturnType<typeof getXmtpClientFromBase64Key>
>;

export type ConversationWithCodecsType = Awaited<
ReturnType<ConverseXmtpClientType["conversations"]["newConversation"]>
>;

export type GroupWithCodecsType = Awaited<
ReturnType<ConverseXmtpClientType["conversations"]["newGroup"]>
>;

export type DecodedMessageWithCodecsType = Awaited<
ReturnType<ConversationWithCodecsType["messages"]>
>[number];

export const isOnXmtp = async (address: string) =>
Client.canMessage(getCleanAddress(address), {
env,
});

export const xmtpClientByAccount: {
[account: string]: ConverseXmtpClientType;
} = {};
export const xmtpClientByAccount: XmtpClientByAccount = {};

// On iOS, it's important to stop writing to SQLite database
// when the app is going from BACKGROUNDED to SUSPENDED
Expand Down
Loading

0 comments on commit 46ae8ca

Please sign in to comment.