Chore: Migrate Database to Typescript (#3580)

Co-authored-by: Gleidson Daniel Silva <gleidson10daniel@hotmail.com>
Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
Reinaldo Neto 2022-02-01 10:39:09 -03:00 committed by GitHub
parent 761f4d1e2c
commit f88bdfb97c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 280 additions and 145 deletions

View File

@ -1,16 +1,18 @@
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Q } from '@nozbe/watermelondb'; import { Q } from '@nozbe/watermelondb';
import { Observable, Subscription } from 'rxjs';
import database from '../../lib/database'; import database from '../../lib/database';
import { getUserSelector } from '../../selectors/login'; import { getUserSelector } from '../../selectors/login';
import { TSubscriptionModel, TUserModel } from '../../definitions';
import Avatar from './Avatar'; import Avatar from './Avatar';
import { IAvatar } from './interfaces'; import { IAvatar } from './interfaces';
class AvatarContainer extends React.Component<IAvatar, any> { class AvatarContainer extends React.Component<IAvatar, any> {
private mounted: boolean; private mounted: boolean;
private subscription: any; private subscription?: Subscription;
static defaultProps = { static defaultProps = {
text: '', text: '',
@ -59,15 +61,17 @@ class AvatarContainer extends React.Component<IAvatar, any> {
record = user; record = user;
} else { } else {
const { rid } = this.props; const { rid } = this.props;
record = await subsCollection.find(rid); if (rid) {
record = await subsCollection.find(rid);
}
} }
} catch { } catch {
// Record not found // Record not found
} }
if (record) { if (record) {
const observable = record.observe(); const observable = record.observe() as Observable<TSubscriptionModel | TUserModel>;
this.subscription = observable.subscribe((r: any) => { this.subscription = observable.subscribe(r => {
const { avatarETag } = r; const { avatarETag } = r;
if (this.mounted) { if (this.mounted) {
this.setState({ avatarETag }); this.setState({ avatarETag });

View File

@ -36,7 +36,7 @@ interface IEmojiPickerProps {
} }
interface IEmojiPickerState { interface IEmojiPickerState {
frequentlyUsed: []; frequentlyUsed: (string | { content?: string; extension?: string; isCustom: boolean })[];
customEmojis: any; customEmojis: any;
show: boolean; show: boolean;
width: number | null; width: number | null;
@ -114,7 +114,7 @@ class EmojiPicker extends Component<IEmojiPickerProps, IEmojiPickerState> {
// Do nothing // Do nothing
} }
await db.action(async () => { await db.write(async () => {
if (freqEmojiRecord) { if (freqEmojiRecord) {
await freqEmojiRecord.update((f: any) => { await freqEmojiRecord.update((f: any) => {
f.count += 1; f.count += 1;
@ -132,8 +132,8 @@ class EmojiPicker extends Component<IEmojiPickerProps, IEmojiPickerState> {
updateFrequentlyUsed = async () => { updateFrequentlyUsed = async () => {
const db = database.active; const db = database.active;
const frequentlyUsedRecords = await db.get('frequently_used_emojis').query().fetch(); const frequentlyUsedRecords = await db.get('frequently_used_emojis').query().fetch();
let frequentlyUsed: any = orderBy(frequentlyUsedRecords, ['count'], ['desc']); const frequentlyUsedOrdered = orderBy(frequentlyUsedRecords, ['count'], ['desc']);
frequentlyUsed = frequentlyUsed.map((item: IEmoji) => { const frequentlyUsed = frequentlyUsedOrdered.map(item => {
if (item.isCustom) { if (item.isCustom) {
return { content: item.content, extension: item.extension, isCustom: item.isCustom }; return { content: item.content, extension: item.extension, isCustom: item.isCustom };
} }

View File

@ -10,6 +10,7 @@ import database from '../../lib/database';
import { Button } from '../ActionSheet'; import { Button } from '../ActionSheet';
import { useDimensions } from '../../dimensions'; import { useDimensions } from '../../dimensions';
import sharedStyles from '../../views/Styles'; import sharedStyles from '../../views/Styles';
import { TFrequentlyUsedEmojiModel } from '../../definitions/IFrequentlyUsedEmoji';
import { IEmoji } from '../EmojiPicker/interfaces'; import { IEmoji } from '../EmojiPicker/interfaces';
interface IHeader { interface IHeader {
@ -90,14 +91,14 @@ const HeaderFooter = React.memo(({ onReaction, theme }: THeaderFooter) => (
)); ));
const Header = React.memo(({ handleReaction, server, message, isMasterDetail, theme }: IHeader) => { const Header = React.memo(({ handleReaction, server, message, isMasterDetail, theme }: IHeader) => {
const [items, setItems] = useState([]); const [items, setItems] = useState<(TFrequentlyUsedEmojiModel | string)[]>([]);
const { width, height }: any = useDimensions(); const { width, height }: any = useDimensions();
const setEmojis = async () => { const setEmojis = async () => {
try { try {
const db = database.active; const db = database.active;
const freqEmojiCollection = db.get('frequently_used_emojis'); const freqEmojiCollection = db.get('frequently_used_emojis');
let freqEmojis = await freqEmojiCollection.query().fetch(); let freqEmojis: (TFrequentlyUsedEmojiModel | string)[] = await freqEmojiCollection.query().fetch();
const isLandscape = width > height; const isLandscape = width > height;
const size = (isLandscape || isMasterDetail ? width / 2 : width) - CONTAINER_MARGIN * 2; const size = (isLandscape || isMasterDetail ? width / 2 : width) - CONTAINER_MARGIN * 2;

View File

@ -15,6 +15,7 @@ import { showConfirmationAlert } from '../../utils/info';
import { useActionSheet } from '../ActionSheet'; import { useActionSheet } from '../ActionSheet';
import Header, { HEADER_HEIGHT } from './Header'; import Header, { HEADER_HEIGHT } from './Header';
import events from '../../utils/log/events'; import events from '../../utils/log/events';
import { TMessageModel } from '../../definitions/IMessage';
interface IMessageActions { interface IMessageActions {
room: { room: {
@ -182,9 +183,9 @@ const MessageActions = React.memo(
if (result.success) { if (result.success) {
const subCollection = db.get('subscriptions'); const subCollection = db.get('subscriptions');
const subRecord = await subCollection.find(rid); const subRecord = await subCollection.find(rid);
await db.action(async () => { await db.write(async () => {
try { try {
await subRecord.update((sub: any) => (sub.lastOpen = ts)); await subRecord.update(sub => (sub.lastOpen = ts));
} catch { } catch {
// do nothing // do nothing
} }
@ -269,11 +270,11 @@ const MessageActions = React.memo(
} }
}; };
const handleToggleTranslation = async (message: any) => { const handleToggleTranslation = async (message: TMessageModel) => {
try { try {
const db = database.active; const db = database.active;
await db.action(async () => { await db.write(async () => {
await message.update((m: any) => { await message.update(m => {
m.autoTranslate = !m.autoTranslate; m.autoTranslate = !m.autoTranslate;
m._updatedAt = new Date(); m._updatedAt = new Date();
}); });
@ -320,7 +321,7 @@ const MessageActions = React.memo(
}); });
}; };
const getOptions = (message: any) => { const getOptions = (message: TMessageModel) => {
let options: any = []; let options: any = [];
// Reply // Reply
@ -446,7 +447,7 @@ const MessageActions = React.memo(
return options; return options;
}; };
const showMessageActions = async (message: any) => { const showMessageActions = async (message: TMessageModel) => {
logEvent(events.ROOM_SHOW_MSG_ACTIONS); logEvent(events.ROOM_SHOW_MSG_ACTIONS);
await getPermissions(); await getPermissions();
showActionSheet({ showActionSheet({

View File

@ -36,7 +36,7 @@ const MessageErrorActions = forwardRef(({ tmid }: any, ref): any => {
try { try {
// Find the thread header and update it // Find the thread header and update it
const msg = await msgCollection.find(tmid); const msg = await msgCollection.find(tmid);
if (msg.tcount <= 1) { if (msg?.tcount && msg.tcount <= 1) {
deleteBatch.push( deleteBatch.push(
msg.prepareUpdate((m: any) => { msg.prepareUpdate((m: any) => {
m.tcount = null; m.tcount = null;
@ -62,7 +62,7 @@ const MessageErrorActions = forwardRef(({ tmid }: any, ref): any => {
// Do nothing: message not found // Do nothing: message not found
} }
} }
await db.action(async () => { await db.write(async () => {
await db.batch(...deleteBatch); await db.batch(...deleteBatch);
}); });
} catch (e) { } catch (e) {

View File

@ -52,8 +52,9 @@ interface IThreadDetails {
} }
const ThreadDetails = ({ item, user, badgeColor, toggleFollowThread, style, theme }: IThreadDetails) => { const ThreadDetails = ({ item, user, badgeColor, toggleFollowThread, style, theme }: IThreadDetails) => {
let { tcount } = item; let tcount: number | string = item?.tcount ?? 0;
if (tcount! >= 1000) {
if (tcount >= 1000) {
tcount = '+999'; tcount = '+999';
} }

View File

@ -182,7 +182,7 @@ const Fields = React.memo(
<Text style={[styles.fieldTitle, { color: themes[theme].bodyText }]}>{field.title}</Text> <Text style={[styles.fieldTitle, { color: themes[theme].bodyText }]}>{field.title}</Text>
{/* @ts-ignore*/} {/* @ts-ignore*/}
<Markdown <Markdown
msg={field.value!} msg={field?.value || ''}
baseUrl={baseUrl} baseUrl={baseUrl}
username={user.username} username={user.username}
getCustomEmoji={getCustomEmoji} getCustomEmoji={getCustomEmoji}

View File

@ -101,7 +101,7 @@ export interface IMessageThread {
msg: string; msg: string;
tcount: number; tcount: number;
theme: string; theme: string;
tlm: string; tlm: Date;
isThreadRoom: boolean; isThreadRoom: boolean;
id: string; id: string;
} }

View File

@ -1,3 +1,4 @@
import { TMessageModel } from '../../definitions/IMessage';
import I18n from '../../i18n'; import I18n from '../../i18n';
import { DISCUSSION } from './constants'; import { DISCUSSION } from './constants';
@ -149,7 +150,7 @@ export const getInfoMessage = ({ type, role, msg, author }: TInfoMessage) => {
return ''; return '';
}; };
export const getMessageTranslation = (message: { translations: any }, autoTranslateLanguage: string) => { export const getMessageTranslation = (message: TMessageModel, autoTranslateLanguage: string) => {
if (!autoTranslateLanguage) { if (!autoTranslateLanguage) {
return null; return null;
} }

View File

@ -7,4 +7,4 @@ export interface IFrequentlyUsedEmoji {
count: number; count: number;
} }
export type TFrequentlyUsedEmoji = IFrequentlyUsedEmoji & Model; export type TFrequentlyUsedEmojiModel = IFrequentlyUsedEmoji & Model;

View File

@ -15,4 +15,4 @@ export interface ILoggedUser {
enableMessageParserEarlyAdoption?: boolean; enableMessageParserEarlyAdoption?: boolean;
} }
export type TLoggedUser = ILoggedUser & Model; export type TLoggedUserModel = ILoggedUser & Model;

View File

@ -7,4 +7,4 @@ export interface IServerHistory {
updatedAt: Date; updatedAt: Date;
} }
export type TServerHistory = IServerHistory & Model; export type TServerHistoryModel = IServerHistory & Model;

View File

@ -79,8 +79,6 @@ export interface ISubscription {
avatarETag?: string; avatarETag?: string;
teamId?: string; teamId?: string;
teamMain?: boolean; teamMain?: boolean;
search?: boolean;
username?: string;
// https://nozbe.github.io/WatermelonDB/Relation.html#relation-api // https://nozbe.github.io/WatermelonDB/Relation.html#relation-api
messages: Relation<TMessageModel>; messages: Relation<TMessageModel>;
threads: Relation<TThreadModel>; threads: Relation<TThreadModel>;

View File

@ -43,10 +43,10 @@ export interface IThread {
id: string; id: string;
msg?: string; msg?: string;
t?: SubscriptionType; t?: SubscriptionType;
rid?: string; rid: string;
_updatedAt?: Date; _updatedAt: Date;
ts?: Date; ts: Date;
u?: IUserMessage; u: IUserMessage;
alias?: string; alias?: string;
parseUrls?: boolean; parseUrls?: boolean;
groupable?: boolean; groupable?: boolean;
@ -64,8 +64,8 @@ export interface IThread {
dcount?: number; dcount?: number;
dlm?: number; dlm?: number;
tmid?: string; tmid?: string;
tcount: number | string; tcount?: number;
tlm?: string; tlm?: Date;
replies?: string[]; replies?: string[];
mentions?: IUserMention[]; mentions?: IUserMention[];
channels?: IUserChannel[]; channels?: IUserChannel[];

View File

@ -3,12 +3,23 @@ import { StackNavigationProp } from '@react-navigation/stack';
import { Dispatch } from 'redux'; import { Dispatch } from 'redux';
export * from './IAttachment'; export * from './IAttachment';
export * from './IMessage';
export * from './INotification'; export * from './INotification';
export * from './IRoom';
export * from './IServer';
export * from './ISubscription';
export * from './IPreferences'; export * from './IPreferences';
export * from './ISubscription';
export * from './IRoom';
export * from './IMessage';
export * from './IThread';
export * from './IThreadMessage';
export * from './ICustomEmoji';
export * from './IFrequentlyUsedEmoji';
export * from './IUpload';
export * from './ISettings';
export * from './IRole';
export * from './IPermission';
export * from './ISlashCommand';
export * from './IUser';
export * from './IServer';
export * from './ILoggedUser';
export * from './IServerHistory'; export * from './IServerHistory';
export interface IBaseScreen<T extends Record<string, object | undefined>, S extends string> { export interface IBaseScreen<T extends Record<string, object | undefined>, S extends string> {

View File

@ -16,7 +16,6 @@ import { ISelectedUsers } from '../../reducers/selectedUsers';
import { IConnect } from '../../reducers/connect'; import { IConnect } from '../../reducers/connect';
import { ISettings } from '../../reducers/settings'; import { ISettings } from '../../reducers/settings';
export interface IApplicationState { export interface IApplicationState {
settings: ISettings; settings: ISettings;
login: any; login: any;
@ -49,4 +48,4 @@ export type TApplicationActions = TActionActiveUsers &
IActionSettings & IActionSettings &
TActionEncryption & TActionEncryption &
TActionSortPreferences & TActionSortPreferences &
TActionUserTyping; TActionUserTyping;

View File

@ -25,6 +25,7 @@ import serversSchema from './schema/servers';
import appSchema from './schema/app'; import appSchema from './schema/app';
import migrations from './model/migrations'; import migrations from './model/migrations';
import serversMigrations from './model/servers/migrations'; import serversMigrations from './model/servers/migrations';
import { TAppDatabase, TServerDatabase } from './interfaces';
const appGroupPath = isIOS ? appGroup.path : ''; const appGroupPath = isIOS ? appGroup.path : '';
@ -32,9 +33,9 @@ if (__DEV__ && isIOS) {
console.log(appGroupPath); console.log(appGroupPath);
} }
const getDatabasePath = name => `${appGroupPath}${name}${isOfficial ? '' : '-experimental'}.db`; const getDatabasePath = (name: string) => `${appGroupPath}${name}${isOfficial ? '' : '-experimental'}.db`;
export const getDatabase = (database = '') => { export const getDatabase = (database = ''): Database => {
const path = database.replace(/(^\w+:|^)\/\//, '').replace(/\//g, '.'); const path = database.replace(/(^\w+:|^)\/\//, '').replace(/\//g, '.');
const dbName = getDatabasePath(path); const dbName = getDatabasePath(path);
@ -64,8 +65,14 @@ export const getDatabase = (database = '') => {
}); });
}; };
interface IDatabases {
shareDB?: TAppDatabase;
serversDB: TServerDatabase;
activeDB?: TAppDatabase;
}
class DB { class DB {
databases = { databases: IDatabases = {
serversDB: new Database({ serversDB: new Database({
adapter: new SQLiteAdapter({ adapter: new SQLiteAdapter({
dbName: getDatabasePath('default'), dbName: getDatabasePath('default'),
@ -73,11 +80,12 @@ class DB {
migrations: serversMigrations migrations: serversMigrations
}), }),
modelClasses: [Server, LoggedUser, ServersHistory] modelClasses: [Server, LoggedUser, ServersHistory]
}) }) as TServerDatabase
}; };
get active() { // Expected at least one database
return this.databases.shareDB || this.databases.activeDB; get active(): TAppDatabase {
return this.databases.shareDB || this.databases.activeDB!;
} }
get share() { get share() {
@ -116,11 +124,11 @@ class DB {
Setting, Setting,
User User
] ]
}); }) as TAppDatabase;
} }
setActiveDB(database) { setActiveDB(database: string) {
this.databases.activeDB = getDatabase(database); this.databases.activeDB = getDatabase(database) as TAppDatabase;
} }
} }

View File

@ -0,0 +1,72 @@
import { Database, Collection } from '@nozbe/watermelondb';
import * as models from './model';
import * as definitions from '../../definitions';
export type TAppDatabaseNames =
| typeof models.SUBSCRIPTIONS_TABLE
| typeof models.ROOMS_TABLE
| typeof models.MESSAGES_TABLE
| typeof models.THREADS_TABLE
| typeof models.THREAD_MESSAGES_TABLE
| typeof models.CUSTOM_EMOJIS_TABLE
| typeof models.FREQUENTLY_USED_EMOJIS_TABLE
| typeof models.UPLOADS_TABLE
| typeof models.SETTINGS_TABLE
| typeof models.ROLES_TABLE
| typeof models.PERMISSIONS_TABLE
| typeof models.SLASH_COMMANDS_TABLE
| typeof models.USERS_TABLE;
// Verify if T extends one type from TAppDatabaseNames, and if is truly,
// returns the specific model type.
// https://stackoverflow.com/a/54166010 TypeScript function return type based on input parameter
type ObjectType<T> = T extends typeof models.SUBSCRIPTIONS_TABLE
? definitions.TSubscriptionModel
: T extends typeof models.ROOMS_TABLE
? definitions.TRoomModel
: T extends typeof models.MESSAGES_TABLE
? definitions.TMessageModel
: T extends typeof models.THREADS_TABLE
? definitions.TThreadModel
: T extends typeof models.THREAD_MESSAGES_TABLE
? definitions.TThreadMessageModel
: T extends typeof models.CUSTOM_EMOJIS_TABLE
? definitions.TCustomEmojiModel
: T extends typeof models.FREQUENTLY_USED_EMOJIS_TABLE
? definitions.TFrequentlyUsedEmojiModel
: T extends typeof models.UPLOADS_TABLE
? definitions.TUploadModel
: T extends typeof models.SETTINGS_TABLE
? definitions.TSettingsModel
: T extends typeof models.ROLES_TABLE
? definitions.TRoleModel
: T extends typeof models.PERMISSIONS_TABLE
? definitions.TPermissionModel
: T extends typeof models.SLASH_COMMANDS_TABLE
? definitions.TSlashCommandModel
: T extends typeof models.USERS_TABLE
? definitions.TUserModel
: never;
export type TAppDatabase = {
get: <T extends TAppDatabaseNames>(db: T) => Collection<ObjectType<T>>;
} & Database;
// Migration to server database
export type TServerDatabaseNames =
| typeof models.SERVERS_TABLE
| typeof models.LOGGED_USERS_TABLE
| typeof models.SERVERS_HISTORY_TABLE;
type ObjectServerType<T> = T extends typeof models.SERVERS_TABLE
? definitions.TServerModel
: T extends typeof models.LOGGED_USERS_TABLE
? definitions.TLoggedUserModel
: T extends typeof models.SERVERS_HISTORY_TABLE
? definitions.TServerHistoryModel
: never;
export type TServerDatabase = {
get: <T extends TServerDatabaseNames>(db: T) => Collection<ObjectServerType<T>>;
} & Database;

View File

@ -3,8 +3,10 @@ import { date, field, json } from '@nozbe/watermelondb/decorators';
import { sanitizer } from '../utils'; import { sanitizer } from '../utils';
export const CUSTOM_EMOJIS_TABLE = 'custom_emojis';
export default class CustomEmoji extends Model { export default class CustomEmoji extends Model {
static table = 'custom_emojis'; static table = CUSTOM_EMOJIS_TABLE;
@field('name') name; @field('name') name;

View File

@ -1,8 +1,9 @@
import { Model } from '@nozbe/watermelondb'; import { Model } from '@nozbe/watermelondb';
import { field } from '@nozbe/watermelondb/decorators'; import { field } from '@nozbe/watermelondb/decorators';
export const FREQUENTLY_USED_EMOJIS_TABLE = 'frequently_used_emojis';
export default class FrequentlyUsedEmoji extends Model { export default class FrequentlyUsedEmoji extends Model {
static table = 'frequently_used_emojis'; static table = FREQUENTLY_USED_EMOJIS_TABLE;
@field('content') content; @field('content') content;

View File

@ -3,10 +3,10 @@ import { date, field, json, relation } from '@nozbe/watermelondb/decorators';
import { sanitizer } from '../utils'; import { sanitizer } from '../utils';
export const TABLE_NAME = 'messages'; export const MESSAGES_TABLE = 'messages';
export default class Message extends Model { export default class Message extends Model {
static table = TABLE_NAME; static table = MESSAGES_TABLE;
static associations = { static associations = {
subscriptions: { type: 'belongs_to', key: 'rid' } subscriptions: { type: 'belongs_to', key: 'rid' }

View File

@ -3,8 +3,10 @@ import { date, json } from '@nozbe/watermelondb/decorators';
import { sanitizer } from '../utils'; import { sanitizer } from '../utils';
export const PERMISSIONS_TABLE = 'permissions';
export default class Permission extends Model { export default class Permission extends Model {
static table = 'permissions'; static table = PERMISSIONS_TABLE;
@json('roles', sanitizer) roles; @json('roles', sanitizer) roles;

View File

@ -1,8 +1,10 @@
import { Model } from '@nozbe/watermelondb'; import { Model } from '@nozbe/watermelondb';
import { field } from '@nozbe/watermelondb/decorators'; import { field } from '@nozbe/watermelondb/decorators';
export const ROLES_TABLE = 'roles';
export default class Role extends Model { export default class Role extends Model {
static table = 'roles'; static table = ROLES_TABLE;
@field('description') description; @field('description') description;
} }

View File

@ -3,8 +3,10 @@ import { field, json } from '@nozbe/watermelondb/decorators';
import { sanitizer } from '../utils'; import { sanitizer } from '../utils';
export const ROOMS_TABLE = 'rooms';
export default class Room extends Model { export default class Room extends Model {
static table = 'rooms'; static table = ROOMS_TABLE;
@json('custom_fields', sanitizer) customFields; @json('custom_fields', sanitizer) customFields;

View File

@ -1,8 +1,10 @@
import { Model } from '@nozbe/watermelondb'; import { Model } from '@nozbe/watermelondb';
import { date, field, readonly } from '@nozbe/watermelondb/decorators'; import { date, field, readonly } from '@nozbe/watermelondb/decorators';
export const SERVERS_HISTORY_TABLE = 'servers_history';
export default class ServersHistory extends Model { export default class ServersHistory extends Model {
static table = 'servers_history'; static table = SERVERS_HISTORY_TABLE;
@field('url') url; @field('url') url;

View File

@ -3,8 +3,10 @@ import { date, field, json } from '@nozbe/watermelondb/decorators';
import { sanitizer } from '../utils'; import { sanitizer } from '../utils';
export const SETTINGS_TABLE = 'settings';
export default class Setting extends Model { export default class Setting extends Model {
static table = 'settings'; static table = SETTINGS_TABLE;
@field('value_as_string') valueAsString; @field('value_as_string') valueAsString;

View File

@ -1,8 +1,10 @@
import { Model } from '@nozbe/watermelondb'; import { Model } from '@nozbe/watermelondb';
import { field } from '@nozbe/watermelondb/decorators'; import { field } from '@nozbe/watermelondb/decorators';
export const SLASH_COMMANDS_TABLE = 'slash_commands';
export default class SlashCommand extends Model { export default class SlashCommand extends Model {
static table = 'slash_commands'; static table = SLASH_COMMANDS_TABLE;
@field('params') params; @field('params') params;

View File

@ -3,10 +3,10 @@ import { children, date, field, json } from '@nozbe/watermelondb/decorators';
import { sanitizer } from '../utils'; import { sanitizer } from '../utils';
export const TABLE_NAME = 'subscriptions'; export const SUBSCRIPTIONS_TABLE = 'subscriptions';
export default class Subscription extends Model { export default class Subscription extends Model {
static table = TABLE_NAME; static table = SUBSCRIPTIONS_TABLE;
static associations = { static associations = {
messages: { type: 'has_many', foreignKey: 'rid' }, messages: { type: 'has_many', foreignKey: 'rid' },

View File

@ -3,10 +3,10 @@ import { date, field, json, relation } from '@nozbe/watermelondb/decorators';
import { sanitizer } from '../utils'; import { sanitizer } from '../utils';
export const TABLE_NAME = 'threads'; export const THREADS_TABLE = 'threads';
export default class Thread extends Model { export default class Thread extends Model {
static table = TABLE_NAME; static table = THREADS_TABLE;
static associations = { static associations = {
subscriptions: { type: 'belongs_to', key: 'rid' } subscriptions: { type: 'belongs_to', key: 'rid' }

View File

@ -3,10 +3,10 @@ import { date, field, json, relation } from '@nozbe/watermelondb/decorators';
import { sanitizer } from '../utils'; import { sanitizer } from '../utils';
export const TABLE_NAME = 'thread_messages'; export const THREAD_MESSAGES_TABLE = 'thread_messages';
export default class ThreadMessage extends Model { export default class ThreadMessage extends Model {
static table = TABLE_NAME; static table = THREAD_MESSAGES_TABLE;
static associations = { static associations = {
subscriptions: { type: 'belongs_to', key: 'subscription_id' } subscriptions: { type: 'belongs_to', key: 'subscription_id' }

View File

@ -1,8 +1,10 @@
import { Model } from '@nozbe/watermelondb'; import { Model } from '@nozbe/watermelondb';
import { field, relation } from '@nozbe/watermelondb/decorators'; import { field, relation } from '@nozbe/watermelondb/decorators';
export const UPLOADS_TABLE = 'uploads';
export default class Upload extends Model { export default class Upload extends Model {
static table = 'uploads'; static table = UPLOADS_TABLE;
static associations = { static associations = {
subscriptions: { type: 'belongs_to', key: 'rid' } subscriptions: { type: 'belongs_to', key: 'rid' }

View File

@ -3,8 +3,10 @@ import { field, json } from '@nozbe/watermelondb/decorators';
import { sanitizer } from '../utils'; import { sanitizer } from '../utils';
export const USERS_TABLE = 'users';
export default class User extends Model { export default class User extends Model {
static table = 'users'; static table = USERS_TABLE;
@field('_id') _id; @field('_id') _id;

View File

@ -0,0 +1,16 @@
export * from './CustomEmoji';
export * from './FrequentlyUsedEmoji';
export * from './Message';
export * from './Permission';
export * from './Role';
export * from './Room';
export * from './Setting';
export * from './SlashCommand';
export * from './Subscription';
export * from './Thread';
export * from './ThreadMessage';
export * from './Upload';
export * from './User';
export * from './ServersHistory';
export * from './servers/Server';
export * from './servers/User';

View File

@ -1,8 +1,10 @@
import { Model } from '@nozbe/watermelondb'; import { Model } from '@nozbe/watermelondb';
import { date, field } from '@nozbe/watermelondb/decorators'; import { date, field } from '@nozbe/watermelondb/decorators';
export const SERVERS_TABLE = 'servers';
export default class Server extends Model { export default class Server extends Model {
static table = 'servers'; static table = SERVERS_TABLE;
@field('name') name; @field('name') name;

View File

@ -3,8 +3,10 @@ import { field, json } from '@nozbe/watermelondb/decorators';
import { sanitizer } from '../../utils'; import { sanitizer } from '../../utils';
export const LOGGED_USERS_TABLE = 'users';
export default class User extends Model { export default class User extends Model {
static table = 'users'; static table = LOGGED_USERS_TABLE;
@field('token') token; @field('token') token;

View File

@ -1,7 +1,7 @@
import database from '..'; import database from '..';
import { TABLE_NAME } from '../model/Message'; import { MESSAGES_TABLE } from '../model/Message';
const getCollection = db => db.get(TABLE_NAME); const getCollection = db => db.get(MESSAGES_TABLE);
export const getMessageById = async messageId => { export const getMessageById = async messageId => {
const db = database.active; const db = database.active;

View File

@ -1,7 +1,7 @@
import database from '..'; import database from '..';
import { TABLE_NAME } from '../model/Subscription'; import { SUBSCRIPTIONS_TABLE } from '../model/Subscription';
const getCollection = db => db.get(TABLE_NAME); const getCollection = db => db.get(SUBSCRIPTIONS_TABLE);
export const getSubscriptionByRoomId = async rid => { export const getSubscriptionByRoomId = async rid => {
const db = database.active; const db = database.active;

View File

@ -1,7 +1,7 @@
import database from '..'; import database from '..';
import { TABLE_NAME } from '../model/Thread'; import { THREADS_TABLE } from '../model/Thread';
const getCollection = db => db.get(TABLE_NAME); const getCollection = db => db.get(THREADS_TABLE);
export const getThreadById = async tmid => { export const getThreadById = async tmid => {
const db = database.active; const db = database.active;

View File

@ -1,7 +1,7 @@
import database from '..'; import database from '..';
import { TABLE_NAME } from '../model/ThreadMessage'; import { THREAD_MESSAGES_TABLE } from '../model/ThreadMessage';
const getCollection = db => db.get(TABLE_NAME); const getCollection = db => db.get(THREAD_MESSAGES_TABLE);
export const getThreadMessageById = async messageId => { export const getThreadMessageById = async messageId => {
const db = database.active; const db = database.active;

View File

@ -61,7 +61,7 @@ const PERMISSIONS = [
export async function setPermissions() { export async function setPermissions() {
const db = database.active; const db = database.active;
const permissionsCollection = db.collections.get('permissions'); const permissionsCollection = db.get('permissions');
const allPermissions = await permissionsCollection.query(Q.where('id', Q.oneOf(PERMISSIONS))).fetch(); const allPermissions = await permissionsCollection.query(Q.where('id', Q.oneOf(PERMISSIONS))).fetch();
const parsed = allPermissions.reduce((acc, item) => ({ ...acc, [item.id]: item.roles }), {}); const parsed = allPermissions.reduce((acc, item) => ({ ...acc, [item.id]: item.roles }), {});

View File

@ -8,7 +8,7 @@ import protectedFunction from './helpers/protectedFunction';
export async function setRoles() { export async function setRoles() {
const db = database.active; const db = database.active;
const rolesCollection = db.collections.get('roles'); const rolesCollection = db.get('roles');
const allRoles = await rolesCollection.query().fetch(); const allRoles = await rolesCollection.query().fetch();
const parsed = allRoles.reduce((acc, item) => ({ ...acc, [item.id]: item.description || item.id }), {}); const parsed = allRoles.reduce((acc, item) => ({ ...acc, [item.id]: item.description || item.id }), {});
reduxStore.dispatch(setRolesAction(parsed)); reduxStore.dispatch(setRolesAction(parsed));

View File

@ -31,9 +31,8 @@ const navigate = ({
}; };
interface IItem extends Partial<ISubscription> { interface IItem extends Partial<ISubscription> {
rid: string; search?: boolean; // comes from spotlight
name: string; username?: string;
t: SubscriptionType;
} }
export const goRoom = async ({ export const goRoom = async ({
@ -46,7 +45,7 @@ export const goRoom = async ({
navigationMethod?: any; navigationMethod?: any;
jumpToMessageId?: string; jumpToMessageId?: string;
}): Promise<void> => { }): Promise<void> => {
if (item.t === 'd' && item.search) { if (item.t === SubscriptionType.DIRECT && item?.search) {
// if user is using the search we need first to join/create room // if user is using the search we need first to join/create room
try { try {
const { username } = item; const { username } = item;
@ -55,7 +54,7 @@ export const goRoom = async ({
return navigate({ return navigate({
item: { item: {
rid: result.room._id, rid: result.room._id,
name: username!, name: username,
t: SubscriptionType.DIRECT t: SubscriptionType.DIRECT
}, },
isMasterDetail, isMasterDetail,

View File

@ -22,11 +22,11 @@ import { goRoom } from '../utils/goRoom';
import { showErrorAlert } from '../utils/info'; import { showErrorAlert } from '../utils/info';
import debounce from '../utils/debounce'; import debounce from '../utils/debounce';
import { ChatsStackParamList } from '../stacks/types'; import { ChatsStackParamList } from '../stacks/types';
import { IRoom } from '../definitions/IRoom'; import { TSubscriptionModel, SubscriptionType } from '../definitions';
interface IAddExistingChannelViewState { interface IAddExistingChannelViewState {
search: Array<IRoom>; search: TSubscriptionModel[];
channels: Array<IRoom>; channels: TSubscriptionModel[];
selected: string[]; selected: string[];
loading: boolean; loading: boolean;
} }
@ -83,7 +83,7 @@ class AddExistingChannelView extends React.Component<IAddExistingChannelViewProp
try { try {
const { addTeamChannelPermission } = this.props; const { addTeamChannelPermission } = this.props;
const db = database.active; const db = database.active;
const channels = await db.collections const channels = await db
.get('subscriptions') .get('subscriptions')
.query( .query(
Q.where('team_id', ''), Q.where('team_id', ''),
@ -94,9 +94,9 @@ class AddExistingChannelView extends React.Component<IAddExistingChannelViewProp
) )
.fetch(); .fetch();
const asyncFilter = async (channelsArray: Array<IRoom>) => { const asyncFilter = async (channelsArray: TSubscriptionModel[]) => {
const results = await Promise.all( const results = await Promise.all(
channelsArray.map(async (channel: IRoom) => { channelsArray.map(async channel => {
if (channel.prid) { if (channel.prid) {
return false; return false;
} }
@ -173,11 +173,10 @@ class AddExistingChannelView extends React.Component<IAddExistingChannelViewProp
} }
}; };
// TODO: refactor with Room Model renderItem = ({ item }: { item: TSubscriptionModel }) => {
renderItem = ({ item }: { item: any }) => {
const isChecked = this.isChecked(item.rid); const isChecked = this.isChecked(item.rid);
// TODO: reuse logic inside RoomTypeIcon // TODO: reuse logic inside RoomTypeIcon
const icon = item.t === 'p' && !item.teamId ? 'channel-private' : 'channel-public'; const icon = item.t === SubscriptionType.DIRECT && !item?.teamId ? 'channel-private' : 'channel-public';
return ( return (
<List.Item <List.Item
title={RocketChat.getRoomTitle(item)} title={RocketChat.getRoomTitle(item)}

View File

@ -7,6 +7,7 @@ import RocketChat from '../../lib/rocketchat';
import I18n from '../../i18n'; import I18n from '../../i18n';
import { MultiSelect } from '../../containers/UIKit/MultiSelect'; import { MultiSelect } from '../../containers/UIKit/MultiSelect';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import { TSubscriptionModel } from '../../definitions/ISubscription';
import styles from './styles'; import styles from './styles';
import { ICreateDiscussionViewSelectChannel } from './interfaces'; import { ICreateDiscussionViewSelectChannel } from './interfaces';
@ -20,7 +21,7 @@ const SelectChannel = ({
serverVersion, serverVersion,
theme theme
}: ICreateDiscussionViewSelectChannel): JSX.Element => { }: ICreateDiscussionViewSelectChannel): JSX.Element => {
const [channels, setChannels] = useState([]); const [channels, setChannels] = useState<TSubscriptionModel[]>([]);
const getChannels = debounce(async (keyword = '') => { const getChannels = debounce(async (keyword = '') => {
try { try {

View File

@ -38,11 +38,11 @@ const SelectUsers = ({
const res = await RocketChat.search({ text: keyword, filterRooms: false }); const res = await RocketChat.search({ text: keyword, filterRooms: false });
let items = [ let items = [
...users.filter((u: IUser) => selected.includes(u.name)), ...users.filter((u: IUser) => selected.includes(u.name)),
...res.filter((r: IUser) => !users.find((u: IUser) => u.name === r.name)) ...res.filter(r => !users.find((u: IUser) => u.name === r.name))
]; ];
const records = await usersCollection.query(Q.where('username', Q.oneOf(items.map(u => u.name)))).fetch(); const records = await usersCollection.query(Q.where('username', Q.oneOf(items.map(u => u.name)))).fetch();
items = items.map(item => { items = items.map(item => {
const index = records.findIndex((r: IUser) => r.username === item.name); const index = records.findIndex(r => r.username === item.name);
if (index > -1) { if (index > -1) {
const record = records[index]; const record = records[index];
return { return {

View File

@ -24,6 +24,7 @@ import { createChannelRequest } from '../actions/createChannel';
import { goRoom } from '../utils/goRoom'; import { goRoom } from '../utils/goRoom';
import SafeAreaView from '../containers/SafeAreaView'; import SafeAreaView from '../containers/SafeAreaView';
import { compareServerVersion, methods } from '../lib/utils'; import { compareServerVersion, methods } from '../lib/utils';
import { TSubscriptionModel } from '../definitions';
import sharedStyles from './Styles'; import sharedStyles from './Styles';
const QUERY_SIZE = 50; const QUERY_SIZE = 50;
@ -68,9 +69,8 @@ interface ISearch {
} }
interface INewMessageViewState { interface INewMessageViewState {
search: ISearch[]; search: (ISearch | TSubscriptionModel)[];
// TODO: Refactor when migrate room chats: TSubscriptionModel[];
chats: any[];
permissions: boolean[]; permissions: boolean[];
} }
@ -108,7 +108,7 @@ class NewMessageView extends React.Component<INewMessageViewProps, INewMessageVi
init = async () => { init = async () => {
try { try {
const db = database.active; const db = database.active;
const chats = await db.collections const chats = await db
.get('subscriptions') .get('subscriptions')
.query(Q.where('t', 'd'), Q.experimentalTake(QUERY_SIZE), Q.experimentalSortBy('room_updated_at', Q.desc)) .query(Q.where('t', 'd'), Q.experimentalTake(QUERY_SIZE), Q.experimentalSortBy('room_updated_at', Q.desc))
.fetch(); .fetch();
@ -153,7 +153,7 @@ class NewMessageView extends React.Component<INewMessageViewProps, INewMessageVi
}; };
search = async (text: string) => { search = async (text: string) => {
const result = await RocketChat.search({ text, filterRooms: false }); const result: ISearch[] | TSubscriptionModel[] = await RocketChat.search({ text, filterRooms: false });
this.setState({ this.setState({
search: result search: result
}); });
@ -280,8 +280,7 @@ class NewMessageView extends React.Component<INewMessageViewProps, INewMessageVi
); );
}; };
// TODO: Refactor when migrate room renderItem = ({ item, index }: { item: ISearch | TSubscriptionModel; index: number }) => {
renderItem = ({ item, index }: { item: ISearch | any; index: number }) => {
const { search, chats } = this.state; const { search, chats } = this.state;
const { theme } = this.props; const { theme } = this.props;
@ -295,10 +294,14 @@ class NewMessageView extends React.Component<INewMessageViewProps, INewMessageVi
if (search.length === 0 && index === chats.length - 1) { if (search.length === 0 && index === chats.length - 1) {
style = { ...style, ...sharedStyles.separatorBottom }; style = { ...style, ...sharedStyles.separatorBottom };
} }
const itemSearch = item as ISearch;
const itemModel = item as TSubscriptionModel;
return ( return (
<UserItem <UserItem
name={item.search ? item.name : item.fname} name={itemSearch.search ? itemSearch.name : itemModel.fname || ''}
username={item.search ? item.username : item.name} username={itemSearch.search ? itemSearch.username : itemModel.name}
onPress={() => this.goRoom(item)} onPress={() => this.goRoom(item)}
testID={`new-message-view-item-${item.name}`} testID={`new-message-view-item-${item.name}`}
style={style} style={style}

View File

@ -6,7 +6,7 @@ import { themes } from '../../../constants/colors';
import { CustomIcon } from '../../../lib/Icons'; import { CustomIcon } from '../../../lib/Icons';
import sharedStyles from '../../Styles'; import sharedStyles from '../../Styles';
import Touch from '../../../utils/touch'; import Touch from '../../../utils/touch';
import { TServerHistory } from '../../../definitions/IServerHistory'; import { TServerHistoryModel } from '../../../definitions/IServerHistory';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -28,10 +28,10 @@ const styles = StyleSheet.create({
}); });
interface IItem { interface IItem {
item: TServerHistory; item: TServerHistoryModel;
theme: string; theme: string;
onPress(url: string): void; onPress(url: string): void;
onDelete(item: TServerHistory): void; onDelete(item: TServerHistoryModel): void;
} }
const Item = ({ item, theme, onPress, onDelete }: IItem): JSX.Element => ( const Item = ({ item, theme, onPress, onDelete }: IItem): JSX.Element => (

View File

@ -5,7 +5,7 @@ import TextInput from '../../../containers/TextInput';
import * as List from '../../../containers/List'; import * as List from '../../../containers/List';
import { themes } from '../../../constants/colors'; import { themes } from '../../../constants/colors';
import I18n from '../../../i18n'; import I18n from '../../../i18n';
import { TServerHistory } from '../../../definitions/IServerHistory'; import { TServerHistoryModel } from '../../../definitions/IServerHistory';
import Item from './Item'; import Item from './Item';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
@ -33,8 +33,8 @@ interface IServerInput extends TextInputProps {
theme: string; theme: string;
serversHistory: any[]; serversHistory: any[];
onSubmit(): void; onSubmit(): void;
onDelete(item: TServerHistory): void; onDelete(item: TServerHistoryModel): void;
onPressServerHistory(serverHistory: TServerHistory): void; onPressServerHistory(serverHistory: TServerHistoryModel): void;
} }
const ServerInput = ({ const ServerInput = ({

View File

@ -14,7 +14,7 @@ import Button from '../../containers/Button';
import FormContainer, { FormContainerInner } from '../../containers/FormContainer'; import FormContainer, { FormContainerInner } from '../../containers/FormContainer';
import * as HeaderButton from '../../containers/HeaderButton'; import * as HeaderButton from '../../containers/HeaderButton';
import OrSeparator from '../../containers/OrSeparator'; import OrSeparator from '../../containers/OrSeparator';
import { IBaseScreen, TServerHistory } from '../../definitions'; import { IBaseScreen, TServerHistoryModel } from '../../definitions';
import { withDimensions } from '../../dimensions'; import { withDimensions } from '../../dimensions';
import I18n from '../../i18n'; import I18n from '../../i18n';
import database from '../../lib/database'; import database from '../../lib/database';
@ -77,7 +77,7 @@ interface INewServerViewState {
text: string; text: string;
connectingOpen: boolean; connectingOpen: boolean;
certificate: any; certificate: any;
serversHistory: TServerHistory[]; serversHistory: TServerHistoryModel[];
} }
interface ISubmitParams { interface ISubmitParams {
@ -153,7 +153,7 @@ class NewServerView extends React.Component<INewServerViewProps, INewServerViewS
const likeString = sanitizeLikeString(text); const likeString = sanitizeLikeString(text);
whereClause = [...whereClause, Q.where('url', Q.like(`%${likeString}%`))]; whereClause = [...whereClause, Q.where('url', Q.like(`%${likeString}%`))];
} }
const serversHistory = (await serversHistoryCollection.query(...whereClause).fetch()) as TServerHistory[]; const serversHistory = await serversHistoryCollection.query(...whereClause).fetch();
this.setState({ serversHistory }); this.setState({ serversHistory });
} catch { } catch {
// Do nothing // Do nothing
@ -177,7 +177,7 @@ class NewServerView extends React.Component<INewServerViewProps, INewServerViewS
dispatch(serverRequest(server)); dispatch(serverRequest(server));
}; };
onPressServerHistory = (serverHistory: TServerHistory) => { onPressServerHistory = (serverHistory: TServerHistoryModel) => {
this.setState({ text: serverHistory.url }, () => this.submit({ fromServerHistory: true, username: serverHistory?.username })); this.setState({ text: serverHistory.url }, () => this.submit({ fromServerHistory: true, username: serverHistory?.username }));
}; };
@ -269,14 +269,14 @@ class NewServerView extends React.Component<INewServerViewProps, INewServerViewS
}); });
}; };
deleteServerHistory = async (item: TServerHistory) => { deleteServerHistory = async (item: TServerHistoryModel) => {
const db = database.servers; const db = database.servers;
try { try {
await db.write(async () => { await db.write(async () => {
await item.destroyPermanently(); await item.destroyPermanently();
}); });
this.setState((prevstate: INewServerViewState) => ({ this.setState((prevstate: INewServerViewState) => ({
serversHistory: prevstate.serversHistory.filter((server: TServerHistory) => server.id !== item.id) serversHistory: prevstate.serversHistory.filter(server => server.id !== item.id)
})); }));
} catch { } catch {
// Nothing // Nothing

View File

@ -80,7 +80,7 @@ class NotificationPreferencesView extends React.Component<INotificationPreferenc
const db = database.active; const db = database.active;
try { try {
await db.action(async () => { await db.write(async () => {
await room.update( await room.update(
protectedFunction((r: any) => { protectedFunction((r: any) => {
r[key] = value; r[key] = value;
@ -97,7 +97,7 @@ class NotificationPreferencesView extends React.Component<INotificationPreferenc
// do nothing // do nothing
} }
await db.action(async () => { await db.write(async () => {
await room.update( await room.update(
protectedFunction((r: any) => { protectedFunction((r: any) => {
r[key] = room[key]; r[key] = room[key];

View File

@ -643,7 +643,7 @@ class RoomActionsView extends React.Component {
const { addTeamChannelPermission, createTeamPermission } = this.props; const { addTeamChannelPermission, createTeamPermission } = this.props;
const QUERY_SIZE = 50; const QUERY_SIZE = 50;
const db = database.active; const db = database.active;
const teams = await db.collections const teams = await db
.get('subscriptions') .get('subscriptions')
.query( .query(
Q.where('team_main', true), Q.where('team_main', true),

View File

@ -144,11 +144,11 @@ class ListContainer extends React.Component {
if (tmid) { if (tmid) {
try { try {
this.thread = await db.collections.get('threads').find(tmid); this.thread = await db.get('threads').find(tmid);
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
this.messagesObservable = db.collections this.messagesObservable = db
.get('thread_messages') .get('thread_messages')
.query(Q.where('rid', tmid), Q.experimentalSortBy('ts', Q.desc), Q.experimentalSkip(0), Q.experimentalTake(this.count)) .query(Q.where('rid', tmid), Q.experimentalSortBy('ts', Q.desc), Q.experimentalSkip(0), Q.experimentalTake(this.count))
.observe(); .observe();
@ -162,7 +162,7 @@ class ListContainer extends React.Component {
if (!showMessageInMainThread) { if (!showMessageInMainThread) {
whereClause.push(Q.or(Q.where('tmid', null), Q.where('tshow', Q.eq(true)))); whereClause.push(Q.or(Q.where('tmid', null), Q.where('tshow', Q.eq(true))));
} }
this.messagesObservable = db.collections this.messagesObservable = db
.get('messages') .get('messages')
.query(...whereClause) .query(...whereClause)
.observe(); .observe();

View File

@ -91,7 +91,7 @@ class UploadProgress extends Component {
} }
const db = database.active; const db = database.active;
this.uploadsObservable = db.collections.get('uploads').query(Q.where('rid', rid)).observeWithColumns(['progress', 'error']); this.uploadsObservable = db.get('uploads').query(Q.where('rid', rid)).observeWithColumns(['progress', 'error']);
this.uploadsSubscription = this.uploadsObservable.subscribe(uploads => { this.uploadsSubscription = this.uploadsObservable.subscribe(uploads => {
if (this.mounted) { if (this.mounted) {

View File

@ -690,7 +690,7 @@ class RoomView extends React.Component {
// eslint-disable-next-line react/sort-comp // eslint-disable-next-line react/sort-comp
updateUnreadCount = async () => { updateUnreadCount = async () => {
const db = database.active; const db = database.active;
const observable = await db.collections const observable = await db
.get('subscriptions') .get('subscriptions')
.query(Q.where('archived', false), Q.where('open', true), Q.where('rid', Q.notEq(this.rid))) .query(Q.where('archived', false), Q.where('open', true), Q.where('rid', Q.notEq(this.rid)))
.observeWithColumns(['unread']); .observeWithColumns(['unread']);

View File

@ -47,7 +47,7 @@ class ServerDropdown extends Component {
async componentDidMount() { async componentDidMount() {
const serversDB = database.servers; const serversDB = database.servers;
const observable = await serversDB.collections.get('servers').query().observeWithColumns(['name']); const observable = await serversDB.get('servers').query().observeWithColumns(['name']);
this.subscription = observable.subscribe(data => { this.subscription = observable.subscribe(data => {
this.setState({ servers: data }); this.setState({ servers: data });

View File

@ -450,7 +450,7 @@ class RoomsListView extends React.Component {
// When we're grouping by something // When we're grouping by something
if (this.isGrouping) { if (this.isGrouping) {
observable = await db.collections observable = await db
.get('subscriptions') .get('subscriptions')
.query(...defaultWhereClause) .query(...defaultWhereClause)
.observeWithColumns(['alert']); .observeWithColumns(['alert']);
@ -458,7 +458,7 @@ class RoomsListView extends React.Component {
// When we're NOT grouping // When we're NOT grouping
} else { } else {
this.count += QUERY_SIZE; this.count += QUERY_SIZE;
observable = await db.collections observable = await db
.get('subscriptions') .get('subscriptions')
.query(...defaultWhereClause, Q.experimentalSkip(0), Q.experimentalTake(this.count)) .query(...defaultWhereClause, Q.experimentalSkip(0), Q.experimentalTake(this.count))
.observe(); .observe();

View File

@ -92,7 +92,7 @@ class ScreenLockConfigView extends React.Component<IScreenLockConfigViewProps, I
const serversDB = database.servers; const serversDB = database.servers;
const serversCollection = serversDB.get('servers'); const serversCollection = serversDB.get('servers');
try { try {
this.serverRecord = (await serversCollection.find(server)) as TServerModel; this.serverRecord = await serversCollection.find(server);
this.setState({ this.setState({
autoLock: this.serverRecord?.autoLock, autoLock: this.serverRecord?.autoLock,
autoLockTime: this.serverRecord?.autoLockTime === null ? DEFAULT_AUTO_LOCK : this.serverRecord?.autoLockTime, autoLockTime: this.serverRecord?.autoLockTime === null ? DEFAULT_AUTO_LOCK : this.serverRecord?.autoLockTime,
@ -115,7 +115,7 @@ class ScreenLockConfigView extends React.Component<IScreenLockConfigViewProps, I
*/ */
observe = () => { observe = () => {
this.observable = this.serverRecord?.observe()?.subscribe(({ biometry }) => { this.observable = this.serverRecord?.observe()?.subscribe(({ biometry }) => {
this.setState({ biometry }); this.setState({ biometry: !!biometry });
}); });
}; };

View File

@ -109,10 +109,7 @@ class SelectedUsersView extends React.Component<ISelectedUsersViewProps, ISelect
init = async () => { init = async () => {
try { try {
const db = database.active; const db = database.active;
const observable = await db.collections const observable = await db.get('subscriptions').query(Q.where('t', 'd')).observeWithColumns(['room_updated_at']);
.get('subscriptions')
.query(Q.where('t', 'd'))
.observeWithColumns(['room_updated_at']);
// TODO: Refactor when migrate room // TODO: Refactor when migrate room
this.querySubscription = observable.subscribe((data: any) => { this.querySubscription = observable.subscribe((data: any) => {
@ -129,7 +126,9 @@ class SelectedUsersView extends React.Component<ISelectedUsersViewProps, ISelect
} }
search = async (text: string) => { search = async (text: string) => {
const result = await RocketChat.search({ text, filterRooms: false }); // TODO: When migrate rocketchat.js pass the param IUser to there and the return should be
// IUser | TSubscriptionModel, this because we do a local search too
const result = (await RocketChat.search({ text, filterRooms: false })) as ISelectedUser[];
this.setState({ this.setState({
search: result search: result
}); });

View File

@ -7,7 +7,6 @@ import { EdgeInsets, withSafeAreaInsets } from 'react-native-safe-area-context';
import { HeaderBackButton, StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack'; import { HeaderBackButton, StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
import { RouteProp } from '@react-navigation/native'; import { RouteProp } from '@react-navigation/native';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import Model from '@nozbe/watermelondb/Model';
import Database from '@nozbe/watermelondb/Database'; import Database from '@nozbe/watermelondb/Database';
import ActivityIndicator from '../../containers/ActivityIndicator'; import ActivityIndicator from '../../containers/ActivityIndicator';
@ -86,7 +85,7 @@ class ThreadMessagesView extends React.Component<IThreadMessagesViewProps, IThre
private messagesSubscription?: Subscription; private messagesSubscription?: Subscription;
private messagesObservable!: Observable<Model>; private messagesObservable?: Observable<TThreadModel[]>;
constructor(props: IThreadMessagesViewProps) { constructor(props: IThreadMessagesViewProps) {
super(props); super(props);
@ -188,7 +187,7 @@ class ThreadMessagesView extends React.Component<IThreadMessagesViewProps, IThre
const db = database.active; const db = database.active;
// subscription query // subscription query
const subscription = (await db.collections.get('subscriptions').find(this.rid)) as TSubscriptionModel; const subscription = await db.get('subscriptions').find(this.rid);
const observable = subscription.observe(); const observable = subscription.observe();
this.subSubscription = observable.subscribe(data => { this.subSubscription = observable.subscribe(data => {
this.setState({ subscription: data }); this.setState({ subscription: data });
@ -214,15 +213,15 @@ class ThreadMessagesView extends React.Component<IThreadMessagesViewProps, IThre
whereClause.push(Q.where('msg', Q.like(`%${sanitizeLikeString(searchText.trim())}%`))); whereClause.push(Q.where('msg', Q.like(`%${sanitizeLikeString(searchText.trim())}%`)));
} }
this.messagesObservable = db.collections this.messagesObservable = db
.get('threads') .get('threads')
.query(...whereClause) .query(...whereClause)
.observeWithColumns(['updated_at']); .observeWithColumns(['updated_at']);
// TODO: Refactor when migrate messages // TODO: Refactor when migrate messages
this.messagesSubscription = this.messagesObservable.subscribe((messages: any) => { this.messagesSubscription = this.messagesObservable.subscribe(messages => {
const { currentFilter } = this.state; const { currentFilter } = this.state;
const displayingThreads = this.getFilteredThreads(messages, subscription!, currentFilter); const displayingThreads = this.getFilteredThreads(messages, subscription, currentFilter);
if (this.mounted) { if (this.mounted) {
this.setState({ messages, displayingThreads }); this.setState({ messages, displayingThreads });
} else { } else {
@ -419,14 +418,14 @@ class ThreadMessagesView extends React.Component<IThreadMessagesViewProps, IThre
}; };
// helper to query threads // helper to query threads
getFilteredThreads = (messages: any, subscription: TSubscriptionModel, currentFilter?: Filter): TThreadModel[] => { getFilteredThreads = (messages: TThreadModel[], subscription?: TSubscriptionModel, currentFilter?: Filter): TThreadModel[] => {
// const { currentFilter } = this.state; // const { currentFilter } = this.state;
const { user } = this.props; 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)); return messages?.filter(item => 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?.filter(item => subscription?.tunread?.includes(item?.id));
} }
return messages; return messages;
}; };