From d3dbdb28c151809f59049af168ec9b06f4340044 Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Thu, 2 Dec 2021 20:12:21 -0300 Subject: [PATCH 01/13] Chore: Migrate ThreadMessagesView to Typescript --- .../{DropdownItem.js => DropdownItem.tsx} | 17 ++++----- ...wnItemFilter.js => DropdownItemFilter.tsx} | 15 ++++---- ...wnItemHeader.js => DropdownItemHeader.tsx} | 13 +++---- .../Dropdown/{index.js => index.tsx} | 25 +++++++------ .../ThreadMessagesView/{Item.js => Item.tsx} | 37 +++++++------------ .../{filters.js => filters.ts} | 0 .../{styles.js => styles.ts} | 3 +- 7 files changed, 50 insertions(+), 60 deletions(-) rename app/views/ThreadMessagesView/Dropdown/{DropdownItem.js => DropdownItem.tsx} (84%) rename app/views/ThreadMessagesView/Dropdown/{DropdownItemFilter.js => DropdownItemFilter.tsx} (54%) rename app/views/ThreadMessagesView/Dropdown/{DropdownItemHeader.js => DropdownItemHeader.tsx} (73%) rename app/views/ThreadMessagesView/Dropdown/{index.js => index.tsx} (86%) rename app/views/ThreadMessagesView/{Item.js => Item.tsx} (81%) rename app/views/ThreadMessagesView/{filters.js => filters.ts} (100%) rename app/views/ThreadMessagesView/{styles.js => styles.ts} (80%) diff --git a/app/views/ThreadMessagesView/Dropdown/DropdownItem.js b/app/views/ThreadMessagesView/Dropdown/DropdownItem.tsx similarity index 84% rename from app/views/ThreadMessagesView/Dropdown/DropdownItem.js rename to app/views/ThreadMessagesView/Dropdown/DropdownItem.tsx index c5d69ba7c8..d78aa0d74a 100644 --- a/app/views/ThreadMessagesView/Dropdown/DropdownItem.js +++ b/app/views/ThreadMessagesView/Dropdown/DropdownItem.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { StyleSheet, Text, View } from 'react-native'; import { themes } from '../../../constants/colors'; @@ -23,7 +22,14 @@ const styles = StyleSheet.create({ } }); -const DropdownItem = React.memo(({ theme, onPress, iconName, text }) => ( +interface IDropdownItem { + text: string; + iconName: string; + theme: string; + onPress: () => void; +} + +const DropdownItem = React.memo(({ theme, onPress, iconName, text }: IDropdownItem) => ( {text} @@ -32,11 +38,4 @@ const DropdownItem = React.memo(({ theme, onPress, iconName, text }) => ( )); -DropdownItem.propTypes = { - text: PropTypes.string, - iconName: PropTypes.string, - theme: PropTypes.string, - onPress: PropTypes.func -}; - export default withTheme(DropdownItem); diff --git a/app/views/ThreadMessagesView/Dropdown/DropdownItemFilter.js b/app/views/ThreadMessagesView/Dropdown/DropdownItemFilter.tsx similarity index 54% rename from app/views/ThreadMessagesView/Dropdown/DropdownItemFilter.js rename to app/views/ThreadMessagesView/Dropdown/DropdownItemFilter.tsx index 7ff7b691fd..5effd8adb6 100644 --- a/app/views/ThreadMessagesView/Dropdown/DropdownItemFilter.js +++ b/app/views/ThreadMessagesView/Dropdown/DropdownItemFilter.tsx @@ -1,17 +1,16 @@ import React from 'react'; -import PropTypes from 'prop-types'; import I18n from '../../../i18n'; import DropdownItem from './DropdownItem'; -const DropdownItemFilter = ({ currentFilter, value, onPress }) => ( +interface IDropdownItemFilter { + currentFilter: string; + value: string; + onPress: (value: string) => void; +} + +const DropdownItemFilter = ({ currentFilter, value, onPress }: IDropdownItemFilter): JSX.Element => ( onPress(value)} /> ); -DropdownItemFilter.propTypes = { - currentFilter: PropTypes.string, - value: PropTypes.string, - onPress: PropTypes.func -}; - export default DropdownItemFilter; diff --git a/app/views/ThreadMessagesView/Dropdown/DropdownItemHeader.js b/app/views/ThreadMessagesView/Dropdown/DropdownItemHeader.tsx similarity index 73% rename from app/views/ThreadMessagesView/Dropdown/DropdownItemHeader.js rename to app/views/ThreadMessagesView/Dropdown/DropdownItemHeader.tsx index d1ee227de8..0b8560d454 100644 --- a/app/views/ThreadMessagesView/Dropdown/DropdownItemHeader.js +++ b/app/views/ThreadMessagesView/Dropdown/DropdownItemHeader.tsx @@ -1,11 +1,15 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { FILTER } from '../filters'; import I18n from '../../../i18n'; import DropdownItem from './DropdownItem'; -const DropdownItemHeader = ({ currentFilter, onPress }) => { +interface IDropdownItemHeader { + currentFilter: string; + onPress: () => void; +} + +const DropdownItemHeader = ({ currentFilter, onPress }: IDropdownItemHeader): JSX.Element => { let text; switch (currentFilter) { case FILTER.FOLLOWING: @@ -21,9 +25,4 @@ const DropdownItemHeader = ({ currentFilter, onPress }) => { return ; }; -DropdownItemHeader.propTypes = { - currentFilter: PropTypes.string, - onPress: PropTypes.func -}; - export default DropdownItemHeader; diff --git a/app/views/ThreadMessagesView/Dropdown/index.js b/app/views/ThreadMessagesView/Dropdown/index.tsx similarity index 86% rename from app/views/ThreadMessagesView/Dropdown/index.js rename to app/views/ThreadMessagesView/Dropdown/index.tsx index ac33cd89b4..a9083a2e37 100644 --- a/app/views/ThreadMessagesView/Dropdown/index.js +++ b/app/views/ThreadMessagesView/Dropdown/index.tsx @@ -1,7 +1,6 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { Animated, Easing, TouchableWithoutFeedback } from 'react-native'; -import { withSafeAreaInsets } from 'react-native-safe-area-context'; +import { EdgeInsets, withSafeAreaInsets } from 'react-native-safe-area-context'; import styles from '../styles'; import { themes } from '../../../constants/colors'; @@ -14,17 +13,19 @@ import DropdownItemHeader from './DropdownItemHeader'; const ANIMATION_DURATION = 200; -class Dropdown extends React.Component { - static propTypes = { - isMasterDetail: PropTypes.bool, - theme: PropTypes.string, - insets: PropTypes.object, - currentFilter: PropTypes.string, - onClose: PropTypes.func, - onFilterSelected: PropTypes.func - }; +interface IDropdownProps { + isMasterDetail: boolean; + theme: string; + insets: EdgeInsets; + currentFilter: string; + onClose: () => void; + onFilterSelected: (value: string) => void; +} + +class Dropdown extends React.Component { + private animatedValue: Animated.Value; - constructor(props) { + constructor(props: IDropdownProps) { super(props); this.animatedValue = new Animated.Value(0); } diff --git a/app/views/ThreadMessagesView/Item.js b/app/views/ThreadMessagesView/Item.tsx similarity index 81% rename from app/views/ThreadMessagesView/Item.js rename to app/views/ThreadMessagesView/Item.tsx index 1caef2c27c..883c93c69b 100644 --- a/app/views/ThreadMessagesView/Item.js +++ b/app/views/ThreadMessagesView/Item.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { StyleSheet, Text, View } from 'react-native'; import Touchable from 'react-native-platform-touchable'; @@ -56,7 +55,18 @@ const styles = StyleSheet.create({ } }); -const Item = ({ item, baseUrl, theme, useRealName, user, badgeColor, onPress, toggleFollowThread }) => { +interface IItem { + item: any; + baseUrl: string; + theme: string; + useRealName: boolean; + user: any; + badgeColor: string; + onPress: (item: any) => void; + toggleFollowThread: (isFollowing: boolean, id: string) => void; +} + +const Item = ({ item, baseUrl, theme, useRealName, user, badgeColor, onPress, toggleFollowThread }: IItem) => { const username = (useRealName && item?.u?.name) || item?.u?.username; let time; if (item?.ts) { @@ -69,16 +79,7 @@ const Item = ({ item, baseUrl, theme, useRealName, user, badgeColor, onPress, to testID={`thread-messages-view-${item.msg}`} style={{ backgroundColor: themes[theme].backgroundColor }}> - + @@ -87,6 +88,7 @@ const Item = ({ item, baseUrl, theme, useRealName, user, badgeColor, onPress, to {time} + {/* @ts-ignore */} Date: Fri, 3 Dec 2021 15:41:00 -0300 Subject: [PATCH 02/13] index migration --- app/views/ThreadMessagesView/filters.ts | 10 +- .../{index.js => index.tsx} | 181 +++++++++++++----- 2 files changed, 136 insertions(+), 55 deletions(-) rename app/views/ThreadMessagesView/{index.js => index.tsx} (75%) diff --git a/app/views/ThreadMessagesView/filters.ts b/app/views/ThreadMessagesView/filters.ts index 388f28f2ae..dd37421a0c 100644 --- a/app/views/ThreadMessagesView/filters.ts +++ b/app/views/ThreadMessagesView/filters.ts @@ -1,5 +1,5 @@ -export const FILTER = { - ALL: 'All', - FOLLOWING: 'Following', - UNREAD: 'Unread' -}; +export enum FILTER { + ALL = 'All', + FOLLOWING = 'Following', + UNREAD = 'Unread' +} diff --git a/app/views/ThreadMessagesView/index.js b/app/views/ThreadMessagesView/index.tsx similarity index 75% rename from app/views/ThreadMessagesView/index.js rename to app/views/ThreadMessagesView/index.tsx index 5c6556efb2..f974afb84d 100644 --- a/app/views/ThreadMessagesView/index.js +++ b/app/views/ThreadMessagesView/index.tsx @@ -1,11 +1,14 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { FlatList } from 'react-native'; import { connect } from 'react-redux'; import { Q } from '@nozbe/watermelondb'; import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; -import { withSafeAreaInsets } from 'react-native-safe-area-context'; -import { HeaderBackButton } from '@react-navigation/stack'; +import { EdgeInsets, withSafeAreaInsets } from 'react-native-safe-area-context'; +import { HeaderBackButton, StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack'; +import { RouteProp } from '@react-navigation/native'; +import { Observable, Subscription } from 'rxjs'; +import Model from '@nozbe/watermelondb/Model'; +import Database from '@nozbe/watermelondb/Database'; import ActivityIndicator from '../../containers/ActivityIndicator'; import I18n from '../../i18n'; @@ -38,19 +41,83 @@ import styles from './styles'; const API_FETCH_COUNT = 50; -class ThreadMessagesView extends React.Component { - static propTypes = { - user: PropTypes.object, - navigation: PropTypes.object, - route: PropTypes.object, - baseUrl: PropTypes.string, - useRealName: PropTypes.bool, - theme: PropTypes.string, - isMasterDetail: PropTypes.bool, - insets: PropTypes.object - }; +interface IFileThread { + _id: string; + name: string; + type: string; +} +interface IUserCreateThread { + _id: string; + username: string; + name: string; +} + +interface IThreadResult { + _id: string; + rid: string; + ts: string; + msg: string; + file?: IFileThread; + files?: IFileThread[]; + groupable?: boolean; + attachments?: any[]; + md?: any[]; + u: IUserCreateThread; + _updatedAt: string; + urls: any[]; + mentions: any[]; + channels: any[]; + replies: string[]; + tcount: number; + tlm: string; +} - constructor(props) { +interface IResultFetch { + threads: IThreadResult[]; + count: number; + offset: number; + total: number; + success: boolean; +} + +interface IThreadMessagesViewState { + loading: boolean; + end: boolean; + messages: any[]; + displayingThreads: any[]; + // TODO: Refactor when migrate room + subscription: any; + showFilterDropdown: boolean; + currentFilter: FILTER; + isSearching: boolean; + searchText: string; +} + +interface IThreadMessagesViewProps { + navigation: StackNavigationProp; + route: RouteProp<{ ThreadMessagesView: { rid: string; t: string } }, 'ThreadMessagesView'>; + user: any; + baseUrl: string; + useRealName: boolean; + theme: string; + isMasterDetail: boolean; + insets: EdgeInsets; +} + +class ThreadMessagesView extends React.Component { + private mounted: boolean; + + private rid: string; + + private t: string; + + private subSubscription: any; + + private messagesSubscription?: Subscription; + + private messagesObservable!: Observable; + + constructor(props: IThreadMessagesViewProps) { super(props); this.mounted = false; this.rid = props.route.params?.rid; @@ -68,7 +135,7 @@ class ThreadMessagesView extends React.Component { }; this.setHeader(); this.initSubscription(); - this.subscribeMessages(); + this.subscribeMessages({}); } componentDidMount() { @@ -76,7 +143,7 @@ class ThreadMessagesView extends React.Component { this.init(); } - componentDidUpdate(prevProps) { + componentDidUpdate(prevProps: IThreadMessagesViewProps) { const { insets } = this.props; if (insets.left !== prevProps.insets.left || insets.right !== prevProps.insets.right) { this.setHeader(); @@ -93,7 +160,7 @@ class ThreadMessagesView extends React.Component { } } - getHeader = () => { + getHeader = (): StackNavigationOptions => { const { isSearching } = this.state; const { navigation, isMasterDetail, insets, theme } = this.props; @@ -115,7 +182,7 @@ class ThreadMessagesView extends React.Component { }; } - const options = { + const options: StackNavigationOptions = { headerLeft: () => ( navigation.pop()} tintColor={themes[theme].headerTintColor} /> ), @@ -150,19 +217,20 @@ class ThreadMessagesView extends React.Component { const db = database.active; // subscription query + // TODO: Refactor when migrate room const subscription = await db.collections.get('subscriptions').find(this.rid); const observable = subscription.observe(); - this.subSubscription = observable.subscribe(data => { + this.subSubscription = observable.subscribe((data: any) => { this.setState({ subscription: data }); }); - this.subscribeMessages(subscription); + this.subscribeMessages({ subscription }); } catch (e) { log(e); } }; - subscribeMessages = (subscription, searchText) => { + subscribeMessages = ({ subscription, searchText }: { subscription?: any; searchText?: string }) => { try { const db = database.active; @@ -180,13 +248,16 @@ class ThreadMessagesView extends React.Component { .get('threads') .query(...whereClause) .observeWithColumns(['updated_at']); - this.messagesSubscription = this.messagesObservable.subscribe(messages => { + // TODO: Refactor when migrate room + this.messagesSubscription = this.messagesObservable.subscribe((messages: any) => { const { currentFilter } = this.state; const displayingThreads = this.getFilteredThreads(messages, subscription, currentFilter); if (this.mounted) { this.setState({ messages, displayingThreads }); } else { + // @ts-ignore this.state.messages = messages; + // @ts-ignore this.state.displayingThreads = displayingThreads; } }); @@ -212,7 +283,15 @@ class ThreadMessagesView extends React.Component { } }; - updateThreads = async ({ update, remove, lastThreadSync }) => { + updateThreads = async ({ + update, + remove, + lastThreadSync + }: { + update: IThreadResult[]; + remove?: IThreadResult[]; + lastThreadSync: Date; + }) => { const { subscription } = this.state; // if there's no subscription, manage data on this.state.messages // note: sync will never be called without subscription @@ -222,21 +301,22 @@ class ThreadMessagesView extends React.Component { } try { - const db = database.active; + const db: Database = database.active; const threadsCollection = db.get('threads'); - const allThreadsRecords = await subscription.threads.fetch(); - let threadsToCreate = []; - let threadsToUpdate = []; - let threadsToDelete = []; + // TODO: Refactor when migrate room + const allThreadsRecords: any[] = await subscription.threads.fetch(); + let threadsToCreate: any[] = []; + let threadsToUpdate: any[] = []; + let threadsToDelete: any[] = []; if (update && update.length) { update = update.map(m => buildMessage(m)); // filter threads threadsToCreate = update.filter(i1 => !allThreadsRecords.find(i2 => i1._id === i2.id)); threadsToUpdate = allThreadsRecords.filter(i1 => update.find(i2 => i1.id === i2._id)); - threadsToCreate = threadsToCreate.map(thread => + threadsToCreate = threadsToCreate.map((thread: IThreadResult) => threadsCollection.prepareCreate( - protectedFunction(t => { + protectedFunction((t: any) => { t._raw = sanitizedRaw({ id: thread._id }, threadsCollection.schema); t.subscription.set(subscription); Object.assign(t, thread); @@ -246,7 +326,7 @@ class ThreadMessagesView extends React.Component { threadsToUpdate = threadsToUpdate.map(thread => { const newThread = update.find(t => t._id === thread.id); return thread.prepareUpdate( - protectedFunction(t => { + protectedFunction((t: any) => { Object.assign(t, newThread); }) ); @@ -258,12 +338,12 @@ class ThreadMessagesView extends React.Component { threadsToDelete = threadsToDelete.map(t => t.prepareDestroyPermanently()); } - await db.action(async () => { + await db.write(async () => { await db.batch( ...threadsToCreate, ...threadsToUpdate, ...threadsToDelete, - subscription.prepareUpdate(s => { + subscription.prepareUpdate((s: any) => { s.lastThreadSync = lastThreadSync; }) ); @@ -274,7 +354,7 @@ class ThreadMessagesView extends React.Component { }; // eslint-disable-next-line react/sort-comp - load = debounce(async lastThreadSync => { + load = debounce(async (lastThreadSync: Date) => { const { loading, end, messages, searchText } = this.state; if (end || loading || !this.mounted) { return; @@ -283,7 +363,7 @@ class ThreadMessagesView extends React.Component { this.setState({ loading: true }); try { - const result = await RocketChat.getThreadsList({ + const result: IResultFetch = await RocketChat.getThreadsList({ rid: this.rid, count: API_FETCH_COUNT, offset: messages.length, @@ -303,7 +383,7 @@ class ThreadMessagesView extends React.Component { }, 300); // eslint-disable-next-line react/sort-comp - sync = async updatedSince => { + sync = async (updatedSince: Date) => { this.setState({ loading: true }); try { @@ -332,17 +412,17 @@ class ThreadMessagesView extends React.Component { this.setState({ isSearching: false, searchText: '' }, () => { const { subscription } = this.state; this.setHeader(); - this.subscribeMessages(subscription); + this.subscribeMessages({ subscription }); }); }; - onSearchChangeText = debounce(searchText => { + onSearchChangeText = debounce((searchText: string) => { const { subscription } = this.state; - this.setState({ searchText }, () => this.subscribeMessages(subscription, searchText)); + this.setState({ searchText }, () => this.subscribeMessages({ subscription, searchText })); }, 300); onThreadPress = debounce( - item => { + (item: any) => { const { subscription } = this.state; const { navigation, isMasterDetail } = this.props; if (isMasterDetail) { @@ -360,20 +440,21 @@ class ThreadMessagesView extends React.Component { true ); - getBadgeColor = item => { + getBadgeColor = (item: any) => { const { subscription } = this.state; const { theme } = this.props; return getBadgeColor({ subscription, theme, messageId: item?.id }); }; // helper to query threads - getFilteredThreads = (messages, subscription, currentFilter) => { + getFilteredThreads = (messages: any, subscription: any /** TODO: Refactor when migrate room */, currentFilter?: FILTER) => { // const { currentFilter } = this.state; const { user } = this.props; if (currentFilter === FILTER.FOLLOWING) { - return messages?.filter(item => item?.replies?.find(u => u === user.id)); - } else if (currentFilter === FILTER.UNREAD) { - return messages?.filter(item => subscription?.tunread?.includes(item?.id)); + return messages?.filter((item: { replies: any[] }) => item?.replies?.find(u => u === user.id)); + } + if (currentFilter === FILTER.UNREAD) { + return messages?.filter((item: { id: string }) => subscription?.tunread?.includes(item?.id)); } return messages; }; @@ -389,13 +470,13 @@ class ThreadMessagesView extends React.Component { closeFilterDropdown = () => this.setState({ showFilterDropdown: false }); - onFilterSelected = filter => { + onFilterSelected = (filter: FILTER) => { const { messages, subscription } = this.state; const displayingThreads = this.getFilteredThreads(messages, subscription, filter); this.setState({ currentFilter: filter, displayingThreads }); }; - toggleFollowThread = async (isFollowingThread, tmid) => { + toggleFollowThread = async (isFollowingThread: boolean, tmid: string) => { try { await RocketChat.toggleFollowMessage(tmid, !isFollowingThread); EventEmitter.emit(LISTENER, { message: isFollowingThread ? I18n.t('Unfollowed_thread') : I18n.t('Following_thread') }); @@ -404,7 +485,7 @@ class ThreadMessagesView extends React.Component { } }; - renderItem = ({ item }) => { + renderItem = ({ item }: { item: any }) => { const { user, navigation, baseUrl, useRealName } = this.props; const badgeColor = this.getBadgeColor(item); return ( @@ -494,7 +575,7 @@ class ThreadMessagesView extends React.Component { } } -const mapStateToProps = state => ({ +const mapStateToProps = (state: any) => ({ baseUrl: state.server.server, user: getUserSelector(state), useRealName: state.settings.UI_Use_Real_Name, From 093a63083079b4ebdae365fd5056c529752cfc64 Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Fri, 3 Dec 2021 17:59:09 -0300 Subject: [PATCH 03/13] minor tweak --- app/stacks/types.ts | 2 +- app/views/ThreadMessagesView/index.tsx | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/stacks/types.ts b/app/stacks/types.ts index c9386d939c..e0e055e9d7 100644 --- a/app/stacks/types.ts +++ b/app/stacks/types.ts @@ -18,7 +18,7 @@ export type ChatsStackParamList = { name?: string; fname?: string; prid?: string; - room: IRoom; + room?: IRoom; jumpToMessageId?: string; jumpToThreadId?: string; roomUserId?: string; diff --git a/app/views/ThreadMessagesView/index.tsx b/app/views/ThreadMessagesView/index.tsx index f974afb84d..1a45eb1cb0 100644 --- a/app/views/ThreadMessagesView/index.tsx +++ b/app/views/ThreadMessagesView/index.tsx @@ -33,6 +33,8 @@ import { getHeaderTitlePosition } from '../../containers/Header'; import EventEmitter from '../../utils/events'; import { LISTENER } from '../../containers/Toast'; import SearchHeader from '../../containers/SearchHeader'; +import { ChatsStackParamList } from '../../stacks/types'; +import { RoomType } from '../../definitions/IRoom'; import { FILTER } from './filters'; import DropdownItemHeader from './Dropdown/DropdownItemHeader'; import Dropdown from './Dropdown'; @@ -94,8 +96,8 @@ interface IThreadMessagesViewState { } interface IThreadMessagesViewProps { - navigation: StackNavigationProp; - route: RouteProp<{ ThreadMessagesView: { rid: string; t: string } }, 'ThreadMessagesView'>; + navigation: StackNavigationProp; + route: RouteProp; user: any; baseUrl: string; useRealName: boolean; @@ -432,7 +434,7 @@ class ThreadMessagesView extends React.Component Date: Fri, 3 Dec 2021 18:03:10 -0300 Subject: [PATCH 04/13] minor tweak --- app/views/ThreadMessagesView/styles.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/app/views/ThreadMessagesView/styles.ts b/app/views/ThreadMessagesView/styles.ts index 5fabbecda9..a6b30bbac7 100644 --- a/app/views/ThreadMessagesView/styles.ts +++ b/app/views/ThreadMessagesView/styles.ts @@ -25,7 +25,6 @@ export default StyleSheet.create({ borderBottomWidth: StyleSheet.hairlineWidth }, backdrop: { - // https://reactnative.dev/docs/stylesheet#absolutefill-vs-absolutefillobject ...StyleSheet.absoluteFillObject } }); From 47f4df32a84548afef75aa96a9634a1f697d6ff3 Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Fri, 3 Dec 2021 18:33:57 -0300 Subject: [PATCH 05/13] tweak subscribeMessages --- app/views/ThreadMessagesView/index.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/views/ThreadMessagesView/index.tsx b/app/views/ThreadMessagesView/index.tsx index 1a45eb1cb0..25b7d5b20b 100644 --- a/app/views/ThreadMessagesView/index.tsx +++ b/app/views/ThreadMessagesView/index.tsx @@ -137,7 +137,7 @@ class ThreadMessagesView extends React.Component { + subscribeMessages = (subscription?: any, searchText?: string) => { try { const db = database.active; @@ -414,13 +414,13 @@ class ThreadMessagesView extends React.Component { const { subscription } = this.state; this.setHeader(); - this.subscribeMessages({ subscription }); + this.subscribeMessages(subscription); }); }; onSearchChangeText = debounce((searchText: string) => { const { subscription } = this.state; - this.setState({ searchText }, () => this.subscribeMessages({ subscription, searchText })); + this.setState({ searchText }, () => this.subscribeMessages(subscription, searchText)); }, 300); onThreadPress = debounce( From 7107f94a2a23de4be977edc72440502e65f3031c Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Fri, 3 Dec 2021 18:45:26 -0300 Subject: [PATCH 06/13] delete TODO --- app/views/ThreadMessagesView/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/ThreadMessagesView/index.tsx b/app/views/ThreadMessagesView/index.tsx index 25b7d5b20b..4c05315c1f 100644 --- a/app/views/ThreadMessagesView/index.tsx +++ b/app/views/ThreadMessagesView/index.tsx @@ -449,7 +449,7 @@ class ThreadMessagesView extends React.Component { + getFilteredThreads = (messages: any, subscription: any, currentFilter?: FILTER) => { // const { currentFilter } = this.state; const { user } = this.props; if (currentFilter === FILTER.FOLLOWING) { From 9ccf816a35e5e5b8c96cd4d9577d5bb763ec39bc Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Thu, 9 Dec 2021 20:26:46 -0300 Subject: [PATCH 07/13] new definitions --- app/definitions/IMention.ts | 6 +++++ app/definitions/IReaction.ts | 5 ++++ app/definitions/IRoom.ts | 11 +++++++++ app/definitions/IThread.ts | 45 ++++++++++++++++++++++++++++++++++++ app/definitions/IUrl.ts | 6 +++++ 5 files changed, 73 insertions(+) create mode 100644 app/definitions/IMention.ts create mode 100644 app/definitions/IReaction.ts create mode 100644 app/definitions/IThread.ts create mode 100644 app/definitions/IUrl.ts diff --git a/app/definitions/IMention.ts b/app/definitions/IMention.ts new file mode 100644 index 0000000000..81305e7e28 --- /dev/null +++ b/app/definitions/IMention.ts @@ -0,0 +1,6 @@ +export interface IMention { + _id: string; + name: string; + username: string; + type: string; +} diff --git a/app/definitions/IReaction.ts b/app/definitions/IReaction.ts new file mode 100644 index 0000000000..a28f5d06e7 --- /dev/null +++ b/app/definitions/IReaction.ts @@ -0,0 +1,5 @@ +export interface IReaction { + _id: string; + emoji: string; + usernames: string[]; +} diff --git a/app/definitions/IRoom.ts b/app/definitions/IRoom.ts index 786c1d7c85..bcac33992c 100644 --- a/app/definitions/IRoom.ts +++ b/app/definitions/IRoom.ts @@ -1,4 +1,7 @@ +import Model from '@nozbe/watermelondb/Model'; + import { IRocketChatRecord } from './IRocketChatRecord'; +import { IThreadModel } from './IThread'; export enum RoomType { GROUP = 'p', @@ -24,4 +27,12 @@ export interface IRoom extends IRocketChatRecord { autoTranslate?: boolean; observe?: Function; usedCannedResponse: string; + lastThreadSync?: Date; + tunread?: string[]; +} + +interface IRoomAssociation { + threads: { fetch(): IThreadModel[] }; } + +export type IRoomModel = IRoom & Model & IRoomAssociation; diff --git a/app/definitions/IThread.ts b/app/definitions/IThread.ts new file mode 100644 index 0000000000..f4d485c04e --- /dev/null +++ b/app/definitions/IThread.ts @@ -0,0 +1,45 @@ +import Model from '@nozbe/watermelondb/Model'; + +import { IAttachment } from './IAttachment'; +import { IMention } from './IMention'; +import { IReaction } from './IReaction'; +import { RoomType } from './IRoom'; +import { IUrl } from './IUrl'; + +export interface IThread { + id: string; + msg: string; + t: RoomType; + rid: string; + _updated_at: Date; + ts: Date; + u: { _id: string; username: string; name: string }; + alias: any; + parse_urls: any; + groupable: boolean; + avatar: string; + emoji: any; + attachments: IAttachment[]; + urls: IUrl[]; + status: number; + pinned: null; + starred: null; + edited_by: { username: string }; + reactions: IReaction[]; + role: null; + drid: null; + dcount: null; + dlm: null; + tmid: string; + tcount: 2; + tlm: Date; + replies: string[]; + mentions: IMention[]; + channels: []; + unread: boolean; + auto_translate: boolean; + translations: any; + e2e: any; +} + +export type IThreadModel = IThread & Model; diff --git a/app/definitions/IUrl.ts b/app/definitions/IUrl.ts new file mode 100644 index 0000000000..9b72fda268 --- /dev/null +++ b/app/definitions/IUrl.ts @@ -0,0 +1,6 @@ +export interface IUrl { + title: string; + description: string; + image: string; + url: string; +} From 58bd5fdd0882488a404ba408faf5b3e7e395ae84 Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Thu, 9 Dec 2021 20:36:55 -0300 Subject: [PATCH 08/13] refactor threadMessageView --- app/definitions/IThread.ts | 25 +++++++ .../Dropdown/DropdownItemHeader.tsx | 8 +- .../ThreadMessagesView/Dropdown/index.tsx | 10 +-- app/views/ThreadMessagesView/Item.tsx | 5 +- app/views/ThreadMessagesView/filters.ts | 8 +- app/views/ThreadMessagesView/index.tsx | 75 ++++++------------- 6 files changed, 63 insertions(+), 68 deletions(-) diff --git a/app/definitions/IThread.ts b/app/definitions/IThread.ts index f4d485c04e..dfd04aa53e 100644 --- a/app/definitions/IThread.ts +++ b/app/definitions/IThread.ts @@ -6,6 +6,31 @@ import { IReaction } from './IReaction'; import { RoomType } from './IRoom'; import { IUrl } from './IUrl'; +interface IFileThread { + _id: string; + name: string; + type: string; +} +export interface IThreadResult { + _id: string; + rid: string; + ts: string; + msg: string; + file?: IFileThread; + files?: IFileThread[]; + groupable?: boolean; + attachments?: IAttachment[]; + md?: any[]; + u: { _id: string; username: string; name: string }; + _updatedAt: string; + urls: any[]; + mentions: any[]; + channels: any[]; + replies: string[]; + tcount: number; + tlm: string; +} + export interface IThread { id: string; msg: string; diff --git a/app/views/ThreadMessagesView/Dropdown/DropdownItemHeader.tsx b/app/views/ThreadMessagesView/Dropdown/DropdownItemHeader.tsx index 0b8560d454..9ffe4c89e0 100644 --- a/app/views/ThreadMessagesView/Dropdown/DropdownItemHeader.tsx +++ b/app/views/ThreadMessagesView/Dropdown/DropdownItemHeader.tsx @@ -1,21 +1,21 @@ import React from 'react'; -import { FILTER } from '../filters'; +import { Filter } from '../filters'; import I18n from '../../../i18n'; import DropdownItem from './DropdownItem'; interface IDropdownItemHeader { - currentFilter: string; + currentFilter: Filter; onPress: () => void; } const DropdownItemHeader = ({ currentFilter, onPress }: IDropdownItemHeader): JSX.Element => { let text; switch (currentFilter) { - case FILTER.FOLLOWING: + case Filter.Following: text = I18n.t('Threads_displaying_following'); break; - case FILTER.UNREAD: + case Filter.Unread: text = I18n.t('Threads_displaying_unread'); break; default: diff --git a/app/views/ThreadMessagesView/Dropdown/index.tsx b/app/views/ThreadMessagesView/Dropdown/index.tsx index a9083a2e37..a7a6eca077 100644 --- a/app/views/ThreadMessagesView/Dropdown/index.tsx +++ b/app/views/ThreadMessagesView/Dropdown/index.tsx @@ -7,7 +7,7 @@ import { themes } from '../../../constants/colors'; import { withTheme } from '../../../theme'; import { headerHeight } from '../../../containers/Header'; import * as List from '../../../containers/List'; -import { FILTER } from '../filters'; +import { Filter } from '../filters'; import DropdownItemFilter from './DropdownItemFilter'; import DropdownItemHeader from './DropdownItemHeader'; @@ -17,7 +17,7 @@ interface IDropdownProps { isMasterDetail: boolean; theme: string; insets: EdgeInsets; - currentFilter: string; + currentFilter: Filter; onClose: () => void; onFilterSelected: (value: string) => void; } @@ -86,9 +86,9 @@ class Dropdown extends React.Component { ]}> - - - + + + ); diff --git a/app/views/ThreadMessagesView/Item.tsx b/app/views/ThreadMessagesView/Item.tsx index 883c93c69b..69cedfa397 100644 --- a/app/views/ThreadMessagesView/Item.tsx +++ b/app/views/ThreadMessagesView/Item.tsx @@ -9,6 +9,7 @@ import { themes } from '../../constants/colors'; import Markdown from '../../containers/markdown'; import { formatDateThreads, makeThreadName } from '../../utils/room'; import ThreadDetails from '../../containers/ThreadDetails'; +import { IThreadModel } from '../../definitions/IThread'; const styles = StyleSheet.create({ container: { @@ -56,13 +57,13 @@ const styles = StyleSheet.create({ }); interface IItem { - item: any; + item: IThreadModel; baseUrl: string; theme: string; useRealName: boolean; user: any; badgeColor: string; - onPress: (item: any) => void; + onPress: (item: IThreadModel) => void; toggleFollowThread: (isFollowing: boolean, id: string) => void; } diff --git a/app/views/ThreadMessagesView/filters.ts b/app/views/ThreadMessagesView/filters.ts index dd37421a0c..69b104e4d7 100644 --- a/app/views/ThreadMessagesView/filters.ts +++ b/app/views/ThreadMessagesView/filters.ts @@ -1,5 +1,5 @@ -export enum FILTER { - ALL = 'All', - FOLLOWING = 'Following', - UNREAD = 'Unread' +export enum Filter { + All = 'All', + Following = 'Following', + Unread = 'Unread' } diff --git a/app/views/ThreadMessagesView/index.tsx b/app/views/ThreadMessagesView/index.tsx index 4c05315c1f..99d4cc504b 100644 --- a/app/views/ThreadMessagesView/index.tsx +++ b/app/views/ThreadMessagesView/index.tsx @@ -34,8 +34,9 @@ import EventEmitter from '../../utils/events'; import { LISTENER } from '../../containers/Toast'; import SearchHeader from '../../containers/SearchHeader'; import { ChatsStackParamList } from '../../stacks/types'; -import { RoomType } from '../../definitions/IRoom'; -import { FILTER } from './filters'; +import { IRoomModel, RoomType } from '../../definitions/IRoom'; +import { IThreadResult, IThreadModel } from '../../definitions/IThread'; +import { Filter } from './filters'; import DropdownItemHeader from './Dropdown/DropdownItemHeader'; import Dropdown from './Dropdown'; import Item from './Item'; @@ -43,37 +44,6 @@ import styles from './styles'; const API_FETCH_COUNT = 50; -interface IFileThread { - _id: string; - name: string; - type: string; -} -interface IUserCreateThread { - _id: string; - username: string; - name: string; -} - -interface IThreadResult { - _id: string; - rid: string; - ts: string; - msg: string; - file?: IFileThread; - files?: IFileThread[]; - groupable?: boolean; - attachments?: any[]; - md?: any[]; - u: IUserCreateThread; - _updatedAt: string; - urls: any[]; - mentions: any[]; - channels: any[]; - replies: string[]; - tcount: number; - tlm: string; -} - interface IResultFetch { threads: IThreadResult[]; count: number; @@ -86,11 +56,10 @@ interface IThreadMessagesViewState { loading: boolean; end: boolean; messages: any[]; - displayingThreads: any[]; - // TODO: Refactor when migrate room - subscription: any; + displayingThreads: IThreadModel[]; + subscription: IRoomModel; showFilterDropdown: boolean; - currentFilter: FILTER; + currentFilter: Filter; isSearching: boolean; searchText: string; } @@ -129,9 +98,9 @@ class ThreadMessagesView extends React.Component { + this.subSubscription = observable.subscribe(data => { this.setState({ subscription: data }); }); @@ -232,7 +200,7 @@ class ThreadMessagesView extends React.Component { + subscribeMessages = (subscription?: IRoomModel, searchText?: string) => { try { const db = database.active; @@ -250,10 +218,11 @@ class ThreadMessagesView extends React.Component { const { currentFilter } = this.state; - const displayingThreads = this.getFilteredThreads(messages, subscription, currentFilter); + const displayingThreads = this.getFilteredThreads(messages, subscription!, currentFilter); if (this.mounted) { this.setState({ messages, displayingThreads }); } else { @@ -306,7 +275,7 @@ class ThreadMessagesView extends React.Component !allThreadsRecords.find(i2 => i1._id === i2.id)); threadsToUpdate = allThreadsRecords.filter(i1 => update.find(i2 => i1.id === i2._id)); - threadsToCreate = threadsToCreate.map((thread: IThreadResult) => + threadsToCreate = threadsToCreate.map(thread => threadsCollection.prepareCreate( protectedFunction((t: any) => { t._raw = sanitizedRaw({ id: thread._id }, threadsCollection.schema); @@ -449,13 +418,13 @@ class ThreadMessagesView extends React.Component { + getFilteredThreads = (messages: any, subscription: IRoomModel, currentFilter?: Filter): IThreadModel[] => { // const { currentFilter } = this.state; const { user } = this.props; - if (currentFilter === FILTER.FOLLOWING) { + if (currentFilter === Filter.Following) { return messages?.filter((item: { replies: any[] }) => item?.replies?.find(u => u === user.id)); } - if (currentFilter === FILTER.UNREAD) { + if (currentFilter === Filter.Unread) { return messages?.filter((item: { id: string }) => subscription?.tunread?.includes(item?.id)); } return messages; @@ -472,7 +441,7 @@ class ThreadMessagesView extends React.Component this.setState({ showFilterDropdown: false }); - onFilterSelected = (filter: FILTER) => { + onFilterSelected = (filter: Filter) => { const { messages, subscription } = this.state; const displayingThreads = this.getFilteredThreads(messages, subscription, filter); this.setState({ currentFilter: filter, displayingThreads }); @@ -525,9 +494,9 @@ class ThreadMessagesView extends React.Component Date: Thu, 9 Dec 2021 20:39:32 -0300 Subject: [PATCH 09/13] renderItem IthreadModel --- app/views/ThreadMessagesView/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/ThreadMessagesView/index.tsx b/app/views/ThreadMessagesView/index.tsx index 99d4cc504b..5a836a3c7e 100644 --- a/app/views/ThreadMessagesView/index.tsx +++ b/app/views/ThreadMessagesView/index.tsx @@ -411,7 +411,7 @@ class ThreadMessagesView extends React.Component { + getBadgeColor = (item: IThreadModel) => { const { subscription } = this.state; const { theme } = this.props; return getBadgeColor({ subscription, theme, messageId: item?.id }); @@ -456,7 +456,7 @@ class ThreadMessagesView extends React.Component { + renderItem = ({ item }: { item: IThreadModel }) => { const { user, navigation, baseUrl, useRealName } = this.props; const badgeColor = this.getBadgeColor(item); return ( From fd221825e2eb930ce1b3f43c2dbead84d7a00e82 Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Tue, 21 Dec 2021 00:57:30 -0300 Subject: [PATCH 10/13] minor tweak --- app/definitions/IThread.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/definitions/IThread.ts b/app/definitions/IThread.ts index dfd04aa53e..c7a1cd5f47 100644 --- a/app/definitions/IThread.ts +++ b/app/definitions/IThread.ts @@ -49,7 +49,7 @@ export interface IThread { status: number; pinned: null; starred: null; - edited_by: { username: string }; + editedBy: { username: string }; reactions: IReaction[]; role: null; drid: null; @@ -62,7 +62,7 @@ export interface IThread { mentions: IMention[]; channels: []; unread: boolean; - auto_translate: boolean; + autoTranslate: boolean; translations: any; e2e: any; } From b0385f554e59cd83592c58f4553a65110fca245d Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Tue, 21 Dec 2021 01:02:07 -0300 Subject: [PATCH 11/13] minor tweak --- app/definitions/IThread.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/definitions/IThread.ts b/app/definitions/IThread.ts index c7a1cd5f47..4ab01e9825 100644 --- a/app/definitions/IThread.ts +++ b/app/definitions/IThread.ts @@ -36,27 +36,27 @@ export interface IThread { msg: string; t: RoomType; rid: string; - _updated_at: Date; + _updatedAt: Date; ts: Date; u: { _id: string; username: string; name: string }; alias: any; - parse_urls: any; + parseUrls: any; groupable: boolean; avatar: string; emoji: any; attachments: IAttachment[]; urls: IUrl[]; status: number; - pinned: null; - starred: null; + pinned: boolean; + starred: boolean; editedBy: { username: string }; reactions: IReaction[]; - role: null; - drid: null; - dcount: null; - dlm: null; + role: string; + drid: string; + dcount: number; + dlm: number; tmid: string; - tcount: 2; + tcount: number; tlm: Date; replies: string[]; mentions: IMention[]; From affb9da14e1c8978cf4425adf190fed0852ddebb Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Tue, 28 Dec 2021 20:38:52 -0300 Subject: [PATCH 12/13] minor tweak --- app/definitions/IThread.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/definitions/IThread.ts b/app/definitions/IThread.ts index 4ab01e9825..6d785caef5 100644 --- a/app/definitions/IThread.ts +++ b/app/definitions/IThread.ts @@ -63,8 +63,8 @@ export interface IThread { channels: []; unread: boolean; autoTranslate: boolean; - translations: any; - e2e: any; + translations: string; + e2e: string; } export type IThreadModel = IThread & Model; From 6a9dfbe8740330991a4a047e271edc9600342f0d Mon Sep 17 00:00:00 2001 From: AlexAlexandre Date: Tue, 11 Jan 2022 16:54:50 -0300 Subject: [PATCH 13/13] refactor: update new types and interfaces for use ISubscription --- app/stacks/types.ts | 2 +- app/views/ThreadMessagesView/Item.tsx | 8 +++---- app/views/ThreadMessagesView/index.tsx | 31 +++++++++++++------------- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/app/stacks/types.ts b/app/stacks/types.ts index daf366d924..c6016f2e4f 100644 --- a/app/stacks/types.ts +++ b/app/stacks/types.ts @@ -18,7 +18,7 @@ export type ChatsStackParamList = { name?: string; fname?: string; prid?: string; - room: ISubscription; + room?: ISubscription; jumpToMessageId?: string; jumpToThreadId?: string; roomUserId?: string; diff --git a/app/views/ThreadMessagesView/Item.tsx b/app/views/ThreadMessagesView/Item.tsx index 69cedfa397..5f444bccb9 100644 --- a/app/views/ThreadMessagesView/Item.tsx +++ b/app/views/ThreadMessagesView/Item.tsx @@ -9,7 +9,7 @@ import { themes } from '../../constants/colors'; import Markdown from '../../containers/markdown'; import { formatDateThreads, makeThreadName } from '../../utils/room'; import ThreadDetails from '../../containers/ThreadDetails'; -import { IThreadModel } from '../../definitions/IThread'; +import { TThreadModel } from '../../definitions/IThread'; const styles = StyleSheet.create({ container: { @@ -57,13 +57,13 @@ const styles = StyleSheet.create({ }); interface IItem { - item: IThreadModel; + item: TThreadModel; baseUrl: string; theme: string; useRealName: boolean; user: any; badgeColor: string; - onPress: (item: IThreadModel) => void; + onPress: (item: TThreadModel) => void; toggleFollowThread: (isFollowing: boolean, id: string) => void; } @@ -93,7 +93,7 @@ const Item = ({ item, baseUrl, theme, useRealName, user, badgeColor, onPress, to { this.setState({ subscription: data }); @@ -200,7 +200,7 @@ class ThreadMessagesView extends React.Component { + subscribeMessages = (subscription?: TSubscriptionModel, searchText?: string) => { try { const db = database.active; @@ -275,7 +275,8 @@ class ThreadMessagesView extends React.Component buildMessage(m)); // filter threads - threadsToCreate = update.filter(i1 => !allThreadsRecords.find(i2 => i1._id === i2.id)); - threadsToUpdate = allThreadsRecords.filter(i1 => update.find(i2 => i1.id === i2._id)); + threadsToCreate = update.filter(i1 => allThreadsRecords.find((i2: { id: string }) => i1._id === i2.id)); + threadsToUpdate = allThreadsRecords.filter((i1: { id: string }) => update.find(i2 => i1.id === i2._id)); threadsToCreate = threadsToCreate.map(thread => threadsCollection.prepareCreate( protectedFunction((t: any) => { @@ -305,7 +306,7 @@ class ThreadMessagesView extends React.Component remove.find(i2 => i1.id === i2._id)); + threadsToDelete = allThreadsRecords.filter((i1: { id: string }) => remove.find(i2 => i1.id === i2._id)); threadsToDelete = threadsToDelete.map(t => t.prepareDestroyPermanently()); } @@ -403,7 +404,7 @@ class ThreadMessagesView extends React.Component { + getBadgeColor = (item: TThreadModel) => { const { subscription } = this.state; const { theme } = this.props; return getBadgeColor({ subscription, theme, messageId: item?.id }); }; // helper to query threads - getFilteredThreads = (messages: any, subscription: IRoomModel, currentFilter?: Filter): IThreadModel[] => { + getFilteredThreads = (messages: any, subscription: TSubscriptionModel, currentFilter?: Filter): TThreadModel[] => { // const { currentFilter } = this.state; const { user } = this.props; if (currentFilter === Filter.Following) { @@ -456,7 +457,7 @@ class ThreadMessagesView extends React.Component { + renderItem = ({ item }: { item: TThreadModel }) => { const { user, navigation, baseUrl, useRealName } = this.props; const badgeColor = this.getBadgeColor(item); return (