diff --git a/app/definitions/IMention.ts b/app/definitions/IMention.ts
new file mode 100644
index 000000000..81305e7e2
--- /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/IUrl.ts b/app/definitions/IUrl.ts
new file mode 100644
index 000000000..9b72fda26
--- /dev/null
+++ b/app/definitions/IUrl.ts
@@ -0,0 +1,6 @@
+export interface IUrl {
+ title: string;
+ description: string;
+ image: string;
+ url: string;
+}
diff --git a/app/stacks/types.ts b/app/stacks/types.ts
index daf366d92..c6016f2e4 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/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 c5d69ba7c..d78aa0d74 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 7ff7b691f..5effd8adb 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 61%
rename from app/views/ThreadMessagesView/Dropdown/DropdownItemHeader.js
rename to app/views/ThreadMessagesView/Dropdown/DropdownItemHeader.tsx
index d1ee227de..9ffe4c89e 100644
--- a/app/views/ThreadMessagesView/Dropdown/DropdownItemHeader.js
+++ b/app/views/ThreadMessagesView/Dropdown/DropdownItemHeader.tsx
@@ -1,17 +1,21 @@
import React from 'react';
-import PropTypes from 'prop-types';
-import { FILTER } from '../filters';
+import { Filter } from '../filters';
import I18n from '../../../i18n';
import DropdownItem from './DropdownItem';
-const DropdownItemHeader = ({ currentFilter, onPress }) => {
+interface IDropdownItemHeader {
+ 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:
@@ -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 74%
rename from app/views/ThreadMessagesView/Dropdown/index.js
rename to app/views/ThreadMessagesView/Dropdown/index.tsx
index ac33cd89b..a7a6eca07 100644
--- a/app/views/ThreadMessagesView/Dropdown/index.js
+++ b/app/views/ThreadMessagesView/Dropdown/index.tsx
@@ -1,30 +1,31 @@
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';
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';
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: Filter;
+ onClose: () => void;
+ onFilterSelected: (value: string) => void;
+}
- constructor(props) {
+class Dropdown extends React.Component {
+ private animatedValue: Animated.Value;
+
+ constructor(props: IDropdownProps) {
super(props);
this.animatedValue = new Animated.Value(0);
}
@@ -85,9 +86,9 @@ class Dropdown extends React.Component {
]}>
-
-
-
+
+
+
>
);
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 1caef2c27..5f444bccb 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';
@@ -10,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 { TThreadModel } from '../../definitions/IThread';
const styles = StyleSheet.create({
container: {
@@ -56,7 +56,18 @@ const styles = StyleSheet.create({
}
});
-const Item = ({ item, baseUrl, theme, useRealName, user, badgeColor, onPress, toggleFollowThread }) => {
+interface IItem {
+ item: TThreadModel;
+ baseUrl: string;
+ theme: string;
+ useRealName: boolean;
+ user: any;
+ badgeColor: string;
+ onPress: (item: TThreadModel) => 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 +80,7 @@ const Item = ({ item, baseUrl, theme, useRealName, user, badgeColor, onPress, to
testID={`thread-messages-view-${item.msg}`}
style={{ backgroundColor: themes[theme].backgroundColor }}>
-
+
@@ -87,10 +89,11 @@ const Item = ({ item, baseUrl, theme, useRealName, user, badgeColor, onPress, to
{time}
+ {/* @ts-ignore */}
;
+ route: RouteProp;
+ 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;
@@ -60,9 +98,9 @@ class ThreadMessagesView extends React.Component {
end: false,
messages: [],
displayingThreads: [],
- subscription: {},
+ subscription: {} as TSubscriptionModel,
showFilterDropdown: false,
- currentFilter: FILTER.ALL,
+ currentFilter: Filter.All,
isSearching: false,
searchText: ''
};
@@ -76,7 +114,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 +131,7 @@ class ThreadMessagesView extends React.Component {
}
}
- getHeader = () => {
+ getHeader = (): StackNavigationOptions => {
const { isSearching } = this.state;
const { navigation, isMasterDetail, insets, theme } = this.props;
@@ -115,7 +153,7 @@ class ThreadMessagesView extends React.Component {
};
}
- const options = {
+ const options: StackNavigationOptions = {
headerLeft: () => (
navigation.pop()} tintColor={themes[theme].headerTintColor} />
),
@@ -150,7 +188,7 @@ class ThreadMessagesView extends React.Component {
const db = database.active;
// subscription query
- const subscription = await db.collections.get('subscriptions').find(this.rid);
+ const subscription = (await db.collections.get('subscriptions').find(this.rid)) as TSubscriptionModel;
const observable = subscription.observe();
this.subSubscription = observable.subscribe(data => {
this.setState({ subscription: data });
@@ -162,7 +200,7 @@ class ThreadMessagesView extends React.Component {
}
};
- subscribeMessages = (subscription, searchText) => {
+ subscribeMessages = (subscription?: TSubscriptionModel, searchText?: string) => {
try {
const db = database.active;
@@ -180,13 +218,17 @@ class ThreadMessagesView extends React.Component {
.get('threads')
.query(...whereClause)
.observeWithColumns(['updated_at']);
- this.messagesSubscription = this.messagesObservable.subscribe(messages => {
+
+ // TODO: Refactor when migrate messages
+ this.messagesSubscription = this.messagesObservable.subscribe((messages: any) => {
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 {
+ // @ts-ignore
this.state.messages = messages;
+ // @ts-ignore
this.state.displayingThreads = displayingThreads;
}
});
@@ -212,7 +254,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 +272,23 @@ 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
+ // @ts-ignore
+ const allThreadsRecords = (await subscription.threads.fetch()) as TThreadModel[];
+ 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 = 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 => {
+ protectedFunction((t: any) => {
t._raw = sanitizedRaw({ id: thread._id }, threadsCollection.schema);
t.subscription.set(subscription);
Object.assign(t, thread);
@@ -246,7 +298,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);
})
);
@@ -254,16 +306,16 @@ class ThreadMessagesView extends React.Component {
}
if (remove && remove.length) {
- threadsToDelete = allThreadsRecords.filter(i1 => 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());
}
- 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 +326,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 +335,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 +355,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 {
@@ -336,13 +388,13 @@ class ThreadMessagesView extends React.Component {
});
};
- onSearchChangeText = debounce(searchText => {
+ onSearchChangeText = debounce((searchText: string) => {
const { subscription } = this.state;
this.setState({ searchText }, () => this.subscribeMessages(subscription, searchText));
}, 300);
onThreadPress = debounce(
- item => {
+ (item: any) => {
const { subscription } = this.state;
const { navigation, isMasterDetail } = this.props;
if (isMasterDetail) {
@@ -352,7 +404,7 @@ class ThreadMessagesView extends React.Component {
rid: item.subscription.id,
tmid: item.id,
name: makeThreadName(item),
- t: 'thread',
+ t: SubscriptionType.THREAD,
roomUserId: RocketChat.getUidDirectMessage(subscription)
});
},
@@ -360,20 +412,21 @@ class ThreadMessagesView extends React.Component {
true
);
- getBadgeColor = item => {
+ getBadgeColor = (item: TThreadModel) => {
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: TSubscriptionModel, currentFilter?: Filter): TThreadModel[] => {
// 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));
+ if (currentFilter === Filter.Following) {
+ 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 +442,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 +457,7 @@ class ThreadMessagesView extends React.Component {
}
};
- renderItem = ({ item }) => {
+ renderItem = ({ item }: { item: TThreadModel }) => {
const { user, navigation, baseUrl, useRealName } = this.props;
const badgeColor = this.getBadgeColor(item);
return (
@@ -442,9 +495,9 @@ class ThreadMessagesView extends React.Component {
const { theme } = this.props;
if (!messages?.length || !displayingThreads?.length) {
let text;
- if (currentFilter === FILTER.FOLLOWING) {
+ if (currentFilter === Filter.Following) {
text = I18n.t('No_threads_following');
- } else if (currentFilter === FILTER.UNREAD) {
+ } else if (currentFilter === Filter.Unread) {
text = I18n.t('No_threads_unread');
} else {
text = I18n.t('No_threads');
@@ -494,7 +547,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,
diff --git a/app/views/ThreadMessagesView/styles.js b/app/views/ThreadMessagesView/styles.ts
similarity index 93%
rename from app/views/ThreadMessagesView/styles.js
rename to app/views/ThreadMessagesView/styles.ts
index ec24da209..a6b30bbac 100644
--- a/app/views/ThreadMessagesView/styles.js
+++ b/app/views/ThreadMessagesView/styles.ts
@@ -25,6 +25,6 @@ export default StyleSheet.create({
borderBottomWidth: StyleSheet.hairlineWidth
},
backdrop: {
- ...StyleSheet.absoluteFill
+ ...StyleSheet.absoluteFillObject
}
});