Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TS migration] Migrate 'Report.js' lib to TypeScript #28730

Merged
merged 40 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
56de552
[TS migration] Migrate 'Report.js' lib to TypeScript
blazejkustra Oct 3, 2023
1e8b624
Merge branch 'main' into ts-migration/Report
blazejkustra Oct 6, 2023
1269110
Migrate first 1000 lines of Report.ts
blazejkustra Oct 6, 2023
dcb222d
Finish migrating Report.ts
blazejkustra Oct 9, 2023
3fca4b7
Merge branch 'main' into ts-migration/Report
blazejkustra Nov 28, 2023
4fea06b
Adjust imports, comments up to line 100
blazejkustra Nov 28, 2023
d686dcc
Update pusher types
blazejkustra Nov 28, 2023
321f78d
Migrate report actions, up to line 400
blazejkustra Nov 28, 2023
f8a3e31
Migrate openReport
blazejkustra Nov 28, 2023
aa93c7e
Fix type problems with NotificationPreference
blazejkustra Nov 28, 2023
475bbb8
Migrate API actions
blazejkustra Nov 28, 2023
a66d6f7
Migrate next portion of Report utils
blazejkustra Nov 28, 2023
11c2b2f
Start migrating emoji reactions
blazejkustra Nov 28, 2023
55a955f
Remove unused imports
blazejkustra Nov 28, 2023
5ea6bd7
Migrate the rest of Report.js
blazejkustra Nov 28, 2023
66fc2df
Fix a typo after refactoring
blazejkustra Nov 28, 2023
057c3c1
Fix imports
blazejkustra Nov 28, 2023
e41d524
Fix tests after migration
blazejkustra Nov 28, 2023
0fca53f
Adjust the code after internal review
blazejkustra Nov 29, 2023
ff75cb8
Merge branch 'main' into ts-migration/Report
blazejkustra Nov 29, 2023
e731ade
Fix typecheck after merging main
blazejkustra Nov 29, 2023
55ca6d1
Merge branch 'main' into ts-migration/Report
blazejkustra Nov 30, 2023
c8efcd0
Merge branch 'main' into ts-migration/Report
blazejkustra Nov 30, 2023
1fc67db
Merge branch 'main' into ts-migration/Report
blazejkustra Dec 1, 2023
ee66d75
Adjust the code after cross review
blazejkustra Dec 1, 2023
111e655
Add explanations to onyx properties
blazejkustra Dec 1, 2023
f7cbe22
Merge branch 'main' into ts-migration/Report
blazejkustra Dec 4, 2023
d311cc0
Adjust onyx keys
blazejkustra Dec 4, 2023
b24460d
Fix emoji types for report.ts
blazejkustra Dec 4, 2023
ae6ec3d
Fix types for linkMetadata and originalMessage
blazejkustra Dec 4, 2023
fd8965b
Add PersonalDetailsList type, add default values in Report.ts
blazejkustra Dec 5, 2023
6eea7f9
Merge branch 'main' into ts-migration/Report
blazejkustra Dec 5, 2023
74c9482
Fix type errors around LocalNotification
blazejkustra Dec 5, 2023
7f6c168
Merge branch 'main' into ts-migration/Report
blazejkustra Dec 7, 2023
41471e9
Clear the logic in leaveRoom util
blazejkustra Dec 7, 2023
2273962
Merge branch 'main' into ts-migration/Report
blazejkustra Dec 8, 2023
fb517ac
Merge branch 'main' into ts-migration/Report
blazejkustra Dec 8, 2023
089dca8
Fix typecheck
blazejkustra Dec 8, 2023
5e32cc6
Merge branch 'main' into ts-migration/Report
blazejkustra Dec 11, 2023
33cb7fc
Fix typecheck
blazejkustra Dec 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -442,12 +442,13 @@ type OnyxValues = {
[ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT]: string;
[ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT_NUMBER_OF_LINES]: number;
[ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE]: boolean;
[ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING]: boolean;
[ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING]: Record<string, boolean>;
blazejkustra marked this conversation as resolved.
Show resolved Hide resolved
[ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM]: boolean;
[ONYXKEYS.COLLECTION.SECURITY_GROUP]: OnyxTypes.SecurityGroup;
[ONYXKEYS.COLLECTION.TRANSACTION]: OnyxTypes.Transaction;
[ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS]: OnyxTypes.RecentlyUsedTags;
[ONYXKEYS.COLLECTION.SELECTED_TAB]: string;
[ONYXKEYS.COLLECTION.PRIVATE_NOTES_DRAFT]: string;

// Forms
[ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM]: OnyxTypes.AddDebitCardForm;
Expand Down
36 changes: 10 additions & 26 deletions src/libs/EmojiUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,13 @@ import {Emoji, HeaderEmoji, PickerEmojis} from '@assets/emojis/types';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import {FrequentlyUsedEmoji} from '@src/types/onyx';
import {ReportActionReaction, UserReactions} from '@src/types/onyx/ReportActionReactions';
import emojisTrie from './EmojiTrie';

type HeaderIndice = {code: string; index: number; icon: React.FC<SvgProps>};
type EmojiSpacer = {code: string; spacer: boolean};
type EmojiPickerList = Array<EmojiSpacer | Emoji | HeaderEmoji>;
type ReplacedEmoji = {text: string; emojis: Emoji[]; cursorPosition?: number};
type UserReactions = {
id: string;
skinTones: Record<number, string>;
};
type UserReactionsWithTimestamps = UserReactions & {
oldestTimestamp: string;
};
type UsersReactionsList = {
createdAt: string;
users: Record<string, UserReactions>;
};
type TimestampedUsersReactions = Record<string, UserReactionsWithTimestamps>;
type EnrichedUserReactions = {
createdAt: string;
oldestTimestamp: string;
users: TimestampedUsersReactions;
};

let frequentlyUsedEmojis: FrequentlyUsedEmoji[] = [];
Onyx.connect({
Expand Down Expand Up @@ -436,9 +420,9 @@ function suggestEmojis(text: string, lang: keyof typeof emojisTrie, limit = CONS
/**
* Retrieve preferredSkinTone as Number to prevent legacy 'default' String value
*/
const getPreferredSkinToneIndex = (val: string | number): number | string => {
const getPreferredSkinToneIndex = (val: string | number | null): number => {
blazejkustra marked this conversation as resolved.
Show resolved Hide resolved
if (val !== null && Number.isInteger(Number(val))) {
return val;
return Number(val);
blazejkustra marked this conversation as resolved.
Show resolved Hide resolved
}

return CONST.EMOJI_DEFAULT_SKIN_TONE;
Expand Down Expand Up @@ -467,7 +451,7 @@ const getPreferredEmojiCode = (emoji: Emoji, preferredSkinTone: number): string
* array of emoji codes, that represents all used variations of the
* emoji, sorted by the reaction timestamp.
*/
const getUniqueEmojiCodes = (emojiAsset: Emoji, users: TimestampedUsersReactions): string[] => {
const getUniqueEmojiCodes = (emojiAsset: Emoji, users: Record<number, UserReactions>): string[] => {
blazejkustra marked this conversation as resolved.
Show resolved Hide resolved
const emojiCodes: Record<string, string> = Object.values(users ?? {}).reduce((result: Record<string, string>, userSkinTones) => {
Object.keys(userSkinTones?.skinTones ?? {}).forEach((skinTone) => {
const createdAt = userSkinTones.skinTones[Number(skinTone)];
Expand All @@ -486,20 +470,20 @@ const getUniqueEmojiCodes = (emojiAsset: Emoji, users: TimestampedUsersReactions
/**
* Given an emoji reaction object and its name, it populates it with the oldest reaction timestamps.
*/
const enrichEmojiReactionWithTimestamps = (emoji: UsersReactionsList, emojiName: string): EnrichedUserReactions => {
const enrichEmojiReactionWithTimestamps = (emoji: ReportActionReaction, emojiName: string): ReportActionReaction => {
let oldestEmojiTimestamp: string | null = null;

const usersWithTimestamps: Record<string, UserReactionsWithTimestamps> = {};
const usersWithTimestamps: Record<number, UserReactions> = {};
Object.keys(emoji.users ?? {}).forEach((id) => {
const user = emoji?.users?.[id];
const user = emoji?.users?.[Number(id)];
blazejkustra marked this conversation as resolved.
Show resolved Hide resolved
const userTimestamps = Object.values(user?.skinTones ?? {});
const oldestUserTimestamp = userTimestamps.reduce((min, curr) => (curr < min ? curr : min), userTimestamps[0]);

if (!oldestEmojiTimestamp || oldestUserTimestamp < oldestEmojiTimestamp) {
oldestEmojiTimestamp = oldestUserTimestamp;
}

usersWithTimestamps[id] = {
usersWithTimestamps[Number(id)] = {
...user,
id,
oldestTimestamp: oldestUserTimestamp,
Expand All @@ -521,7 +505,7 @@ const enrichEmojiReactionWithTimestamps = (emoji: UsersReactionsList, emojiName:
* Uses the NEW FORMAT for "emojiReactions"
* @param usersReactions - all the users reactions
*/
function hasAccountIDEmojiReacted(accountID: string, usersReactions: TimestampedUsersReactions, skinTone?: number) {
function hasAccountIDEmojiReacted(accountID: number, usersReactions: Record<number, UserReactions>, skinTone?: number) {
if (skinTone === undefined) {
return Boolean(usersReactions[accountID]);
}
Expand All @@ -535,7 +519,7 @@ function hasAccountIDEmojiReacted(accountID: string, usersReactions: Timestamped
/**
* Given an emoji reaction and current user's account ID, it returns the reusable details of the emoji reaction.
*/
const getEmojiReactionDetails = (emojiName: string, reaction: UsersReactionsList, currentUserAccountID: string) => {
const getEmojiReactionDetails = (emojiName: string, reaction: ReportActionReaction, currentUserAccountID: number) => {
const {users, oldestTimestamp} = enrichEmojiReactionWithTimestamps(reaction, emojiName);

const emoji = findEmojiByName(emojiName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import BrowserNotifications from './BrowserNotifications';

/**
* @param {Object} options
* @param {Object} options.report
* @param {Object|null|undefined} options.report
* @param {Object} options.reportAction
* @param {Function} options.onClick
*/
Expand All @@ -16,7 +16,7 @@ function showUpdateAvailableNotification() {

/**
* @param {Object} options
* @param {Object} options.report
* @param {Object|null|undefined} options.report
* @param {Object} options.reportAction
* @param {Function} options.onClick
*/
Expand Down
2 changes: 1 addition & 1 deletion src/libs/PersonalDetailsUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ function getAccountIDsByLogins(logins) {
* Given a list of accountIDs, find the associated personal detail and return related logins.
*
* @param {Array<number>} accountIDs Array of user accountIDs
* @returns {Array} - Array of logins according to passed accountIDs
* @returns {Array<string>} - Array of logins according to passed accountIDs
*/
function getLoginsByAccountIDs(accountIDs) {
return _.reduce(
Expand Down
32 changes: 25 additions & 7 deletions src/libs/Pusher/pusher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,25 @@ type Args = {

type PushJSON = OnyxUpdateEvent[] | OnyxUpdatesFromServer;

type UserIsTypingEvent = Record<string, boolean> & {
userLogin?: string;
};

type UserIsLeavingRoomEvent = Record<string, boolean> & {
userLogin?: string;
};

type PusherEventMap = {
[TYPE.USER_IS_TYPING]: UserIsTypingEvent;
[TYPE.USER_IS_LEAVING_ROOM]: UserIsLeavingRoomEvent;
};

type EventData<EventName extends string> = EventName extends keyof PusherEventMap ? PusherEventMap[EventName] : PushJSON;

type EventCallbackError = {type: ValueOf<typeof CONST.ERROR>; data: {code: number}};

type ChunkedDataEvents = {chunks: unknown[]; receivedFinal: boolean};

type EventData = {id?: string; chunk?: unknown; final?: boolean; index: number};

type SocketEventCallback = (eventName: SocketEventName, data?: States | EventCallbackError) => void;

type PusherWithAuthParams = InstanceType<typeof Pusher> & {
Expand Down Expand Up @@ -139,13 +152,13 @@ function getChannel(channelName: string): Channel | undefined {
/**
* Binds an event callback to a channel + eventName
*/
function bindEventToChannel(channel: Channel | undefined, eventName: PusherEventName, eventCallback: (data: PushJSON) => void = () => {}) {
function bindEventToChannel<EventName extends PusherEventName>(channel: Channel | undefined, eventName: EventName, eventCallback: (data: EventData<EventName>) => void = () => {}) {
if (!eventName) {
return;
}

const chunkedDataEvents: Record<string, ChunkedDataEvents> = {};
const callback = (eventData: string | Record<string, unknown> | EventData) => {
const callback = (eventData: EventData<EventName>) => {
if (shouldForceOffline) {
Log.info('[Pusher] Ignoring a Push event because shouldForceOffline = true');
return;
Expand Down Expand Up @@ -207,7 +220,12 @@ function bindEventToChannel(channel: Channel | undefined, eventName: PusherEvent
* Subscribe to a channel and an event
* @param [onResubscribe] Callback to be called when reconnection happen
*/
function subscribe(channelName: string, eventName: PusherEventName, eventCallback: (data: PushJSON) => void = () => {}, onResubscribe = () => {}): Promise<void> {
function subscribe<EventName extends PusherEventName>(
channelName: string,
eventName: EventName,
eventCallback: (data: EventData<EventName>) => void = () => {},
onResubscribe = () => {},
): Promise<void> {
return new Promise((resolve, reject) => {
// We cannot call subscribe() before init(). Prevent any attempt to do this on dev.
if (!socket) {
Expand Down Expand Up @@ -307,7 +325,7 @@ function isSubscribed(channelName: string): boolean {
/**
* Sends an event over a specific event/channel in pusher.
*/
function sendEvent(channelName: string, eventName: PusherEventName, payload: Record<string, unknown>) {
function sendEvent<EventName extends PusherEventName>(channelName: string, eventName: EventName, payload: EventData<EventName>) {
// Check to see if we are subscribed to this channel before sending the event. Sending client events over channels
// we are not subscribed too will throw errors and cause reconnection attempts. Subscriptions are not instant and
// can happen later than we expect.
Expand Down Expand Up @@ -394,4 +412,4 @@ export {
getPusherSocketID,
};

export type {EventCallbackError, States, PushJSON};
export type {EventCallbackError, States, PushJSON, UserIsTypingEvent, UserIsLeavingRoomEvent};
2 changes: 1 addition & 1 deletion src/libs/PusherUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function triggerMultiEventHandler(eventType: string, data: OnyxUpdate[]): Promis
* Abstraction around subscribing to private user channel events. Handles all logs and errors automatically.
*/
function subscribeToPrivateUserChannelEvent(eventName: string, accountID: string, onEvent: (pushJSON: PushJSON) => void) {
const pusherChannelName = `${CONST.PUSHER.PRIVATE_USER_CHANNEL_PREFIX}${accountID}${CONFIG.PUSHER.SUFFIX}`;
const pusherChannelName = `${CONST.PUSHER.PRIVATE_USER_CHANNEL_PREFIX}${accountID}${CONFIG.PUSHER.SUFFIX}` as const;

function logPusherEvent(pushJSON: PushJSON) {
Log.info(`[Report] Handled ${eventName} event sent by Pusher`, false, pushJSON);
Expand Down
18 changes: 9 additions & 9 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import {Beta, Login, PersonalDetails, Policy, PolicyTags, Report, ReportAction, Transaction} from '@src/types/onyx';
import {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon';
import {ChangeLog, IOUMessage, OriginalMessageActionName} from '@src/types/onyx/OriginalMessage';
import {Message, ReportActions} from '@src/types/onyx/ReportAction';
import {ChangeLog, IOUMessage, OriginalMessageActionName, OriginalMessageCreated} from '@src/types/onyx/OriginalMessage';
import {NotificationPreference} from '@src/types/onyx/Report';
import {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction';
import {Receipt, WaypointCollection} from '@src/types/onyx/Transaction';
import DeepValueOf from '@src/types/utils/DeepValueOf';
import {EmptyObject, isEmptyObject, isNotEmptyObject} from '@src/types/utils/EmptyObject';
Expand Down Expand Up @@ -183,10 +184,8 @@ type OptimisticClosedReportAction = Pick<
'actionName' | 'actorAccountID' | 'automatic' | 'avatar' | 'created' | 'message' | 'originalMessage' | 'pendingAction' | 'person' | 'reportActionID' | 'shouldShow'
>;

type OptimisticCreatedReportAction = Pick<
ReportAction,
'actionName' | 'actorAccountID' | 'automatic' | 'avatar' | 'created' | 'message' | 'person' | 'reportActionID' | 'shouldShow' | 'pendingAction'
>;
type OptimisticCreatedReportAction = OriginalMessageCreated &
Pick<ReportActionBase, 'actorAccountID' | 'automatic' | 'avatar' | 'created' | 'message' | 'person' | 'reportActionID' | 'shouldShow' | 'pendingAction'>;

type OptimisticChatReport = Pick<
Report,
Expand Down Expand Up @@ -2349,7 +2348,7 @@ function getParsedComment(text: string): string {
return text.length <= CONST.MAX_MARKUP_LENGTH ? parser.replace(text) : lodashEscape(text);
}

function buildOptimisticAddCommentReportAction(text?: string, file?: File & {source: string; uri: string}): OptimisticReportAction {
function buildOptimisticAddCommentReportAction(text?: string, file?: File): OptimisticReportAction {
const parser = new ExpensiMark();
const commentText = getParsedComment(text ?? '');
const isAttachment = !text && file !== undefined;
Expand Down Expand Up @@ -3015,7 +3014,7 @@ function buildOptimisticChatReport(
oldPolicyName = '',
visibility: ValueOf<typeof CONST.REPORT.VISIBILITY> | undefined = undefined,
writeCapability: ValueOf<typeof CONST.REPORT.WRITE_CAPABILITIES> | undefined = undefined,
notificationPreference: string | number = CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS,
notificationPreference: NotificationPreference = CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS,
parentReportActionID = '',
parentReportID = '',
welcomeMessage = '',
Expand Down Expand Up @@ -3059,6 +3058,7 @@ function buildOptimisticCreatedReportAction(emailCreatingAction: string, created
return {
reportActionID: NumberUtils.rand64(),
actionName: CONST.REPORT.ACTIONS.TYPE.CREATED,
originalMessage: undefined,
blazejkustra marked this conversation as resolved.
Show resolved Hide resolved
pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD,
actorAccountID: currentUserAccountID,
message: [
Expand Down Expand Up @@ -4383,4 +4383,4 @@ export {
canEditWriteCapability,
};

export type {OptionData};
export type {OptionData, OptimisticChatReport};
2 changes: 1 addition & 1 deletion src/libs/SidebarUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ function getOptionData(
result.hasOutstandingChildRequest = report.hasOutstandingChildRequest;
result.parentReportID = report.parentReportID ?? '';
result.isWaitingOnBankAccount = report.isWaitingOnBankAccount;
result.notificationPreference = report.notificationPreference ?? '';
result.notificationPreference = report.notificationPreference;
result.isAllowedToComment = ReportUtils.canUserPerformWriteAction(report);
result.chatType = report.chatType;

Expand Down
Loading
Loading