Merge branch 'develop' into new.add-discusions-roomactionsview

This commit is contained in:
Gerzon Z 2022-01-28 10:59:02 -04:00 committed by GitHub
commit 5a320a806c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 1096 additions and 640 deletions

View File

@ -1,20 +0,0 @@
import * as types from './actionsTypes';
export function connectRequest() {
return {
type: types.METEOR.REQUEST
};
}
export function connectSuccess() {
return {
type: types.METEOR.SUCCESS
};
}
export function disconnect(err) {
return {
type: types.METEOR.DISCONNECT,
err
};
}

21
app/actions/connect.ts Normal file
View File

@ -0,0 +1,21 @@
import { Action } from 'redux';
import * as types from './actionsTypes';
export function connectRequest(): Action {
return {
type: types.METEOR.REQUEST
};
}
export function connectSuccess(): Action {
return {
type: types.METEOR.SUCCESS
};
}
export function disconnect(): Action {
return {
type: types.METEOR.DISCONNECT
};
}

View File

@ -1,8 +0,0 @@
import * as types from './actionsTypes';
export function setCustomEmojis(emojis) {
return {
type: types.SET_CUSTOM_EMOJIS,
emojis
};
}

View File

@ -0,0 +1,17 @@
import { Action } from 'redux';
import { ICustomEmojis } from '../reducers/customEmojis';
import { SET_CUSTOM_EMOJIS } from './actionsTypes';
export interface ISetCustomEmojis extends Action {
emojis: ICustomEmojis;
}
export type TActionCustomEmojis = ISetCustomEmojis;
export function setCustomEmojis(emojis: ICustomEmojis): ISetCustomEmojis {
return {
type: SET_CUSTOM_EMOJIS,
emojis
};
}

View File

@ -1,8 +0,0 @@
import * as types from './actionsTypes';
export function deepLinkingOpen(params) {
return {
type: types.DEEP_LINKING.OPEN,
params
};
}

View File

@ -0,0 +1,25 @@
import { Action } from 'redux';
import { DEEP_LINKING } from './actionsTypes';
interface IParams {
path: string;
rid: string;
messageId: string;
host: string;
isCall: boolean;
fullURL: string;
type: string;
token: string;
}
interface IDeepLinkingOpen extends Action {
params: Partial<IParams>;
}
export function deepLinkingOpen(params: Partial<IParams>): IDeepLinkingOpen {
return {
type: DEEP_LINKING.OPEN,
params
};
}

View File

@ -1,35 +0,0 @@
import * as types from './actionsTypes';
export function encryptionInit() {
return {
type: types.ENCRYPTION.INIT
};
}
export function encryptionStop() {
return {
type: types.ENCRYPTION.STOP
};
}
export function encryptionSet(enabled = false, banner = null) {
return {
type: types.ENCRYPTION.SET,
enabled,
banner
};
}
export function encryptionSetBanner(banner) {
return {
type: types.ENCRYPTION.SET_BANNER,
banner
};
}
export function encryptionDecodeKey(password) {
return {
type: types.ENCRYPTION.DECODE_KEY,
password
};
}

52
app/actions/encryption.ts Normal file
View File

@ -0,0 +1,52 @@
import { Action } from 'redux';
import { IBanner } from '../reducers/encryption';
import { ENCRYPTION } from './actionsTypes';
export interface IEncryptionSet extends Action {
enabled: boolean;
banner: IBanner;
}
export interface IEncryptionSetBanner extends Action {
banner: IBanner;
}
export interface IEncryptionDecodeKey extends Action {
password: string;
}
export type TActionEncryption = IEncryptionSet & IEncryptionSetBanner & IEncryptionDecodeKey;
export function encryptionInit(): Action {
return {
type: ENCRYPTION.INIT
};
}
export function encryptionStop(): Action {
return {
type: ENCRYPTION.STOP
};
}
export function encryptionSet(enabled = false, banner: IBanner = ''): IEncryptionSet {
return {
type: ENCRYPTION.SET,
enabled,
banner
};
}
export function encryptionSetBanner(banner: IBanner = ''): IEncryptionSetBanner {
return {
type: ENCRYPTION.SET_BANNER,
banner
};
}
export function encryptionDecodeKey(password: string): IEncryptionDecodeKey {
return {
type: ENCRYPTION.DECODE_KEY,
password
};
}

View File

@ -1,54 +0,0 @@
import * as types from './actionsTypes';
export function inviteLinksSetToken(token) {
return {
type: types.INVITE_LINKS.SET_TOKEN,
token
};
}
export function inviteLinksRequest(token) {
return {
type: types.INVITE_LINKS.REQUEST,
token
};
}
export function inviteLinksSuccess() {
return {
type: types.INVITE_LINKS.SUCCESS
};
}
export function inviteLinksFailure() {
return {
type: types.INVITE_LINKS.FAILURE
};
}
export function inviteLinksClear() {
return {
type: types.INVITE_LINKS.CLEAR
};
}
export function inviteLinksCreate(rid) {
return {
type: types.INVITE_LINKS.CREATE,
rid
};
}
export function inviteLinksSetParams(params) {
return {
type: types.INVITE_LINKS.SET_PARAMS,
params
};
}
export function inviteLinksSetInvite(invite) {
return {
type: types.INVITE_LINKS.SET_INVITE,
invite
};
}

View File

@ -0,0 +1,61 @@
import { Action } from 'redux';
import { TInvite } from '../reducers/inviteLinks';
import { INVITE_LINKS } from './actionsTypes';
interface IInviteLinksGeneric extends Action {
token: string;
}
interface IInviteLinksCreate extends Action {
rid: string;
}
interface IInviteLinksSetInvite extends Action {
invite: TInvite;
}
type TParams = Record<string, any>;
interface IInviteLinksSetParams extends Action {
params: TParams;
}
export type TActionInviteLinks = IInviteLinksGeneric & IInviteLinksCreate & IInviteLinksSetInvite & IInviteLinksSetParams;
export const inviteLinksSetToken = (token: string): IInviteLinksGeneric => ({
type: INVITE_LINKS.SET_TOKEN,
token
});
export const inviteLinksRequest = (token: string): IInviteLinksGeneric => ({
type: INVITE_LINKS.REQUEST,
token
});
export const inviteLinksSuccess = (): Action => ({
type: INVITE_LINKS.SUCCESS
});
export const inviteLinksFailure = (): Action => ({
type: INVITE_LINKS.FAILURE
});
export const inviteLinksClear = (): Action => ({
type: INVITE_LINKS.CLEAR
});
export const inviteLinksCreate = (rid: string): IInviteLinksCreate => ({
type: INVITE_LINKS.CREATE,
rid
});
export const inviteLinksSetParams = (params: TParams): IInviteLinksSetParams => ({
type: INVITE_LINKS.SET_PARAMS,
params
});
export const inviteLinksSetInvite = (invite: TInvite): IInviteLinksSetInvite => ({
type: INVITE_LINKS.SET_INVITE,
invite
});

View File

@ -1,8 +0,0 @@
import * as types from './actionsTypes';
export function replyBroadcast(message) {
return {
type: types.MESSAGES.REPLY_BROADCAST,
message
};
}

16
app/actions/messages.ts Normal file
View File

@ -0,0 +1,16 @@
import { Action } from 'redux';
import { MESSAGES } from './actionsTypes';
type IMessage = Record<string, string>;
interface IReplyBroadcast extends Action {
message: IMessage;
}
export function replyBroadcast(message: IMessage): IReplyBroadcast {
return {
type: MESSAGES.REPLY_BROADCAST,
message
};
}

View File

@ -1,20 +0,0 @@
import * as types from './actionsTypes';
export function setRoles(roles) {
return {
type: types.ROLES.SET,
roles
};
}
export function updateRoles(id, desc) {
return {
type: types.ROLES.UPDATE,
payload: { id, desc }
};
}
export function removeRoles(id) {
return {
type: types.ROLES.REMOVE,
payload: { id }
};
}

39
app/actions/roles.ts Normal file
View File

@ -0,0 +1,39 @@
import { Action } from 'redux';
import { IRoles } from '../reducers/roles';
import { ROLES } from './actionsTypes';
export interface ISetRoles extends Action {
roles: IRoles;
}
export interface IUpdateRoles extends Action {
payload: { id: string; desc: string };
}
export interface IRemoveRoles extends Action {
payload: { id: string };
}
export type IActionRoles = ISetRoles & IUpdateRoles & IRemoveRoles;
export function setRoles(roles: IRoles): ISetRoles {
return {
type: ROLES.SET,
roles
};
}
export function updateRoles(id: string, desc: string): IUpdateRoles {
return {
type: ROLES.UPDATE,
payload: { id, desc }
};
}
export function removeRoles(id: string): IRemoveRoles {
return {
type: ROLES.REMOVE,
payload: { id }
};
}

View File

@ -1,58 +0,0 @@
import * as types from './actionsTypes';
export function roomsRequest(params = { allData: false }) {
return {
type: types.ROOMS.REQUEST,
params
};
}
export function roomsSuccess() {
return {
type: types.ROOMS.SUCCESS
};
}
export function roomsFailure(err) {
return {
type: types.ROOMS.FAILURE,
err
};
}
export function roomsRefresh() {
return {
type: types.ROOMS.REFRESH
};
}
export function setSearch(searchText) {
return {
type: types.ROOMS.SET_SEARCH,
searchText
};
}
export function closeServerDropdown() {
return {
type: types.ROOMS.CLOSE_SERVER_DROPDOWN
};
}
export function toggleServerDropdown() {
return {
type: types.ROOMS.TOGGLE_SERVER_DROPDOWN
};
}
export function openSearchHeader() {
return {
type: types.ROOMS.OPEN_SEARCH_HEADER
};
}
export function closeSearchHeader() {
return {
type: types.ROOMS.CLOSE_SEARCH_HEADER
};
}

78
app/actions/rooms.ts Normal file
View File

@ -0,0 +1,78 @@
import { Action } from 'redux';
import { ROOMS } from './actionsTypes';
export interface IRoomsRequest extends Action {
params: any;
}
export interface ISetSearch extends Action {
searchText: string;
}
export interface IRoomsFailure extends Action {
err: Record<string, any> | string;
}
export type IRoomsAction = IRoomsRequest & ISetSearch & IRoomsFailure;
export function roomsRequest(
params: {
allData: boolean;
} = { allData: false }
): IRoomsRequest {
return {
type: ROOMS.REQUEST,
params
};
}
export function roomsSuccess(): Action {
return {
type: ROOMS.SUCCESS
};
}
export function roomsFailure(err: string): IRoomsFailure {
return {
type: ROOMS.FAILURE,
err
};
}
export function roomsRefresh(): Action {
return {
type: ROOMS.REFRESH
};
}
export function setSearch(searchText: string): ISetSearch {
return {
type: ROOMS.SET_SEARCH,
searchText
};
}
export function closeServerDropdown(): Action {
return {
type: ROOMS.CLOSE_SERVER_DROPDOWN
};
}
export function toggleServerDropdown(): Action {
return {
type: ROOMS.TOGGLE_SERVER_DROPDOWN
};
}
export function openSearchHeader(): Action {
return {
type: ROOMS.OPEN_SEARCH_HEADER
};
}
export function closeSearchHeader(): Action {
return {
type: ROOMS.CLOSE_SEARCH_HEADER
};
}

View File

@ -24,11 +24,12 @@ export function selectServerFailure() {
}; };
} }
export function serverRequest(server, username = null, fromServerHistory = false) { // TODO
export function serverRequest(server, username, fromServerHistory = false) {
return { return {
type: SERVER.REQUEST, type: SERVER.REQUEST,
server, server,
username, username: username || null,
fromServerHistory fromServerHistory
}; };
} }

View File

@ -1,21 +0,0 @@
import { SETTINGS } from './actionsTypes';
export function addSettings(settings) {
return {
type: SETTINGS.ADD,
payload: settings
};
}
export function updateSettings(id, value) {
return {
type: SETTINGS.UPDATE,
payload: { id, value }
};
}
export function clearSettings() {
return {
type: SETTINGS.CLEAR
};
}

34
app/actions/settings.ts Normal file
View File

@ -0,0 +1,34 @@
import { Action } from 'redux';
import { ISettings, TSettings } from '../reducers/settings';
import { SETTINGS } from './actionsTypes';
interface IAddSettings extends Action {
payload: ISettings;
}
interface IUpdateSettings extends Action {
payload: { id: string; value: TSettings };
}
export type IActionSettings = IAddSettings & IUpdateSettings;
export function addSettings(settings: ISettings): IAddSettings {
return {
type: SETTINGS.ADD,
payload: settings
};
}
export function updateSettings(id: string, value: TSettings): IUpdateSettings {
return {
type: SETTINGS.UPDATE,
payload: { id, value }
};
}
export function clearSettings(): Action {
return {
type: SETTINGS.CLEAR
};
}

View File

@ -1,15 +0,0 @@
import * as types from './actionsTypes';
export function setAllPreferences(preferences) {
return {
type: types.SORT_PREFERENCES.SET_ALL,
preferences
};
}
export function setPreference(preference) {
return {
type: types.SORT_PREFERENCES.SET,
preference
};
}

View File

@ -0,0 +1,28 @@
import { Action } from 'redux';
import { IPreferences } from '../definitions';
import { SORT_PREFERENCES } from './actionsTypes';
interface ISetAllPreferences extends Action {
preferences: IPreferences;
}
interface ISetPreference extends Action {
preference: Partial<IPreferences>;
}
export type TActionSortPreferences = ISetAllPreferences & ISetPreference;
export function setAllPreferences(preferences: IPreferences): ISetAllPreferences {
return {
type: SORT_PREFERENCES.SET_ALL,
preferences
};
}
export function setPreference(preference: Partial<IPreferences>): ISetPreference {
return {
type: SORT_PREFERENCES.SET,
preference
};
}

View File

@ -1,21 +0,0 @@
import { USERS_TYPING } from './actionsTypes';
export function addUserTyping(username) {
return {
type: USERS_TYPING.ADD,
username
};
}
export function removeUserTyping(username) {
return {
type: USERS_TYPING.REMOVE,
username
};
}
export function clearUserTyping() {
return {
type: USERS_TYPING.CLEAR
};
}

View File

@ -0,0 +1,29 @@
import { Action } from 'redux';
import { USERS_TYPING } from './actionsTypes';
export interface IUsersTypingGenericAction extends Action {
username: string;
}
export type TActionUserTyping = IUsersTypingGenericAction & Action;
export function addUserTyping(username: string): IUsersTypingGenericAction {
return {
type: USERS_TYPING.ADD,
username
};
}
export function removeUserTyping(username: string): IUsersTypingGenericAction {
return {
type: USERS_TYPING.REMOVE,
username
};
}
export function clearUserTyping(): Action {
return {
type: USERS_TYPING.CLEAR
};
}

View File

@ -1,10 +1,11 @@
import { dequal } from 'dequal';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { dequal } from 'dequal';
import RoomHeader from './RoomHeader'; import { IApplicationState } from '../../definitions';
import { withDimensions } from '../../dimensions'; import { withDimensions } from '../../dimensions';
import I18n from '../../i18n'; import I18n from '../../i18n';
import RoomHeader from './RoomHeader';
interface IRoomHeaderContainerProps { interface IRoomHeaderContainerProps {
title: string; title: string;
@ -122,8 +123,8 @@ class RoomHeaderContainer extends Component<IRoomHeaderContainerProps, any> {
} }
} }
const mapStateToProps = (state: any, ownProps: any) => { const mapStateToProps = (state: IApplicationState, ownProps: any) => {
let statusText; let statusText = '';
let status = 'offline'; let status = 'offline';
const { roomUserId, type, visitor = {}, tmid } = ownProps; const { roomUserId, type, visitor = {}, tmid } = ownProps;

View File

@ -0,0 +1,10 @@
import { SortBy, DisplayMode } from '../constants/constantDisplayMode';
export interface IPreferences {
sortBy: SortBy;
groupByType: boolean;
showFavorites: boolean;
showUnread: boolean;
showAvatar: boolean;
displayMode: DisplayMode;
}

View File

@ -8,6 +8,8 @@ export * from './INotification';
export * from './IRoom'; export * from './IRoom';
export * from './IServer'; export * from './IServer';
export * from './ISubscription'; export * from './ISubscription';
export * from './IPreferences';
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> {
navigation: StackNavigationProp<T, S>; navigation: StackNavigationProp<T, S>;

View File

@ -1,13 +1,26 @@
import { TActionSelectedUsers } from '../../actions/selectedUsers';
import { TActionActiveUsers } from '../../actions/activeUsers'; import { TActionActiveUsers } from '../../actions/activeUsers';
import { TActionCustomEmojis } from '../../actions/customEmojis';
import { TActionEncryption } from '../../actions/encryption';
import { TActionInviteLinks } from '../../actions/inviteLinks';
import { IActionRoles } from '../../actions/roles';
import { TActionSelectedUsers } from '../../actions/selectedUsers';
import { IActionSettings } from '../../actions/settings';
import { TActionSortPreferences } from '../../actions/sortPreferences';
import { TActionUserTyping } from '../../actions/usersTyping';
// REDUCERS // REDUCERS
import { IActiveUsers } from '../../reducers/activeUsers'; import { IActiveUsers } from '../../reducers/activeUsers';
import { IEncryption } from '../../reducers/encryption';
import { IInviteLinks } from '../../reducers/inviteLinks';
import { IRoles } from '../../reducers/roles';
import { ISelectedUsers } from '../../reducers/selectedUsers'; import { ISelectedUsers } from '../../reducers/selectedUsers';
import { IConnect } from '../../reducers/connect';
import { ISettings } from '../../reducers/settings';
export interface IApplicationState { export interface IApplicationState {
settings: any; settings: ISettings;
login: any; login: any;
meteor: any; meteor: IConnect;
server: any; server: any;
selectedUsers: ISelectedUsers; selectedUsers: ISelectedUsers;
createChannel: any; createChannel: any;
@ -19,13 +32,21 @@ export interface IApplicationState {
customEmojis: any; customEmojis: any;
activeUsers: IActiveUsers; activeUsers: IActiveUsers;
usersTyping: any; usersTyping: any;
inviteLinks: any; inviteLinks: IInviteLinks;
createDiscussion: any; createDiscussion: any;
inquiry: any; inquiry: any;
enterpriseModules: any; enterpriseModules: any;
encryption: any; encryption: IEncryption;
permissions: any; permissions: any;
roles: any; roles: IRoles;
} }
export type TApplicationActions = TActionActiveUsers & TActionSelectedUsers; export type TApplicationActions = TActionActiveUsers &
TActionSelectedUsers &
TActionCustomEmojis &
TActionInviteLinks &
IActionRoles &
IActionSettings &
TActionEncryption &
TActionSortPreferences &
TActionUserTyping;

View File

@ -4,7 +4,7 @@ import { SET_ACTIVE_USERS } from '../actions/actionsTypes';
type TUserStatus = 'online' | 'offline'; type TUserStatus = 'online' | 'offline';
export interface IActiveUser { export interface IActiveUser {
status: TUserStatus; status: TUserStatus;
statusText?: string; statusText: string;
} }
export interface IActiveUsers { export interface IActiveUsers {

View File

@ -0,0 +1,28 @@
import { connectRequest, connectSuccess, disconnect } from '../actions/connect';
import { initialState } from './connect';
import { mockedStore } from './mockedStore';
describe('test reducer', () => {
it('should return initial state', () => {
const { meteor } = mockedStore.getState();
expect(meteor).toEqual(initialState);
});
it('should return correct meteor state after dispatch connectRequest action', () => {
mockedStore.dispatch(connectRequest());
const { meteor } = mockedStore.getState();
expect(meteor).toEqual({ connecting: true, connected: false });
});
it('should return correct meteor state after dispatch connectSuccess action', () => {
mockedStore.dispatch(connectSuccess());
const { meteor } = mockedStore.getState();
expect(meteor).toEqual({ connecting: false, connected: true });
});
it('should return correct meteor state after dispatch disconnect action', () => {
mockedStore.dispatch(disconnect());
const { meteor } = mockedStore.getState();
expect(meteor).toEqual(initialState);
});
});

View File

@ -1,11 +1,18 @@
import { Action } from 'redux';
import { METEOR } from '../actions/actionsTypes'; import { METEOR } from '../actions/actionsTypes';
const initialState = { export interface IConnect {
connecting: boolean;
connected: boolean;
}
export const initialState: IConnect = {
connecting: false, connecting: false,
connected: false connected: false
}; };
export default function connect(state = initialState, action) { export default function connect(state = initialState, action: Action): IConnect {
switch (action.type) { switch (action.type) {
case METEOR.REQUEST: case METEOR.REQUEST:
return { return {

View File

@ -1,14 +0,0 @@
import { SET_CUSTOM_EMOJIS } from '../actions/actionsTypes';
const initialState = {
customEmojis: {}
};
export default function customEmojis(state = initialState, action) {
switch (action.type) {
case SET_CUSTOM_EMOJIS:
return action.emojis;
default:
return state;
}
}

View File

@ -0,0 +1,16 @@
import { setCustomEmojis } from '../actions/customEmojis';
import { ICustomEmojis, initialState } from './customEmojis';
import { mockedStore } from './mockedStore';
describe('test reducer', () => {
it('should return initial state', () => {
const state = mockedStore.getState().customEmojis;
expect(state).toEqual(initialState);
});
it('should return modified store after action', () => {
const emojis: ICustomEmojis = { dog: { name: 'dog', extension: 'jpg' }, cat: { name: 'cat', extension: 'jpg' } };
mockedStore.dispatch(setCustomEmojis(emojis));
const state = mockedStore.getState().customEmojis;
expect(state).toEqual(emojis);
});
});

View File

@ -0,0 +1,23 @@
import { SET_CUSTOM_EMOJIS } from '../actions/actionsTypes';
import { TApplicationActions } from '../definitions';
// There are at least three interfaces for emoji, but none of them includes only this data.
interface IEmoji {
name: string;
extension: string;
}
export interface ICustomEmojis {
[key: string]: IEmoji;
}
export const initialState: ICustomEmojis = {};
export default function customEmojis(state = initialState, action: TApplicationActions): ICustomEmojis {
switch (action.type) {
case SET_CUSTOM_EMOJIS:
return action.emojis;
default:
return state;
}
}

View File

@ -0,0 +1,28 @@
import { encryptionSet, encryptionInit, encryptionSetBanner } from '../actions/encryption';
import { mockedStore } from './mockedStore';
import { initialState } from './encryption';
describe('test encryption reducer', () => {
it('should return initial state', () => {
const state = mockedStore.getState().encryption;
expect(state).toEqual(initialState);
});
it('should return modified store after encryptionSet', () => {
mockedStore.dispatch(encryptionSet(true, 'BANNER'));
const state = mockedStore.getState().encryption;
expect(state).toEqual({ banner: 'BANNER', enabled: true });
});
it('should return empty store after encryptionInit', () => {
mockedStore.dispatch(encryptionInit());
const state = mockedStore.getState().encryption;
expect(state).toEqual({ banner: '', enabled: false });
});
it('should return initial state after encryptionSetBanner', () => {
mockedStore.dispatch(encryptionSetBanner('BANNER_NEW'));
const state = mockedStore.getState().encryption;
expect(state).toEqual({ banner: 'BANNER_NEW', enabled: false });
});
});

View File

@ -1,11 +1,18 @@
import { ENCRYPTION } from '../actions/actionsTypes'; import { ENCRYPTION } from '../actions/actionsTypes';
import { TApplicationActions } from '../definitions';
const initialState = { export type IBanner = string;
export interface IEncryption {
enabled: boolean;
banner: IBanner;
}
export const initialState: IEncryption = {
enabled: false, enabled: false,
banner: null banner: ''
}; };
export default function encryption(state = initialState, action) { export default function encryption(state = initialState, action: TApplicationActions): IEncryption {
switch (action.type) { switch (action.type) {
case ENCRYPTION.SET: case ENCRYPTION.SET:
return { return {

View File

@ -0,0 +1,72 @@
import {
inviteLinksClear,
inviteLinksFailure,
inviteLinksRequest,
inviteLinksSetInvite,
inviteLinksSetParams,
inviteLinksSetToken,
inviteLinksSuccess
} from '../actions/inviteLinks';
import { initialState } from './inviteLinks';
import { mockedStore } from './mockedStore';
describe('test roles reducer', () => {
const invite = {
_id: 'nZestg',
days: 1,
maxUses: 0,
createdAt: '2022-01-17T20:32:44.695Z',
expires: '2022-01-18T20:32:44.695Z',
uses: 0,
_updatedAt: '2022-01-17T20:32:44.695Z',
url: 'https://go.rocket.chat/invite?host=open.rocket.chat&path=invite%2FnZestg',
success: true,
token: ''
};
it('should return initial state', () => {
const state = mockedStore.getState().inviteLinks;
expect(state).toEqual(initialState);
});
it('should return initialState after call inviteLinksFailure', () => {
mockedStore.dispatch(inviteLinksFailure());
const state = mockedStore.getState().inviteLinks;
expect(state).toEqual(initialState);
});
it('should return initialState after call inviteLinksSuccess', () => {
mockedStore.dispatch(inviteLinksSuccess());
const state = mockedStore.getState().inviteLinks;
expect(state).toEqual(initialState);
});
it('should return correctly token after call inviteLinksSetToken', () => {
mockedStore.dispatch(inviteLinksSetToken('xxx'));
const { token } = mockedStore.getState().inviteLinks;
expect(token).toEqual('xxx');
});
it('should return correctly invite value after call inviteLinksSetInvite', () => {
mockedStore.dispatch(inviteLinksSetInvite(invite));
const state = mockedStore.getState().inviteLinks;
expect(state.invite).toEqual(invite);
});
it('should return modified store after call inviteLinksSetParams', () => {
mockedStore.dispatch(inviteLinksSetParams({ token: 'nZestg' }));
const { token } = mockedStore.getState().inviteLinks;
expect(token).toEqual('nZestg');
});
it('should return initialState after call inviteLinksClear', () => {
mockedStore.dispatch(inviteLinksClear());
const state = mockedStore.getState().inviteLinks;
expect(state).toEqual(initialState);
});
it('should return actual state after call inviteLinksRequest', () => {
mockedStore.dispatch(inviteLinksRequest('xxx'));
const state = mockedStore.getState().inviteLinks;
expect(state).toEqual(initialState);
});
});

View File

@ -1,18 +1,26 @@
import { TActionInviteLinks } from '../actions/inviteLinks';
import { INVITE_LINKS } from '../actions/actionsTypes'; import { INVITE_LINKS } from '../actions/actionsTypes';
const initialState = { export type TInvite = { url: string; expires: string; maxUses: number; uses: number; [x: string]: any };
export interface IInviteLinks {
token: string;
days: number;
maxUses: number;
invite: TInvite;
}
export const initialState: IInviteLinks = {
token: '', token: '',
days: 1, days: 1,
maxUses: 0, maxUses: 0,
invite: {} invite: { url: '', expires: '', maxUses: 0, uses: 0 }
}; };
export default (state = initialState, action) => { export default (state = initialState, action: TActionInviteLinks): IInviteLinks => {
switch (action.type) { switch (action.type) {
case INVITE_LINKS.SET_TOKEN: case INVITE_LINKS.SET_TOKEN:
return { return { ...state, token: action.token };
token: action.token
};
case INVITE_LINKS.SET_PARAMS: case INVITE_LINKS.SET_PARAMS:
return { return {
...state, ...state,

View File

@ -0,0 +1,35 @@
import { setRoles, updateRoles, removeRoles } from '../actions/roles';
import { mockedStore } from './mockedStore';
import { initialState } from './roles';
describe('test roles reducer', () => {
it('should return initial state', () => {
const state = mockedStore.getState().roles;
expect(state).toEqual(initialState);
});
it('should return modified store after call setRoles action', () => {
const roles = { admin: 'enabled', user: 'enabled', dog: 'carlitos' };
mockedStore.dispatch(setRoles(roles));
const state = mockedStore.getState().roles;
expect(state.admin).toEqual('enabled');
expect(state.user).toEqual('enabled');
expect(state.dog).toEqual('carlitos');
});
it('should return modified store after call updateRoles action', () => {
mockedStore.dispatch(updateRoles('admin', 'disabled'));
const state = mockedStore.getState().roles;
expect(state.admin).toEqual('disabled');
expect(state.user).toEqual('enabled');
expect(state.dog).toEqual('carlitos');
});
it('should return modified store after call removeRoles action', () => {
mockedStore.dispatch(removeRoles('dog'));
const state = mockedStore.getState().roles;
expect(state.admin).toEqual('disabled');
expect(state.user).toEqual('enabled');
expect(state.dog).toEqual(undefined);
});
});

View File

@ -1,8 +1,11 @@
import { ROLES } from '../actions/actionsTypes'; import { ROLES } from '../actions/actionsTypes';
import { IActionRoles } from '../actions/roles';
const initialState = {}; export type IRoles = Record<string, string>;
export default function permissions(state = initialState, action) { export const initialState: IRoles = {};
export default function roles(state = initialState, action: IActionRoles): IRoles {
switch (action.type) { switch (action.type) {
case ROLES.SET: case ROLES.SET:
return action.roles; return action.roles;

View File

@ -0,0 +1,77 @@
import {
closeSearchHeader,
closeServerDropdown,
openSearchHeader,
roomsFailure,
roomsRefresh,
roomsRequest,
roomsSuccess,
setSearch,
toggleServerDropdown
} from '../actions/rooms';
import { mockedStore } from './mockedStore';
import { initialState } from './rooms';
describe('test selectedUsers reducer', () => {
it('should return initial state', () => {
const state = mockedStore.getState().rooms;
expect(state).toEqual(initialState);
});
it('should return modified store after call roomsRequest', () => {
mockedStore.dispatch(roomsRequest());
const state = mockedStore.getState().rooms;
const manipulated = { ...initialState, isFetching: true, failure: false, errorMessage: {} };
expect(state).toEqual(manipulated);
});
it('should return modified store after call roomsSuccess', () => {
mockedStore.dispatch(roomsSuccess());
const state = mockedStore.getState().rooms;
const manipulated = { ...initialState, isFetching: false, refreshing: false };
expect(state).toEqual(manipulated);
});
it('should return modified store after call roomsRefresh', () => {
mockedStore.dispatch(roomsRefresh());
const state = mockedStore.getState().rooms;
const manipulated = { ...initialState, isFetching: true, refreshing: true };
expect(state).toEqual(manipulated);
});
it('should return modified store after call setSearch', () => {
mockedStore.dispatch(setSearch('dog'));
const state = mockedStore.getState().rooms;
expect(state.searchText).toEqual('dog');
});
it('should return modified store after call closeServerDropdown', () => {
mockedStore.dispatch(closeServerDropdown());
const state = mockedStore.getState().rooms;
expect(state.closeServerDropdown).toEqual(!initialState.closeServerDropdown);
});
it('should return modified store after call toggleServerDropdown', () => {
mockedStore.dispatch(toggleServerDropdown());
const state = mockedStore.getState().rooms;
expect(state.showServerDropdown).toEqual(!initialState.showServerDropdown);
});
it('should return modified store after call openSearchHeader', () => {
mockedStore.dispatch(openSearchHeader());
const state = mockedStore.getState().rooms;
expect(state.showSearchHeader).toEqual(true);
});
it('should return modified store after call closeSearchHeader', () => {
mockedStore.dispatch(closeSearchHeader());
const state = mockedStore.getState().rooms;
expect(state.showSearchHeader).toEqual(false);
});
it('should return modified store after call roomsFailure', () => {
mockedStore.dispatch(roomsFailure('error'));
const state = mockedStore.getState().rooms;
expect(state.errorMessage).toEqual('error');
});
});

View File

@ -1,6 +1,18 @@
import * as types from '../actions/actionsTypes'; import { IRoomsAction } from '../actions/rooms';
import { ROOMS } from '../actions/actionsTypes';
const initialState = { export interface IRooms {
isFetching: boolean;
refreshing: boolean;
failure: boolean;
errorMessage: Record<string, any> | string;
searchText: string;
showServerDropdown: boolean;
closeServerDropdown: boolean;
showSearchHeader: boolean;
}
export const initialState: IRooms = {
isFetching: false, isFetching: false,
refreshing: false, refreshing: false,
failure: false, failure: false,
@ -11,22 +23,22 @@ const initialState = {
showSearchHeader: false showSearchHeader: false
}; };
export default function login(state = initialState, action) { export default function rooms(state = initialState, action: IRoomsAction): IRooms {
switch (action.type) { switch (action.type) {
case types.ROOMS.REQUEST: case ROOMS.REQUEST:
return { return {
...state, ...state,
isFetching: true, isFetching: true,
failure: false, failure: false,
errorMessage: {} errorMessage: {}
}; };
case types.ROOMS.SUCCESS: case ROOMS.SUCCESS:
return { return {
...state, ...state,
isFetching: false, isFetching: false,
refreshing: false refreshing: false
}; };
case types.ROOMS.FAILURE: case ROOMS.FAILURE:
return { return {
...state, ...state,
isFetching: false, isFetching: false,
@ -34,33 +46,33 @@ export default function login(state = initialState, action) {
failure: true, failure: true,
errorMessage: action.err errorMessage: action.err
}; };
case types.ROOMS.REFRESH: case ROOMS.REFRESH:
return { return {
...state, ...state,
isFetching: true, isFetching: true,
refreshing: true refreshing: true
}; };
case types.ROOMS.SET_SEARCH: case ROOMS.SET_SEARCH:
return { return {
...state, ...state,
searchText: action.searchText searchText: action.searchText
}; };
case types.ROOMS.CLOSE_SERVER_DROPDOWN: case ROOMS.CLOSE_SERVER_DROPDOWN:
return { return {
...state, ...state,
closeServerDropdown: !state.closeServerDropdown closeServerDropdown: !state.closeServerDropdown
}; };
case types.ROOMS.TOGGLE_SERVER_DROPDOWN: case ROOMS.TOGGLE_SERVER_DROPDOWN:
return { return {
...state, ...state,
showServerDropdown: !state.showServerDropdown showServerDropdown: !state.showServerDropdown
}; };
case types.ROOMS.OPEN_SEARCH_HEADER: case ROOMS.OPEN_SEARCH_HEADER:
return { return {
...state, ...state,
showSearchHeader: true showSearchHeader: true
}; };
case types.ROOMS.CLOSE_SEARCH_HEADER: case ROOMS.CLOSE_SEARCH_HEADER:
return { return {
...state, ...state,
showSearchHeader: false showSearchHeader: false

View File

@ -0,0 +1,31 @@
import { addSettings, clearSettings, updateSettings } from '../actions/settings';
import { mockedStore } from './mockedStore';
import { initialState } from './settings';
describe('test settings reducer', () => {
it('should return initial state', () => {
const state = mockedStore.getState().settings;
expect(state).toEqual(initialState);
});
const settings = { API_Use_REST_For_DDP_Calls: true, FileUpload_MaxFileSize: 600857600, Jitsi_URL_Room_Prefix: 'RocketChat' };
it('should return modified store after call addSettings action', () => {
mockedStore.dispatch(addSettings(settings));
const state = mockedStore.getState().settings;
expect(state).toEqual(settings);
});
it('should return correctly settings after call updateSettings action', () => {
const id = 'Jitsi_URL_Room_Prefix';
mockedStore.dispatch(updateSettings(id, 'ChatRocket'));
const state = mockedStore.getState().settings;
expect(state[id]).toEqual('ChatRocket');
});
it('should return initial state after clearSettings', () => {
mockedStore.dispatch(clearSettings());
const state = mockedStore.getState().settings;
expect(state).toEqual({});
});
});

View File

@ -1,8 +1,13 @@
import { IActionSettings } from '../actions/settings';
import { SETTINGS } from '../actions/actionsTypes'; import { SETTINGS } from '../actions/actionsTypes';
const initialState = {}; export type TSettings = string | number | boolean;
export default (state = initialState, action) => { export type ISettings = Record<string, TSettings>;
export const initialState: ISettings = {};
export default (state = initialState, action: IActionSettings): ISettings => {
switch (action.type) { switch (action.type) {
case SETTINGS.ADD: case SETTINGS.ADD:
return { return {

View File

@ -0,0 +1,35 @@
import { IPreferences } from '../definitions';
import { setAllPreferences, setPreference } from '../actions/sortPreferences';
import { mockedStore } from './mockedStore';
import { initialState } from './sortPreferences';
import { DisplayMode, SortBy } from '../constants/constantDisplayMode';
describe('test sortPreferences reducer', () => {
it('should return initial state', () => {
const state = mockedStore.getState().sortPreferences;
expect(state).toEqual(initialState);
});
it('should return correctly value after call setPreference action', () => {
const preferences: IPreferences = {
displayMode: DisplayMode.Condensed,
groupByType: true,
showAvatar: true,
showFavorites: true,
showUnread: true,
sortBy: SortBy.Activity
};
mockedStore.dispatch(setAllPreferences(preferences));
const state = mockedStore.getState().sortPreferences;
expect(state).toEqual(preferences);
});
it('should return correctly value after call setPreference action', () => {
const preference: Partial<IPreferences> = {
displayMode: DisplayMode.Expanded
};
mockedStore.dispatch(setPreference(preference));
const { displayMode } = mockedStore.getState().sortPreferences;
expect(displayMode).toEqual(DisplayMode.Expanded);
});
});

View File

@ -1,7 +1,8 @@
import { SORT_PREFERENCES } from '../actions/actionsTypes'; import { SORT_PREFERENCES } from '../actions/actionsTypes';
import { DisplayMode, SortBy } from '../constants/constantDisplayMode'; import { DisplayMode, SortBy } from '../constants/constantDisplayMode';
import { IPreferences, TApplicationActions } from '../definitions';
const initialState = { export const initialState: IPreferences = {
sortBy: SortBy.Activity, sortBy: SortBy.Activity,
groupByType: false, groupByType: false,
showFavorites: false, showFavorites: false,
@ -10,7 +11,7 @@ const initialState = {
displayMode: DisplayMode.Expanded displayMode: DisplayMode.Expanded
}; };
export default (state = initialState, action) => { export default (state = initialState, action: TApplicationActions): IPreferences => {
switch (action.type) { switch (action.type) {
case SORT_PREFERENCES.SET_ALL: case SORT_PREFERENCES.SET_ALL:
return { return {

View File

@ -0,0 +1,30 @@
import { addUserTyping, removeUserTyping, clearUserTyping } from '../actions/usersTyping';
import { mockedStore } from './mockedStore';
import { initialState } from './usersTyping';
describe('test usersTyping reducer', () => {
it('should return initial state', () => {
const state = mockedStore.getState().usersTyping;
expect(state).toEqual(initialState);
});
it('should return modified store after addUserTyping', () => {
mockedStore.dispatch(addUserTyping('diego'));
mockedStore.dispatch(addUserTyping('carlos'));
mockedStore.dispatch(addUserTyping('maria'));
const state = mockedStore.getState().usersTyping;
expect(state).toEqual(['diego', 'carlos', 'maria']);
});
it('should return modified store after removeUserTyping', () => {
mockedStore.dispatch(removeUserTyping('diego'));
const state = mockedStore.getState().usersTyping;
expect(state).toEqual(['carlos', 'maria']);
});
it('should return initial state after reset', () => {
mockedStore.dispatch(clearUserTyping());
const state = mockedStore.getState().usersTyping;
expect(state).toEqual(initialState);
});
});

View File

@ -1,8 +1,11 @@
import { USERS_TYPING } from '../actions/actionsTypes'; import { USERS_TYPING } from '../actions/actionsTypes';
import { TApplicationActions } from '../definitions';
const initialState = []; export type IUsersTyping = string[];
export default function usersTyping(state = initialState, action) { export const initialState: IUsersTyping = [];
export default function usersTyping(state = initialState, action: TApplicationActions): IUsersTyping {
switch (action.type) { switch (action.type) {
case USERS_TYPING.ADD: case USERS_TYPING.ADD:
if (state.findIndex(item => item === action.username) === -1) { if (state.findIndex(item => item === action.username) === -1) {

View File

@ -1,31 +1,23 @@
import { StackNavigationProp } from '@react-navigation/stack';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { Switch } from 'react-native'; import { Switch } from 'react-native';
import { RadioButton } from 'react-native-ui-lib'; import { RadioButton } from 'react-native-ui-lib';
import { StackNavigationProp } from '@react-navigation/stack';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { setPreference } from '../actions/sortPreferences'; import { setPreference } from '../actions/sortPreferences';
import RocketChat from '../lib/rocketchat';
import StatusBar from '../containers/StatusBar';
import I18n from '../i18n';
import * as List from '../containers/List';
import { useTheme } from '../theme';
import { themes } from '../constants/colors'; import { themes } from '../constants/colors';
import * as HeaderButton from '../containers/HeaderButton';
import SafeAreaView from '../containers/SafeAreaView';
import { ICON_SIZE } from '../containers/List/constants';
import log, { events, logEvent } from '../utils/log';
import { DisplayMode, SortBy } from '../constants/constantDisplayMode'; import { DisplayMode, SortBy } from '../constants/constantDisplayMode';
import * as HeaderButton from '../containers/HeaderButton';
import * as List from '../containers/List';
import { ICON_SIZE } from '../containers/List/constants';
import SafeAreaView from '../containers/SafeAreaView';
import StatusBar from '../containers/StatusBar';
import { IApplicationState, IPreferences } from '../definitions';
import I18n from '../i18n';
import RocketChat from '../lib/rocketchat';
import { SettingsStackParamList } from '../stacks/types'; import { SettingsStackParamList } from '../stacks/types';
import { useTheme } from '../theme';
interface IParam { import log, { events, logEvent } from '../utils/log';
sortBy: SortBy;
groupByType: boolean;
showFavorites: boolean;
showUnread: boolean;
showAvatar: boolean;
displayMode: DisplayMode;
}
interface IDisplayPrefsView { interface IDisplayPrefsView {
navigation: StackNavigationProp<SettingsStackParamList, 'DisplayPrefsView'>; navigation: StackNavigationProp<SettingsStackParamList, 'DisplayPrefsView'>;
@ -36,7 +28,7 @@ const DisplayPrefsView = (props: IDisplayPrefsView): JSX.Element => {
const { theme } = useTheme(); const { theme } = useTheme();
const { sortBy, groupByType, showFavorites, showUnread, showAvatar, displayMode } = useSelector( const { sortBy, groupByType, showFavorites, showUnread, showAvatar, displayMode } = useSelector(
(state: any) => state.sortPreferences (state: IApplicationState) => state.sortPreferences
); );
const { isMasterDetail } = useSelector((state: any) => state.app); const { isMasterDetail } = useSelector((state: any) => state.app);
const dispatch = useDispatch(); const dispatch = useDispatch();
@ -53,7 +45,7 @@ const DisplayPrefsView = (props: IDisplayPrefsView): JSX.Element => {
} }
}, []); }, []);
const setSortPreference = async (param: Partial<IParam>) => { const setSortPreference = async (param: Partial<IPreferences>) => {
try { try {
dispatch(setPreference(param)); dispatch(setPreference(param));
await RocketChat.saveSortPreference(param); await RocketChat.saveSortPreference(param);

View File

@ -1,23 +1,23 @@
import { StackNavigationOptions } from '@react-navigation/stack';
import React from 'react'; import React from 'react';
import { ScrollView, StyleSheet, Text } from 'react-native'; import { ScrollView, StyleSheet, Text } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
import I18n from '../i18n'; import { encryptionDecodeKey } from '../actions/encryption';
import { withTheme } from '../theme';
import Button from '../containers/Button';
import { themes } from '../constants/colors'; import { themes } from '../constants/colors';
import TextInput from '../containers/TextInput'; import Button from '../containers/Button';
import SafeAreaView from '../containers/SafeAreaView';
import * as HeaderButton from '../containers/HeaderButton'; import * as HeaderButton from '../containers/HeaderButton';
import { encryptionDecodeKey as encryptionDecodeKeyAction } from '../actions/encryption'; import SafeAreaView from '../containers/SafeAreaView';
import scrollPersistTaps from '../utils/scrollPersistTaps';
import KeyboardView from '../presentation/KeyboardView';
import StatusBar from '../containers/StatusBar'; import StatusBar from '../containers/StatusBar';
import { events, logEvent } from '../utils/log'; import TextInput from '../containers/TextInput';
import sharedStyles from './Styles'; import { IBaseScreen } from '../definitions';
import I18n from '../i18n';
import KeyboardView from '../presentation/KeyboardView';
import { E2EEnterYourPasswordStackParamList } from '../stacks/types'; import { E2EEnterYourPasswordStackParamList } from '../stacks/types';
import { withTheme } from '../theme';
import { events, logEvent } from '../utils/log';
import scrollPersistTaps from '../utils/scrollPersistTaps';
import sharedStyles from './Styles';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -34,21 +34,17 @@ interface IE2EEnterYourPasswordViewState {
password: string; password: string;
} }
interface IE2EEnterYourPasswordViewProps { type TE2EEnterYourPasswordViewProps = IBaseScreen<E2EEnterYourPasswordStackParamList, 'E2EEnterYourPasswordView'>;
encryptionDecodeKey: (password: string) => void;
theme: string;
navigation: StackNavigationProp<E2EEnterYourPasswordStackParamList, 'E2EEnterYourPasswordView'>;
}
class E2EEnterYourPasswordView extends React.Component<IE2EEnterYourPasswordViewProps, IE2EEnterYourPasswordViewState> { class E2EEnterYourPasswordView extends React.Component<TE2EEnterYourPasswordViewProps, IE2EEnterYourPasswordViewState> {
private passwordInput?: TextInput; private passwordInput?: TextInput;
static navigationOptions = ({ navigation }: Pick<IE2EEnterYourPasswordViewProps, 'navigation'>): StackNavigationOptions => ({ static navigationOptions = ({ navigation }: Pick<TE2EEnterYourPasswordViewProps, 'navigation'>): StackNavigationOptions => ({
headerLeft: () => <HeaderButton.CloseModal navigation={navigation} testID='e2e-enter-your-password-view-close' />, headerLeft: () => <HeaderButton.CloseModal navigation={navigation} testID='e2e-enter-your-password-view-close' />,
title: I18n.t('Enter_Your_E2E_Password') title: I18n.t('Enter_Your_E2E_Password')
}); });
constructor(props: IE2EEnterYourPasswordViewProps) { constructor(props: TE2EEnterYourPasswordViewProps) {
super(props); super(props);
this.state = { this.state = {
password: '' password: ''
@ -58,8 +54,8 @@ class E2EEnterYourPasswordView extends React.Component<IE2EEnterYourPasswordView
submit = () => { submit = () => {
logEvent(events.E2E_ENTER_PW_SUBMIT); logEvent(events.E2E_ENTER_PW_SUBMIT);
const { password } = this.state; const { password } = this.state;
const { encryptionDecodeKey } = this.props; const { dispatch } = this.props;
encryptionDecodeKey(password); dispatch(encryptionDecodeKey(password));
}; };
render() { render() {
@ -109,7 +105,4 @@ class E2EEnterYourPasswordView extends React.Component<IE2EEnterYourPasswordView
} }
} }
const mapDispatchToProps = (dispatch: Dispatch) => ({ export default connect(null)(withTheme(E2EEnterYourPasswordView));
encryptionDecodeKey: (password: string) => dispatch(encryptionDecodeKeyAction(password))
});
export default connect(null, mapDispatchToProps)(withTheme(E2EEnterYourPasswordView));

View File

@ -1,25 +1,24 @@
import React from 'react'; import React from 'react';
import { StackNavigationProp } from '@react-navigation/stack';
import { Dispatch } from 'redux';
import { connect } from 'react-redux';
import { Clipboard, ScrollView, StyleSheet, Text, View } from 'react-native'; import { Clipboard, ScrollView, StyleSheet, Text, View } from 'react-native';
import { connect } from 'react-redux';
import { encryptionSetBanner as encryptionSetBannerAction } from '../actions/encryption'; import { encryptionSetBanner } from '../actions/encryption';
import { E2E_RANDOM_PASSWORD_KEY } from '../lib/encryption/constants'; import { themes } from '../constants/colors';
import Button from '../containers/Button';
import * as HeaderButton from '../containers/HeaderButton'; import * as HeaderButton from '../containers/HeaderButton';
import scrollPersistTaps from '../utils/scrollPersistTaps';
import SafeAreaView from '../containers/SafeAreaView'; import SafeAreaView from '../containers/SafeAreaView';
import UserPreferences from '../lib/userPreferences';
import { events, logEvent } from '../utils/log';
import StatusBar from '../containers/StatusBar'; import StatusBar from '../containers/StatusBar';
import { LISTENER } from '../containers/Toast'; import { LISTENER } from '../containers/Toast';
import { themes } from '../constants/colors'; import { IApplicationState, IBaseScreen } from '../definitions';
import EventEmitter from '../utils/events';
import Button from '../containers/Button';
import { withTheme } from '../theme';
import I18n from '../i18n'; import I18n from '../i18n';
import sharedStyles from './Styles'; import { E2E_RANDOM_PASSWORD_KEY } from '../lib/encryption/constants';
import UserPreferences from '../lib/userPreferences';
import { E2ESaveYourPasswordStackParamList } from '../stacks/types'; import { E2ESaveYourPasswordStackParamList } from '../stacks/types';
import { withTheme } from '../theme';
import EventEmitter from '../utils/events';
import { events, logEvent } from '../utils/log';
import scrollPersistTaps from '../utils/scrollPersistTaps';
import sharedStyles from './Styles';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -59,11 +58,8 @@ interface IE2ESaveYourPasswordViewState {
password: string; password: string;
} }
interface IE2ESaveYourPasswordViewProps { interface IE2ESaveYourPasswordViewProps extends IBaseScreen<E2ESaveYourPasswordStackParamList, 'E2ESaveYourPasswordView'> {
server: string; server: string;
navigation: StackNavigationProp<E2ESaveYourPasswordStackParamList, 'E2ESaveYourPasswordView'>;
encryptionSetBanner(): void;
theme: string;
} }
class E2ESaveYourPasswordView extends React.Component<IE2ESaveYourPasswordViewProps, IE2ESaveYourPasswordViewState> { class E2ESaveYourPasswordView extends React.Component<IE2ESaveYourPasswordViewProps, IE2ESaveYourPasswordViewState> {
@ -103,11 +99,11 @@ class E2ESaveYourPasswordView extends React.Component<IE2ESaveYourPasswordViewPr
onSaved = async () => { onSaved = async () => {
logEvent(events.E2E_SAVE_PW_SAVED); logEvent(events.E2E_SAVE_PW_SAVED);
const { navigation, server, encryptionSetBanner } = this.props; const { navigation, server, dispatch } = this.props;
// Remove stored password // Remove stored password
await UserPreferences.removeItem(`${server}-${E2E_RANDOM_PASSWORD_KEY}`); await UserPreferences.removeItem(`${server}-${E2E_RANDOM_PASSWORD_KEY}`);
// Hide encryption banner // Hide encryption banner
encryptionSetBanner(); dispatch(encryptionSetBanner());
navigation.pop(); navigation.pop();
}; };
@ -173,10 +169,8 @@ class E2ESaveYourPasswordView extends React.Component<IE2ESaveYourPasswordViewPr
} }
} }
const mapStateToProps = (state: any) => ({ const mapStateToProps = (state: IApplicationState) => ({
server: state.server.server server: state.server.server
}); });
const mapDispatchToProps = (dispatch: Dispatch) => ({
encryptionSetBanner: () => dispatch(encryptionSetBannerAction()) export default connect(mapStateToProps)(withTheme(E2ESaveYourPasswordView));
});
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(E2ESaveYourPasswordView));

View File

@ -1,25 +1,21 @@
import { StackNavigationOptions } from '@react-navigation/stack';
import React from 'react'; import React from 'react';
import { TextInputProps, View } from 'react-native'; import { TextInputProps, View } from 'react-native';
import { connect } from 'react-redux';
import RNPickerSelect from 'react-native-picker-select'; import RNPickerSelect from 'react-native-picker-select';
import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack'; import { connect } from 'react-redux';
import { RouteProp } from '@react-navigation/core';
import { Dispatch } from 'redux';
import { import { inviteLinksCreate, inviteLinksSetParams } from '../../actions/inviteLinks';
inviteLinksCreate as inviteLinksCreateAction,
inviteLinksSetParams as inviteLinksSetParamsAction
} from '../../actions/inviteLinks';
import * as List from '../../containers/List';
import Button from '../../containers/Button';
import I18n from '../../i18n';
import StatusBar from '../../containers/StatusBar';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import { withTheme } from '../../theme'; import Button from '../../containers/Button';
import * as List from '../../containers/List';
import SafeAreaView from '../../containers/SafeAreaView'; import SafeAreaView from '../../containers/SafeAreaView';
import StatusBar from '../../containers/StatusBar';
import { IApplicationState, IBaseScreen } from '../../definitions';
import I18n from '../../i18n';
import { ChatsStackParamList } from '../../stacks/types';
import { withTheme } from '../../theme';
import { events, logEvent } from '../../utils/log'; import { events, logEvent } from '../../utils/log';
import styles from './styles'; import styles from './styles';
import { ChatsStackParamList } from '../../stacks/types';
const OPTIONS = { const OPTIONS = {
days: [ days: [
@ -68,12 +64,7 @@ const OPTIONS = {
] ]
}; };
interface IInviteUsersEditViewProps { interface IInviteUsersEditViewProps extends IBaseScreen<ChatsStackParamList, 'InviteUsersEditView'> {
navigation: StackNavigationProp<ChatsStackParamList, 'InviteUsersEditView'>;
route: RouteProp<ChatsStackParamList, 'InviteUsersEditView'>;
theme: string;
createInviteLink(rid: string): void;
inviteLinksSetParams(params: { [key: string]: number }): void;
days: number; days: number;
maxUses: number; maxUses: number;
} }
@ -91,18 +82,18 @@ class InviteUsersEditView extends React.Component<IInviteUsersEditViewProps, any
} }
onValueChangePicker = (key: string, value: number) => { onValueChangePicker = (key: string, value: number) => {
const { dispatch } = this.props;
logEvent(events.IU_EDIT_SET_LINK_PARAM); logEvent(events.IU_EDIT_SET_LINK_PARAM);
const { inviteLinksSetParams } = this.props;
const params = { const params = {
[key]: value [key]: value
}; };
inviteLinksSetParams(params); dispatch(inviteLinksSetParams(params));
}; };
createInviteLink = () => { createInviteLink = () => {
const { dispatch, navigation } = this.props;
logEvent(events.IU_EDIT_CREATE_LINK); logEvent(events.IU_EDIT_CREATE_LINK);
const { createInviteLink, navigation } = this.props; dispatch(inviteLinksCreate(this.rid));
createInviteLink(this.rid);
navigation.pop(); navigation.pop();
}; };
@ -151,14 +142,9 @@ class InviteUsersEditView extends React.Component<IInviteUsersEditViewProps, any
} }
} }
const mapStateToProps = (state: any) => ({ const mapStateToProps = (state: IApplicationState) => ({
days: state.inviteLinks.days, days: state.inviteLinks.days,
maxUses: state.inviteLinks.maxUses maxUses: state.inviteLinks.maxUses
}); });
const mapDispatchToProps = (dispatch: Dispatch) => ({ export default connect(mapStateToProps)(withTheme(InviteUsersEditView));
inviteLinksSetParams: (params: object) => dispatch(inviteLinksSetParamsAction(params)),
createInviteLink: (rid: string) => dispatch(inviteLinksCreateAction(rid))
});
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(InviteUsersEditView));

View File

@ -1,62 +1,49 @@
import { StackNavigationOptions } from '@react-navigation/stack';
import moment from 'moment';
import React from 'react'; import React from 'react';
import { ScrollView, Share, View } from 'react-native'; import { ScrollView, Share, View } from 'react-native';
import moment from 'moment';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { StackNavigationProp, StackNavigationOptions } from '@react-navigation/stack';
import { RouteProp } from '@react-navigation/core';
import { Dispatch } from 'redux';
import { ChatsStackParamList } from '../../stacks/types'; import { inviteLinksClear, inviteLinksCreate } from '../../actions/inviteLinks';
import {
inviteLinksClear as inviteLinksClearAction,
inviteLinksCreate as inviteLinksCreateAction
} from '../../actions/inviteLinks';
import RCTextInput from '../../containers/TextInput';
import Markdown from '../../containers/markdown';
import Button from '../../containers/Button';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
import I18n from '../../i18n';
import StatusBar from '../../containers/StatusBar';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import { withTheme } from '../../theme'; import Button from '../../containers/Button';
import Markdown from '../../containers/markdown';
import SafeAreaView from '../../containers/SafeAreaView'; import SafeAreaView from '../../containers/SafeAreaView';
import StatusBar from '../../containers/StatusBar';
import RCTextInput from '../../containers/TextInput';
import { IApplicationState, IBaseScreen } from '../../definitions';
import I18n from '../../i18n';
import { TInvite } from '../../reducers/inviteLinks';
import { ChatsStackParamList } from '../../stacks/types';
import { withTheme } from '../../theme';
import { events, logEvent } from '../../utils/log'; import { events, logEvent } from '../../utils/log';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
import styles from './styles'; import styles from './styles';
interface IInviteUsersViewProps { interface IInviteUsersViewProps extends IBaseScreen<ChatsStackParamList, 'InviteUsersView'> {
navigation: StackNavigationProp<ChatsStackParamList, 'InviteUsersView'>;
route: RouteProp<ChatsStackParamList, 'InviteUsersView'>;
theme: string;
timeDateFormat: string; timeDateFormat: string;
invite: { invite: TInvite;
url: string;
expires: number;
maxUses: number;
uses: number;
};
createInviteLink(rid: string): void;
clearInviteLink(): void;
} }
class InviteUsersView extends React.Component<IInviteUsersViewProps, any> { class InviteUsersView extends React.Component<IInviteUsersViewProps, any> {
private rid: string;
static navigationOptions = (): StackNavigationOptions => ({ static navigationOptions = (): StackNavigationOptions => ({
title: I18n.t('Invite_users') title: I18n.t('Invite_users')
}); });
private rid: string;
constructor(props: IInviteUsersViewProps) { constructor(props: IInviteUsersViewProps) {
super(props); super(props);
this.rid = props.route.params?.rid; this.rid = props.route.params?.rid;
} }
componentDidMount() { componentDidMount() {
const { createInviteLink } = this.props; const { dispatch } = this.props;
createInviteLink(this.rid); dispatch(inviteLinksCreate(this.rid));
} }
componentWillUnmount() { componentWillUnmount() {
const { clearInviteLink } = this.props; const { dispatch } = this.props;
clearInviteLink(); dispatch(inviteLinksClear());
} }
share = () => { share = () => {
@ -133,16 +120,9 @@ class InviteUsersView extends React.Component<IInviteUsersViewProps, any> {
} }
} }
const mapStateToProps = (state: any) => ({ const mapStateToProps = (state: IApplicationState) => ({
timeDateFormat: state.settings.Message_TimeAndDateFormat, timeDateFormat: state.settings.Message_TimeAndDateFormat as string,
days: state.inviteLinks.days,
maxUses: state.inviteLinks.maxUses,
invite: state.inviteLinks.invite invite: state.inviteLinks.invite
}); });
const mapDispatchToProps = (dispatch: Dispatch) => ({ export default connect(mapStateToProps)(withTheme(InviteUsersView));
createInviteLink: (rid: string) => dispatch(inviteLinksCreateAction(rid)),
clearInviteLink: () => dispatch(inviteLinksClearAction())
});
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(InviteUsersView));

View File

@ -1,39 +1,37 @@
import React from 'react';
import { Text, Keyboard, StyleSheet, View, BackHandler, Image } from 'react-native';
import { connect } from 'react-redux';
import { Base64 } from 'js-base64';
import parse from 'url-parse';
import { Q } from '@nozbe/watermelondb'; import { Q } from '@nozbe/watermelondb';
import { Base64 } from 'js-base64';
import React from 'react';
import { BackHandler, Image, Keyboard, StyleSheet, Text, View } from 'react-native';
import { TouchableOpacity } from 'react-native-gesture-handler'; import { TouchableOpacity } from 'react-native-gesture-handler';
import Orientation from 'react-native-orientation-locker'; import Orientation from 'react-native-orientation-locker';
import { StackNavigationProp } from '@react-navigation/stack'; import { connect } from 'react-redux';
import { Dispatch } from 'redux'; import parse from 'url-parse';
import UserPreferences from '../../lib/userPreferences'; import { inviteLinksClear } from '../../actions/inviteLinks';
import EventEmitter from '../../utils/events'; import { selectServerRequest, serverFinishAdd, serverRequest } from '../../actions/server';
import { selectServerRequest, serverRequest, serverFinishAdd as serverFinishAddAction } from '../../actions/server';
import { inviteLinksClear as inviteLinksClearAction } from '../../actions/inviteLinks';
import sharedStyles from '../Styles';
import Button from '../../containers/Button';
import OrSeparator from '../../containers/OrSeparator';
import FormContainer, { FormContainerInner } from '../../containers/FormContainer';
import I18n from '../../i18n';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import { events, logEvent } from '../../utils/log'; import Button from '../../containers/Button';
import { withTheme } from '../../theme'; import FormContainer, { FormContainerInner } from '../../containers/FormContainer';
import { BASIC_AUTH_KEY, setBasicAuth } from '../../utils/fetch';
import * as HeaderButton from '../../containers/HeaderButton'; import * as HeaderButton from '../../containers/HeaderButton';
import { showConfirmationAlert } from '../../utils/info'; import OrSeparator from '../../containers/OrSeparator';
import { IBaseScreen, TServerHistory } from '../../definitions';
import { withDimensions } from '../../dimensions';
import I18n from '../../i18n';
import database from '../../lib/database'; import database from '../../lib/database';
import { sanitizeLikeString } from '../../lib/database/utils'; import { sanitizeLikeString } from '../../lib/database/utils';
import SSLPinning from '../../utils/sslPinning';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
import { isTablet } from '../../utils/deviceInfo'; import UserPreferences from '../../lib/userPreferences';
import { verticalScale, moderateScale } from '../../utils/scaling';
import { withDimensions } from '../../dimensions';
import ServerInput from './ServerInput';
import { OutsideParamList } from '../../stacks/types'; import { OutsideParamList } from '../../stacks/types';
import { TServerHistory } from '../../definitions/IServerHistory'; import { withTheme } from '../../theme';
import { isTablet } from '../../utils/deviceInfo';
import EventEmitter from '../../utils/events';
import { BASIC_AUTH_KEY, setBasicAuth } from '../../utils/fetch';
import { showConfirmationAlert } from '../../utils/info';
import { events, logEvent } from '../../utils/log';
import { moderateScale, verticalScale } from '../../utils/scaling';
import SSLPinning from '../../utils/sslPinning';
import sharedStyles from '../Styles';
import ServerInput from './ServerInput';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
onboardingImage: { onboardingImage: {
@ -68,20 +66,14 @@ const styles = StyleSheet.create({
} }
}); });
interface INewServerView { interface INewServerViewProps extends IBaseScreen<OutsideParamList, 'NewServerView'> {
navigation: StackNavigationProp<OutsideParamList, 'NewServerView'>;
theme: string;
connecting: boolean; connecting: boolean;
connectServer(server: string, username?: string, fromServerHistory?: boolean): void;
selectServer(server: string): void;
previousServer: string; previousServer: string;
inviteLinksClear(): void;
serverFinishAdd(): void;
width: number; width: number;
height: number; height: number;
} }
interface IState { interface INewServerViewState {
text: string; text: string;
connectingOpen: boolean; connectingOpen: boolean;
certificate: any; certificate: any;
@ -93,8 +85,8 @@ interface ISubmitParams {
username?: string; username?: string;
} }
class NewServerView extends React.Component<INewServerView, IState> { class NewServerView extends React.Component<INewServerViewProps, INewServerViewState> {
constructor(props: INewServerView) { constructor(props: INewServerViewProps) {
super(props); super(props);
if (!isTablet) { if (!isTablet) {
Orientation.lockToPortrait(); Orientation.lockToPortrait();
@ -118,9 +110,9 @@ class NewServerView extends React.Component<INewServerView, IState> {
componentWillUnmount() { componentWillUnmount() {
EventEmitter.removeListener('NewServer', this.handleNewServerEvent); EventEmitter.removeListener('NewServer', this.handleNewServerEvent);
BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress); BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress);
const { previousServer, serverFinishAdd } = this.props; const { previousServer, dispatch } = this.props;
if (previousServer) { if (previousServer) {
serverFinishAdd(); dispatch(serverFinishAdd());
} }
} }
@ -169,9 +161,9 @@ class NewServerView extends React.Component<INewServerView, IState> {
}; };
close = () => { close = () => {
const { selectServer, previousServer, inviteLinksClear } = this.props; const { dispatch, previousServer } = this.props;
inviteLinksClear(); dispatch(inviteLinksClear());
selectServer(previousServer); dispatch(selectServerRequest(previousServer));
}; };
handleNewServerEvent = (event: { server: string }) => { handleNewServerEvent = (event: { server: string }) => {
@ -179,10 +171,10 @@ class NewServerView extends React.Component<INewServerView, IState> {
if (!server) { if (!server) {
return; return;
} }
const { connectServer } = this.props; const { dispatch } = this.props;
this.setState({ text: server }); this.setState({ text: server });
server = this.completeUrl(server); server = this.completeUrl(server);
connectServer(server); dispatch(serverRequest(server));
}; };
onPressServerHistory = (serverHistory: TServerHistory) => { onPressServerHistory = (serverHistory: TServerHistory) => {
@ -192,7 +184,7 @@ class NewServerView extends React.Component<INewServerView, IState> {
submit = async ({ fromServerHistory = false, username }: ISubmitParams = {}) => { submit = async ({ fromServerHistory = false, username }: ISubmitParams = {}) => {
logEvent(events.NS_CONNECT_TO_WORKSPACE); logEvent(events.NS_CONNECT_TO_WORKSPACE);
const { text, certificate } = this.state; const { text, certificate } = this.state;
const { connectServer } = this.props; const { dispatch } = this.props;
this.setState({ connectingOpen: false }); this.setState({ connectingOpen: false });
@ -207,9 +199,9 @@ class NewServerView extends React.Component<INewServerView, IState> {
await this.basicAuth(server, text); await this.basicAuth(server, text);
if (fromServerHistory) { if (fromServerHistory) {
connectServer(server, username, true); dispatch(serverRequest(server, username, true));
} else { } else {
connectServer(server); dispatch(serverRequest(server));
} }
} }
}; };
@ -217,8 +209,8 @@ class NewServerView extends React.Component<INewServerView, IState> {
connectOpen = () => { connectOpen = () => {
logEvent(events.NS_JOIN_OPEN_WORKSPACE); logEvent(events.NS_JOIN_OPEN_WORKSPACE);
this.setState({ connectingOpen: true }); this.setState({ connectingOpen: true });
const { connectServer } = this.props; const { dispatch } = this.props;
connectServer('https://open.rocket.chat'); dispatch(serverRequest('https://open.rocket.chat'));
}; };
basicAuth = async (server: string, text: string) => { basicAuth = async (server: string, text: string) => {
@ -283,7 +275,7 @@ class NewServerView extends React.Component<INewServerView, IState> {
await db.write(async () => { await db.write(async () => {
await item.destroyPermanently(); await item.destroyPermanently();
}); });
this.setState((prevstate: IState) => ({ this.setState((prevstate: INewServerViewState) => ({
serversHistory: prevstate.serversHistory.filter((server: TServerHistory) => server.id !== item.id) serversHistory: prevstate.serversHistory.filter((server: TServerHistory) => server.id !== item.id)
})); }));
} catch { } catch {
@ -417,12 +409,4 @@ const mapStateToProps = (state: any) => ({
previousServer: state.server.previousServer previousServer: state.server.previousServer
}); });
const mapDispatchToProps = (dispatch: Dispatch) => ({ export default connect(mapStateToProps)(withDimensions(withTheme(NewServerView)));
connectServer: (server: string, username: string & null, fromServerHistory?: boolean) =>
dispatch(serverRequest(server, username, fromServerHistory)),
selectServer: (server: string) => dispatch(selectServerRequest(server)),
inviteLinksClear: () => dispatch(inviteLinksClearAction()),
serverFinishAdd: () => dispatch(serverFinishAddAction())
});
export default connect(mapStateToProps, mapDispatchToProps)(withDimensions(withTheme(NewServerView)));

View File

@ -2,7 +2,7 @@ import React, { PureComponent } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { toggleServerDropdown, closeServerDropdown, setSearch as setSearchAction } from '../../../actions/rooms'; import { toggleServerDropdown, closeServerDropdown, setSearch } from '../../../actions/rooms';
import { withTheme } from '../../../theme'; import { withTheme } from '../../../theme';
import EventEmitter from '../../../utils/events'; import EventEmitter from '../../../utils/events';
import { KEY_COMMAND, handleCommandOpenServerDropdown } from '../../../commands'; import { KEY_COMMAND, handleCommandOpenServerDropdown } from '../../../commands';
@ -19,10 +19,7 @@ class RoomsListHeaderView extends PureComponent {
connected: PropTypes.bool, connected: PropTypes.bool,
isFetching: PropTypes.bool, isFetching: PropTypes.bool,
theme: PropTypes.string, theme: PropTypes.string,
server: PropTypes.string, server: PropTypes.string
open: PropTypes.func,
close: PropTypes.func,
setSearch: PropTypes.func
}; };
componentDidMount() { componentDidMount() {
@ -45,17 +42,17 @@ class RoomsListHeaderView extends PureComponent {
}; };
onSearchChangeText = text => { onSearchChangeText = text => {
const { setSearch } = this.props; const { dispatch } = this.props;
setSearch(text.trim()); dispatch(setSearch(text.trim()));
}; };
onPress = () => { onPress = () => {
logEvent(events.RL_TOGGLE_SERVER_DROPDOWN); logEvent(events.RL_TOGGLE_SERVER_DROPDOWN);
const { showServerDropdown, close, open } = this.props; const { showServerDropdown, dispatch } = this.props;
if (showServerDropdown) { if (showServerDropdown) {
close(); dispatch(closeServerDropdown());
} else { } else {
open(); dispatch(toggleServerDropdown());
} }
}; };
@ -89,10 +86,4 @@ const mapStateToProps = state => ({
server: state.server.server server: state.server.server
}); });
const mapDispatchtoProps = dispatch => ({ export default connect(mapStateToProps)(withTheme(RoomsListHeaderView));
close: () => dispatch(closeServerDropdown()),
open: () => dispatch(toggleServerDropdown()),
setSearch: searchText => dispatch(setSearchAction(searchText))
});
export default connect(mapStateToProps, mapDispatchtoProps)(withTheme(RoomsListHeaderView));

View File

@ -6,9 +6,9 @@ import { withSafeAreaInsets } from 'react-native-safe-area-context';
import * as List from '../../containers/List'; import * as List from '../../containers/List';
import Button from '../../containers/Button'; import Button from '../../containers/Button';
import { toggleServerDropdown as toggleServerDropdownAction } from '../../actions/rooms'; import { toggleServerDropdown } from '../../actions/rooms';
import { selectServerRequest as selectServerRequestAction, serverInitAdd as serverInitAddAction } from '../../actions/server'; import { selectServerRequest, serverInitAdd } from '../../actions/server';
import { appStart as appStartAction, ROOT_OUTSIDE } from '../../actions/app'; import { appStart, ROOT_OUTSIDE } from '../../actions/app';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
import I18n from '../../i18n'; import I18n from '../../i18n';
import EventEmitter from '../../utils/events'; import EventEmitter from '../../utils/events';
@ -36,11 +36,7 @@ class ServerDropdown extends Component {
closeServerDropdown: PropTypes.bool, closeServerDropdown: PropTypes.bool,
server: PropTypes.string, server: PropTypes.string,
theme: PropTypes.string, theme: PropTypes.string,
isMasterDetail: PropTypes.bool, isMasterDetail: PropTypes.bool
appStart: PropTypes.func,
toggleServerDropdown: PropTypes.func,
selectServerRequest: PropTypes.func,
initAdd: PropTypes.func
}; };
constructor(props) { constructor(props) {
@ -89,13 +85,13 @@ class ServerDropdown extends Component {
} }
close = () => { close = () => {
const { toggleServerDropdown } = this.props; const { dispatch } = this.props;
Animated.timing(this.animatedValue, { Animated.timing(this.animatedValue, {
toValue: 0, toValue: 0,
duration: ANIMATION_DURATION, duration: ANIMATION_DURATION,
easing: Easing.inOut(Easing.quad), easing: Easing.inOut(Easing.quad),
useNativeDriver: true useNativeDriver: true
}).start(() => toggleServerDropdown()); }).start(() => dispatch(toggleServerDropdown()));
}; };
createWorkspace = async () => { createWorkspace = async () => {
@ -108,10 +104,10 @@ class ServerDropdown extends Component {
}; };
navToNewServer = previousServer => { navToNewServer = previousServer => {
const { appStart, initAdd } = this.props; const { dispatch } = this.props;
batch(() => { batch(() => {
appStart({ root: ROOT_OUTSIDE }); dispatch(appStart({ root: ROOT_OUTSIDE }));
initAdd(previousServer); dispatch(serverInitAdd(previousServer));
}); });
}; };
@ -125,7 +121,7 @@ class ServerDropdown extends Component {
}; };
select = async (server, version) => { select = async (server, version) => {
const { server: currentServer, selectServerRequest, isMasterDetail } = this.props; const { server: currentServer, dispatch, isMasterDetail } = this.props;
this.close(); this.close();
if (currentServer !== server) { if (currentServer !== server) {
logEvent(events.RL_CHANGE_SERVER); logEvent(events.RL_CHANGE_SERVER);
@ -142,7 +138,7 @@ class ServerDropdown extends Component {
}, ANIMATION_DURATION); }, ANIMATION_DURATION);
} else { } else {
await localAuthenticate(server); await localAuthenticate(server);
selectServerRequest(server, version); dispatch(selectServerRequest(server, version, true, true));
} }
} }
}; };
@ -263,11 +259,4 @@ const mapStateToProps = state => ({
isMasterDetail: state.app.isMasterDetail isMasterDetail: state.app.isMasterDetail
}); });
const mapDispatchToProps = dispatch => ({ export default connect(mapStateToProps)(withSafeAreaInsets(withTheme(ServerDropdown)));
toggleServerDropdown: () => dispatch(toggleServerDropdownAction()),
selectServerRequest: (server, version) => dispatch(selectServerRequestAction(server, version, true, true)),
appStart: params => dispatch(appStartAction(params)),
initAdd: previousServer => dispatch(serverInitAddAction(previousServer))
});
export default connect(mapStateToProps, mapDispatchToProps)(withSafeAreaInsets(withTheme(ServerDropdown)));

View File

@ -12,19 +12,13 @@ import RocketChat from '../../lib/rocketchat';
import RoomItem, { ROW_HEIGHT, ROW_HEIGHT_CONDENSED } from '../../presentation/RoomItem'; import RoomItem, { ROW_HEIGHT, ROW_HEIGHT_CONDENSED } from '../../presentation/RoomItem';
import log, { logEvent, events } from '../../utils/log'; import log, { logEvent, events } from '../../utils/log';
import I18n from '../../i18n'; import I18n from '../../i18n';
import { import { closeSearchHeader, closeServerDropdown, openSearchHeader, roomsRequest } from '../../actions/rooms';
closeSearchHeader as closeSearchHeaderAction, import { appStart, ROOT_OUTSIDE } from '../../actions/app';
closeServerDropdown as closeServerDropdownAction,
openSearchHeader as openSearchHeaderAction,
roomsRequest as roomsRequestAction
} from '../../actions/rooms';
import { appStart as appStartAction, ROOT_OUTSIDE } from '../../actions/app';
import debounce from '../../utils/debounce'; import debounce from '../../utils/debounce';
import { isIOS, isTablet } from '../../utils/deviceInfo'; import { isIOS, isTablet } from '../../utils/deviceInfo';
import * as HeaderButton from '../../containers/HeaderButton'; import * as HeaderButton from '../../containers/HeaderButton';
import StatusBar from '../../containers/StatusBar'; import StatusBar from '../../containers/StatusBar';
import ActivityIndicator from '../../containers/ActivityIndicator'; import ActivityIndicator from '../../containers/ActivityIndicator';
import { selectServerRequest as selectServerRequestAction, serverInitAdd as serverInitAddAction } from '../../actions/server';
import { animateNextTransition } from '../../utils/layoutAnimation'; import { animateNextTransition } from '../../utils/layoutAnimation';
import { withTheme } from '../../theme'; import { withTheme } from '../../theme';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
@ -124,11 +118,6 @@ class RoomsListView extends React.Component {
refreshing: PropTypes.bool, refreshing: PropTypes.bool,
StoreLastMessage: PropTypes.bool, StoreLastMessage: PropTypes.bool,
theme: PropTypes.string, theme: PropTypes.string,
openSearchHeader: PropTypes.func,
closeSearchHeader: PropTypes.func,
appStart: PropTypes.func,
roomsRequest: PropTypes.func,
closeServerDropdown: PropTypes.func,
useRealName: PropTypes.bool, useRealName: PropTypes.bool,
isMasterDetail: PropTypes.bool, isMasterDetail: PropTypes.bool,
rooms: PropTypes.array, rooms: PropTypes.array,
@ -169,7 +158,7 @@ class RoomsListView extends React.Component {
} }
componentDidMount() { componentDidMount() {
const { navigation, closeServerDropdown } = this.props; const { navigation, dispatch } = this.props;
this.handleHasPermission(); this.handleHasPermission();
this.mounted = true; this.mounted = true;
@ -193,7 +182,7 @@ class RoomsListView extends React.Component {
}); });
this.unsubscribeBlur = navigation.addListener('blur', () => { this.unsubscribeBlur = navigation.addListener('blur', () => {
this.animated = false; this.animated = false;
closeServerDropdown(); dispatch(closeServerDropdown());
this.cancelSearch(); this.cancelSearch();
if (this.backHandler && this.backHandler.remove) { if (this.backHandler && this.backHandler.remove) {
this.backHandler.remove(); this.backHandler.remove();
@ -553,9 +542,9 @@ class RoomsListView extends React.Component {
initSearching = () => { initSearching = () => {
logEvent(events.RL_SEARCH); logEvent(events.RL_SEARCH);
const { openSearchHeader } = this.props; const { dispatch } = this.props;
this.internalSetState({ searching: true }, () => { this.internalSetState({ searching: true }, () => {
openSearchHeader(); dispatch(openSearchHeader());
this.search(''); this.search('');
this.setHeader(); this.setHeader();
}); });
@ -563,7 +552,7 @@ class RoomsListView extends React.Component {
cancelSearch = () => { cancelSearch = () => {
const { searching } = this.state; const { searching } = this.state;
const { closeSearchHeader } = this.props; const { dispatch } = this.props;
if (!searching) { if (!searching) {
return; return;
@ -573,7 +562,7 @@ class RoomsListView extends React.Component {
this.setState({ searching: false, search: [] }, () => { this.setState({ searching: false, search: [] }, () => {
this.setHeader(); this.setHeader();
closeSearchHeader(); dispatch(closeSearchHeader());
setTimeout(() => { setTimeout(() => {
this.scrollToTop(); this.scrollToTop();
}, 200); }, 200);
@ -842,7 +831,7 @@ class RoomsListView extends React.Component {
}; };
handleCommands = ({ event }) => { handleCommands = ({ event }) => {
const { navigation, server, isMasterDetail, appStart, initAdd } = this.props; const { navigation, server, isMasterDetail, dispatch, initAdd } = this.props;
const { input } = event; const { input } = event;
if (handleCommandShowPreferences(event)) { if (handleCommandShowPreferences(event)) {
navigation.navigate('SettingsView'); navigation.navigate('SettingsView');
@ -862,7 +851,7 @@ class RoomsListView extends React.Component {
} }
} else if (handleCommandAddNewServer(event)) { } else if (handleCommandAddNewServer(event)) {
batch(() => { batch(() => {
appStart({ root: ROOT_OUTSIDE }); dispatch(appStart({ root: ROOT_OUTSIDE }));
initAdd(server); initAdd(server);
}); });
} }
@ -870,11 +859,11 @@ class RoomsListView extends React.Component {
onRefresh = () => { onRefresh = () => {
const { searching } = this.state; const { searching } = this.state;
const { roomsRequest } = this.props; const { dispatch } = this.props;
if (searching) { if (searching) {
return; return;
} }
roomsRequest({ allData: true }); dispatch(roomsRequest({ allData: true }));
}; };
onEndReached = () => { onEndReached = () => {
@ -1045,14 +1034,4 @@ const mapStateToProps = state => ({
createDiscussionPermission: state.permissions['start-discussion'] createDiscussionPermission: state.permissions['start-discussion']
}); });
const mapDispatchToProps = dispatch => ({ export default connect(mapStateToProps)(withDimensions(withTheme(withSafeAreaInsets(RoomsListView))));
openSearchHeader: () => dispatch(openSearchHeaderAction()),
closeSearchHeader: () => dispatch(closeSearchHeaderAction()),
roomsRequest: params => dispatch(roomsRequestAction(params)),
selectServerRequest: server => dispatch(selectServerRequestAction(server)),
closeServerDropdown: () => dispatch(closeServerDropdownAction()),
appStart: params => dispatch(appStartAction(params)),
initAdd: previousServer => dispatch(serverInitAddAction(previousServer))
});
export default connect(mapStateToProps, mapDispatchToProps)(withDimensions(withTheme(withSafeAreaInsets(RoomsListView))));

View File

@ -1,36 +1,36 @@
import { Q } from '@nozbe/watermelondb';
import { HeaderBackButton, StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
import React from 'react'; import React from 'react';
import { Alert, FlatList, Keyboard } from 'react-native'; import { Alert, FlatList, Keyboard } from 'react-native';
import { RouteProp } from '@react-navigation/native';
import { Dispatch } from 'redux';
import { Q } from '@nozbe/watermelondb';
import { EdgeInsets, withSafeAreaInsets } from 'react-native-safe-area-context'; import { EdgeInsets, withSafeAreaInsets } from 'react-native-safe-area-context';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { HeaderBackButton, StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
import StatusBar from '../containers/StatusBar'; import { deleteRoom } from '../actions/room';
import RoomHeader from '../containers/RoomHeader'; import { themes } from '../constants/colors';
import { withTheme } from '../theme'; import { withActionSheet } from '../containers/ActionSheet';
import log, { events, logEvent } from '../utils/log'; import ActivityIndicator from '../containers/ActivityIndicator';
import database from '../lib/database'; import BackgroundContainer from '../containers/BackgroundContainer';
import { getUserSelector } from '../selectors/login';
import { getHeaderTitlePosition } from '../containers/Header'; import { getHeaderTitlePosition } from '../containers/Header';
import * as HeaderButton from '../containers/HeaderButton'; import * as HeaderButton from '../containers/HeaderButton';
import BackgroundContainer from '../containers/BackgroundContainer'; import RoomHeader from '../containers/RoomHeader';
import SafeAreaView from '../containers/SafeAreaView'; import SafeAreaView from '../containers/SafeAreaView';
import ActivityIndicator from '../containers/ActivityIndicator';
import SearchHeader from '../containers/SearchHeader'; import SearchHeader from '../containers/SearchHeader';
import RoomItem, { ROW_HEIGHT } from '../presentation/RoomItem'; import StatusBar from '../containers/StatusBar';
import RocketChat from '../lib/rocketchat'; import { IApplicationState, IBaseScreen } from '../definitions';
import { withDimensions } from '../dimensions'; import { withDimensions } from '../dimensions';
import { isIOS } from '../utils/deviceInfo';
import debounce from '../utils/debounce';
import { showErrorAlert } from '../utils/info';
import { goRoom } from '../utils/goRoom';
import I18n from '../i18n'; import I18n from '../i18n';
import { withActionSheet } from '../containers/ActionSheet'; import database from '../lib/database';
import { deleteRoom as deleteRoomAction } from '../actions/room';
import { CustomIcon } from '../lib/Icons'; import { CustomIcon } from '../lib/Icons';
import { themes } from '../constants/colors'; import RocketChat from '../lib/rocketchat';
import RoomItem, { ROW_HEIGHT } from '../presentation/RoomItem';
import { getUserSelector } from '../selectors/login';
import { ChatsStackParamList } from '../stacks/types';
import { withTheme } from '../theme';
import debounce from '../utils/debounce';
import { isIOS } from '../utils/deviceInfo';
import { goRoom } from '../utils/goRoom';
import { showErrorAlert } from '../utils/info';
import log, { events, logEvent } from '../utils/log';
const API_FETCH_COUNT = 25; const API_FETCH_COUNT = 25;
const PERMISSION_DELETE_C = 'delete-c'; const PERMISSION_DELETE_C = 'delete-c';
@ -78,9 +78,11 @@ interface ITeamChannelsViewState {
showCreate: boolean; showCreate: boolean;
} }
interface ITeamChannelsViewProps { type IProps = Omit<IBaseScreen<ChatsStackParamList, 'TeamChannelsView'>, 'navigation'> & {
route: RouteProp<{ TeamChannelsView: { teamId: string } }, 'TeamChannelsView'>;
navigation: StackNavigationProp<any, 'TeamChannelsView'>; navigation: StackNavigationProp<any, 'TeamChannelsView'>;
};
interface ITeamChannelsViewProps extends IProps {
isMasterDetail: boolean; isMasterDetail: boolean;
insets: EdgeInsets; insets: EdgeInsets;
theme: string; theme: string;
@ -93,7 +95,6 @@ interface ITeamChannelsViewProps {
deleteCPermission: string[]; deleteCPermission: string[];
deletePPermission: string[]; deletePPermission: string[];
showActionSheet: (options: any) => void; showActionSheet: (options: any) => void;
deleteRoom: (rid: string, t: string) => void;
showAvatar: boolean; showAvatar: boolean;
displayMode: string; displayMode: string;
} }
@ -424,7 +425,7 @@ class TeamChannelsView extends React.Component<ITeamChannelsViewProps, ITeamChan
delete = (item: IItem) => { delete = (item: IItem) => {
logEvent(events.TC_DELETE_ROOM); logEvent(events.TC_DELETE_ROOM);
const { deleteRoom } = this.props; const { dispatch } = this.props;
Alert.alert( Alert.alert(
I18n.t('Are_you_sure_question_mark'), I18n.t('Are_you_sure_question_mark'),
@ -437,7 +438,7 @@ class TeamChannelsView extends React.Component<ITeamChannelsViewProps, ITeamChan
{ {
text: I18n.t('Yes_action_it', { action: I18n.t('delete') }), text: I18n.t('Yes_action_it', { action: I18n.t('delete') }),
style: 'destructive', style: 'destructive',
onPress: () => deleteRoom(item._id, item.t) onPress: () => dispatch(deleteRoom(item._id, item.t))
} }
], ],
{ cancelable: false } { cancelable: false }
@ -576,7 +577,7 @@ class TeamChannelsView extends React.Component<ITeamChannelsViewProps, ITeamChan
} }
} }
const mapStateToProps = (state: any) => ({ const mapStateToProps = (state: IApplicationState) => ({
baseUrl: state.server.server, baseUrl: state.server.server,
user: getUserSelector(state), user: getUserSelector(state),
useRealName: state.settings.UI_Use_Real_Name, useRealName: state.settings.UI_Use_Real_Name,
@ -591,11 +592,4 @@ const mapStateToProps = (state: any) => ({
displayMode: state.sortPreferences.displayMode displayMode: state.sortPreferences.displayMode
}); });
const mapDispatchToProps = (dispatch: Dispatch) => ({ export default connect(mapStateToProps)(withDimensions(withSafeAreaInsets(withTheme(withActionSheet(TeamChannelsView)))));
deleteRoom: (rid: string, t: string) => dispatch(deleteRoomAction(rid, t))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(withDimensions(withSafeAreaInsets(withTheme(withActionSheet(TeamChannelsView)))));