Chore: evaluate getUserSelector (#4013)
* update: type for user * update: LanguageView's user type * update: UserNotificationPreferencesView's user * chore: set `enableMessageParserEarlyAdoption` as non-optional
This commit is contained in:
parent
d38c17bcd8
commit
13a2962fa2
|
@ -7,7 +7,7 @@ export interface ILoggedUser {
|
||||||
id: string;
|
id: string;
|
||||||
token: string;
|
token: string;
|
||||||
username: string;
|
username: string;
|
||||||
name: string;
|
name?: string;
|
||||||
language?: string;
|
language?: string;
|
||||||
status: TUserStatus;
|
status: TUserStatus;
|
||||||
statusText?: string;
|
statusText?: string;
|
||||||
|
@ -20,7 +20,7 @@ export interface ILoggedUser {
|
||||||
avatarETag?: string;
|
avatarETag?: string;
|
||||||
showMessageInMainThread?: boolean;
|
showMessageInMainThread?: boolean;
|
||||||
isFromWebView?: boolean;
|
isFromWebView?: boolean;
|
||||||
enableMessageParserEarlyAdoption?: boolean;
|
enableMessageParserEarlyAdoption: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ILoggedUserResultFromServer
|
export interface ILoggedUserResultFromServer
|
||||||
|
|
|
@ -2,20 +2,7 @@ import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { ProfileStackParamList } from '../stacks/types';
|
import { ProfileStackParamList } from '../stacks/types';
|
||||||
|
import { IUser } from './IUser';
|
||||||
export interface IUser {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
username: string;
|
|
||||||
emails: {
|
|
||||||
[index: number]: {
|
|
||||||
address: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
customFields: {
|
|
||||||
[index: string | number]: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IParams {
|
export interface IParams {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
|
@ -115,33 +115,23 @@ export interface INotificationPreferences {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IUserPreferences {
|
export interface IUserPreferences {
|
||||||
user: { _id: string };
|
user: Pick<IUser, '_id'>;
|
||||||
settings: {
|
settings: {
|
||||||
preferences: INotificationPreferences;
|
preferences: INotificationPreferences;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IUser extends IRocketChatRecord, Omit<ILoggedUser, 'username' | 'name' | 'status'> {
|
export interface IUser extends IRocketChatRecord, ILoggedUser {
|
||||||
_id: string;
|
_id: string;
|
||||||
id: string;
|
|
||||||
token: string;
|
|
||||||
createdAt?: Date;
|
createdAt?: Date;
|
||||||
roles?: string[];
|
|
||||||
type?: string;
|
type?: string;
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
username: string;
|
|
||||||
name?: string;
|
|
||||||
services?: IUserServices;
|
services?: IUserServices;
|
||||||
emails?: IUserEmail[];
|
|
||||||
status: TUserStatus;
|
|
||||||
statusConnection?: string;
|
statusConnection?: string;
|
||||||
lastLogin?: Date;
|
lastLogin?: Date;
|
||||||
avatarOrigin?: string;
|
avatarOrigin?: string;
|
||||||
avatarETag?: string;
|
|
||||||
utcOffset?: number;
|
utcOffset?: number;
|
||||||
language?: string;
|
|
||||||
statusDefault?: TUserStatus;
|
statusDefault?: TUserStatus;
|
||||||
statusText?: string;
|
|
||||||
oauth?: {
|
oauth?: {
|
||||||
authorizedClients: string[];
|
authorizedClients: string[];
|
||||||
};
|
};
|
||||||
|
@ -151,9 +141,6 @@ export interface IUser extends IRocketChatRecord, Omit<ILoggedUser, 'username' |
|
||||||
public_key: string;
|
public_key: string;
|
||||||
};
|
};
|
||||||
requirePasswordChange?: boolean;
|
requirePasswordChange?: boolean;
|
||||||
customFields?: {
|
|
||||||
[key: string]: any;
|
|
||||||
};
|
|
||||||
settings?: IUserSettings;
|
settings?: IUserSettings;
|
||||||
defaultRoom?: string;
|
defaultRoom?: string;
|
||||||
ldap?: boolean;
|
ldap?: boolean;
|
||||||
|
|
|
@ -13,18 +13,17 @@ export interface IServices {
|
||||||
wordpress: { clientId: string; serverURL: string };
|
wordpress: { clientId: string; serverURL: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
const getUser = (state: IApplicationState): Partial<IUser> => {
|
const getUser = (state: IApplicationState): IUser => {
|
||||||
if (!isEmpty(state.share?.user)) {
|
if (!isEmpty(state.share?.user)) {
|
||||||
return state.share.user;
|
return state.share.user as IUser;
|
||||||
}
|
}
|
||||||
return state.login?.user;
|
return state.login?.user as IUser;
|
||||||
};
|
};
|
||||||
const getLoginServices = (state: IApplicationState) => (state.login.services as IServices) || {};
|
const getLoginServices = (state: IApplicationState) => (state.login.services as IServices) || {};
|
||||||
const getShowFormLoginSetting = (state: IApplicationState) => (state.settings.Accounts_ShowFormLogin as boolean) || false;
|
const getShowFormLoginSetting = (state: IApplicationState) => (state.settings.Accounts_ShowFormLogin as boolean) || false;
|
||||||
const getIframeEnabledSetting = (state: IApplicationState) => (state.settings.Accounts_iframe_enabled as boolean) || false;
|
const getIframeEnabledSetting = (state: IApplicationState) => (state.settings.Accounts_iframe_enabled as boolean) || false;
|
||||||
|
|
||||||
// TODO: we need to change 42 files to fix a correct type, i believe is better to do this later
|
export const getUserSelector = createSelector([getUser], user => user);
|
||||||
export const getUserSelector = createSelector([getUser], user => user) as any;
|
|
||||||
|
|
||||||
export const getShowLoginButton = createSelector(
|
export const getShowLoginButton = createSelector(
|
||||||
[getLoginServices, getShowFormLoginSetting, getIframeEnabledSetting],
|
[getLoginServices, getShowFormLoginSetting, getIframeEnabledSetting],
|
||||||
|
|
|
@ -23,7 +23,7 @@ import SafeAreaView from '../containers/SafeAreaView';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import sharedStyles from './Styles';
|
import sharedStyles from './Styles';
|
||||||
import { ChatsStackParamList } from '../stacks/types';
|
import { ChatsStackParamList } from '../stacks/types';
|
||||||
import { IApplicationState, IBaseScreen } from '../definitions';
|
import { IApplicationState, IBaseScreen, IUser } from '../definitions';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -90,11 +90,7 @@ interface ICreateChannelViewProps extends IBaseScreen<ChatsStackParamList, 'Crea
|
||||||
isFetching: boolean;
|
isFetching: boolean;
|
||||||
encryptionEnabled: boolean;
|
encryptionEnabled: boolean;
|
||||||
users: IOtherUser[];
|
users: IOtherUser[];
|
||||||
user: {
|
user: IUser;
|
||||||
id: string;
|
|
||||||
token: string;
|
|
||||||
roles: string[];
|
|
||||||
};
|
|
||||||
teamId: string;
|
teamId: string;
|
||||||
createPublicChannelPermission: string[] | undefined;
|
createPublicChannelPermission: string[] | undefined;
|
||||||
createPrivateChannelPermission: string[] | undefined;
|
createPrivateChannelPermission: string[] | undefined;
|
||||||
|
|
|
@ -23,6 +23,7 @@ import EventEmitter from '../utils/events';
|
||||||
import { LISTENER } from '../containers/Toast';
|
import { LISTENER } from '../containers/Toast';
|
||||||
import debounce from '../utils/debounce';
|
import debounce from '../utils/debounce';
|
||||||
import sharedStyles from './Styles';
|
import sharedStyles from './Styles';
|
||||||
|
import { IUser } from '../definitions';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -48,10 +49,7 @@ interface IE2EEncryptionSecurityViewState {
|
||||||
|
|
||||||
interface IE2EEncryptionSecurityViewProps {
|
interface IE2EEncryptionSecurityViewProps {
|
||||||
theme: string;
|
theme: string;
|
||||||
user: {
|
user: IUser;
|
||||||
roles: string[];
|
|
||||||
id: string;
|
|
||||||
};
|
|
||||||
server: string;
|
server: string;
|
||||||
encryptionEnabled: boolean;
|
encryptionEnabled: boolean;
|
||||||
logout(): void;
|
logout(): void;
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { events, logEvent } from '../utils/log';
|
||||||
import { isAndroid, isIOS } from '../utils/deviceInfo';
|
import { isAndroid, isIOS } from '../utils/deviceInfo';
|
||||||
import { withTheme } from '../theme';
|
import { withTheme } from '../theme';
|
||||||
import { InsideStackParamList } from '../stacks/types';
|
import { InsideStackParamList } from '../stacks/types';
|
||||||
|
import { IApplicationState, IUser } from '../definitions';
|
||||||
|
|
||||||
const formatUrl = (url: string, baseUrl: string, uriSize: number, avatarAuthURLFragment: string) =>
|
const formatUrl = (url: string, baseUrl: string, uriSize: number, avatarAuthURLFragment: string) =>
|
||||||
`${baseUrl}/avatar/${url}?format=png&width=${uriSize}&height=${uriSize}${avatarAuthURLFragment}`;
|
`${baseUrl}/avatar/${url}?format=png&width=${uriSize}&height=${uriSize}${avatarAuthURLFragment}`;
|
||||||
|
@ -30,12 +31,7 @@ interface IJitsiMeetViewProps {
|
||||||
route: RouteProp<InsideStackParamList, 'JitsiMeetView'>;
|
route: RouteProp<InsideStackParamList, 'JitsiMeetView'>;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
theme: string;
|
theme: string;
|
||||||
user: {
|
user: IUser;
|
||||||
id: string;
|
|
||||||
username: string;
|
|
||||||
name: string;
|
|
||||||
token: string;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class JitsiMeetView extends React.Component<IJitsiMeetViewProps, IJitsiMeetViewState> {
|
class JitsiMeetView extends React.Component<IJitsiMeetViewProps, IJitsiMeetViewState> {
|
||||||
|
@ -50,12 +46,12 @@ class JitsiMeetView extends React.Component<IJitsiMeetViewProps, IJitsiMeetViewS
|
||||||
this.jitsiTimeout = null;
|
this.jitsiTimeout = null;
|
||||||
|
|
||||||
const { user, baseUrl } = props;
|
const { user, baseUrl } = props;
|
||||||
const { name: displayName, id: userId, token, username } = user;
|
const { name, id: userId, token, username } = user;
|
||||||
const avatarAuthURLFragment = `&rc_token=${token}&rc_uid=${userId}`;
|
const avatarAuthURLFragment = `&rc_token=${token}&rc_uid=${userId}`;
|
||||||
const avatar = formatUrl(username, baseUrl, 100, avatarAuthURLFragment);
|
const avatar = formatUrl(username, baseUrl, 100, avatarAuthURLFragment);
|
||||||
this.state = {
|
this.state = {
|
||||||
userInfo: {
|
userInfo: {
|
||||||
displayName,
|
displayName: name as string,
|
||||||
avatar
|
avatar
|
||||||
},
|
},
|
||||||
loading: true
|
loading: true
|
||||||
|
@ -135,7 +131,7 @@ class JitsiMeetView extends React.Component<IJitsiMeetViewProps, IJitsiMeetViewS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state: any) => ({
|
const mapStateToProps = (state: IApplicationState) => ({
|
||||||
user: getUserSelector(state),
|
user: getUserSelector(state),
|
||||||
baseUrl: state.server.server
|
baseUrl: state.server.server
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { themes } from '../../lib/constants';
|
||||||
import * as List from '../../containers/List';
|
import * as List from '../../containers/List';
|
||||||
import SafeAreaView from '../../containers/SafeAreaView';
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
import StatusBar from '../../containers/StatusBar';
|
import StatusBar from '../../containers/StatusBar';
|
||||||
import { IApplicationState, IBaseScreen, RootEnum } from '../../definitions';
|
import { IApplicationState, IBaseScreen, IUser, RootEnum } from '../../definitions';
|
||||||
import I18n, { isRTL, LANGUAGES } from '../../i18n';
|
import I18n, { isRTL, LANGUAGES } from '../../i18n';
|
||||||
import database from '../../lib/database';
|
import database from '../../lib/database';
|
||||||
import RocketChat from '../../lib/rocketchat';
|
import RocketChat from '../../lib/rocketchat';
|
||||||
|
@ -20,10 +20,7 @@ import { showErrorAlert } from '../../utils/info';
|
||||||
import log, { events, logEvent } from '../../utils/log';
|
import log, { events, logEvent } from '../../utils/log';
|
||||||
|
|
||||||
interface ILanguageViewProps extends IBaseScreen<SettingsStackParamList, 'LanguageView'> {
|
interface ILanguageViewProps extends IBaseScreen<SettingsStackParamList, 'LanguageView'> {
|
||||||
user: {
|
user: IUser;
|
||||||
id: string;
|
|
||||||
language: string;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ILanguageViewState {
|
interface ILanguageViewState {
|
||||||
|
@ -38,7 +35,7 @@ class LanguageView extends React.Component<ILanguageViewProps, ILanguageViewStat
|
||||||
constructor(props: ILanguageViewProps) {
|
constructor(props: ILanguageViewProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
language: props.user ? props.user.language : 'en'
|
language: props.user ? (props.user.language as string) : 'en'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,7 +100,7 @@ class LanguageView extends React.Component<ILanguageViewProps, ILanguageViewStat
|
||||||
await serversDB.write(async () => {
|
await serversDB.write(async () => {
|
||||||
try {
|
try {
|
||||||
const userRecord = await usersCollection.find(user.id);
|
const userRecord = await usersCollection.find(user.id);
|
||||||
await userRecord.update((record: any) => {
|
await userRecord.update(record => {
|
||||||
record.language = params.language;
|
record.language = params.language;
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -37,9 +37,9 @@ import {
|
||||||
INavigationOptions,
|
INavigationOptions,
|
||||||
IParams,
|
IParams,
|
||||||
IProfileViewProps,
|
IProfileViewProps,
|
||||||
IProfileViewState,
|
IProfileViewState
|
||||||
IUser
|
|
||||||
} from '../../definitions/IProfileViewInterfaces';
|
} from '../../definitions/IProfileViewInterfaces';
|
||||||
|
import { IUser } from '../../definitions';
|
||||||
|
|
||||||
class ProfileView extends React.Component<IProfileViewProps, IProfileViewState> {
|
class ProfileView extends React.Component<IProfileViewProps, IProfileViewState> {
|
||||||
private name: any;
|
private name: any;
|
||||||
|
@ -116,7 +116,7 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
|
||||||
const { name, username, emails, customFields } = user || userProps;
|
const { name, username, emails, customFields } = user || userProps;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
name,
|
name: name as string,
|
||||||
username,
|
username,
|
||||||
email: emails ? emails[0].address : null,
|
email: emails ? emails[0].address : null,
|
||||||
newPassword: null,
|
newPassword: null,
|
||||||
|
|
|
@ -13,7 +13,7 @@ import Loading from '../containers/Loading';
|
||||||
import SafeAreaView from '../containers/SafeAreaView';
|
import SafeAreaView from '../containers/SafeAreaView';
|
||||||
import SearchBox from '../containers/SearchBox';
|
import SearchBox from '../containers/SearchBox';
|
||||||
import StatusBar from '../containers/StatusBar';
|
import StatusBar from '../containers/StatusBar';
|
||||||
import { IApplicationState, IBaseScreen, ISubscription } from '../definitions';
|
import { IApplicationState, IBaseScreen, ISubscription, IUser } from '../definitions';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import database from '../lib/database';
|
import database from '../lib/database';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
|
@ -39,12 +39,7 @@ interface ISelectedUsersViewProps extends IBaseScreen<ChatsStackParamList, 'Sele
|
||||||
// REDUX STATE
|
// REDUX STATE
|
||||||
users: ISelectedUser[];
|
users: ISelectedUser[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
user: {
|
user: IUser;
|
||||||
id: string;
|
|
||||||
token: string;
|
|
||||||
username: string;
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +59,7 @@ class SelectedUsersView extends React.Component<ISelectedUsersViewProps, ISelect
|
||||||
};
|
};
|
||||||
const { user, dispatch } = this.props;
|
const { user, dispatch } = this.props;
|
||||||
if (this.isGroupChat()) {
|
if (this.isGroupChat()) {
|
||||||
dispatch(addUser({ _id: user.id, name: user.username, fname: user.name }));
|
dispatch(addUser({ _id: user.id, name: user.username, fname: user.name as string }));
|
||||||
}
|
}
|
||||||
this.setHeader(props.route.params?.showButton);
|
this.setHeader(props.route.params?.showButton);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ import * as List from '../../containers/List';
|
||||||
import SafeAreaView from '../../containers/SafeAreaView';
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
import StatusBar from '../../containers/StatusBar';
|
import StatusBar from '../../containers/StatusBar';
|
||||||
import { LISTENER } from '../../containers/Toast';
|
import { LISTENER } from '../../containers/Toast';
|
||||||
import { IApplicationState, IBaseScreen, RootEnum } from '../../definitions';
|
import { IApplicationState, IBaseScreen, IUser, RootEnum } from '../../definitions';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import database from '../../lib/database';
|
import database from '../../lib/database';
|
||||||
import RocketChat from '../../lib/rocketchat';
|
import RocketChat from '../../lib/rocketchat';
|
||||||
|
@ -34,10 +34,7 @@ import SidebarView from '../SidebarView';
|
||||||
interface ISettingsViewProps extends IBaseScreen<SettingsStackParamList, 'SettingsView'> {
|
interface ISettingsViewProps extends IBaseScreen<SettingsStackParamList, 'SettingsView'> {
|
||||||
server: IServer;
|
server: IServer;
|
||||||
isMasterDetail: boolean;
|
isMasterDetail: boolean;
|
||||||
user: {
|
user: IUser;
|
||||||
roles: [];
|
|
||||||
id: string;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SettingsView extends React.Component<ISettingsViewProps, any> {
|
class SettingsView extends React.Component<ISettingsViewProps, any> {
|
||||||
|
|
|
@ -19,7 +19,7 @@ import Navigation from '../../lib/navigation/appNavigation';
|
||||||
import SidebarItem from './SidebarItem';
|
import SidebarItem from './SidebarItem';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { DrawerParamList } from '../../stacks/types';
|
import { DrawerParamList } from '../../stacks/types';
|
||||||
import { TUserStatus } from '../../definitions';
|
import { IUser } from '../../definitions';
|
||||||
|
|
||||||
interface ISeparatorProps {
|
interface ISeparatorProps {
|
||||||
theme: string;
|
theme: string;
|
||||||
|
@ -39,13 +39,7 @@ interface ISidebarProps {
|
||||||
navigation: DrawerNavigationProp<DrawerParamList>;
|
navigation: DrawerNavigationProp<DrawerParamList>;
|
||||||
state: DrawerNavigationState<DrawerParamList>;
|
state: DrawerNavigationState<DrawerParamList>;
|
||||||
Site_Name: string;
|
Site_Name: string;
|
||||||
user: {
|
user: IUser;
|
||||||
statusText: string;
|
|
||||||
status: TUserStatus;
|
|
||||||
username: string;
|
|
||||||
name: string;
|
|
||||||
roles: string[];
|
|
||||||
};
|
|
||||||
theme?: string;
|
theme?: string;
|
||||||
loadingServer: boolean;
|
loadingServer: boolean;
|
||||||
useRealName: boolean;
|
useRealName: boolean;
|
||||||
|
|
|
@ -64,7 +64,7 @@ interface IStatusViewState {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IStatusViewProps extends IBaseScreen<any, 'StatusView'> {
|
interface IStatusViewProps extends IBaseScreen<any, 'StatusView'> {
|
||||||
user: Pick<IUser, 'id' | 'status' | 'statusText'>;
|
user: IUser;
|
||||||
isMasterDetail: boolean;
|
isMasterDetail: boolean;
|
||||||
Accounts_AllowInvisibleStatusOption: boolean;
|
Accounts_AllowInvisibleStatusOption: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ import { getUserSelector } from '../../selectors/login';
|
||||||
import sharedStyles from '../Styles';
|
import sharedStyles from '../Styles';
|
||||||
import { OPTIONS } from './options';
|
import { OPTIONS } from './options';
|
||||||
import { ProfileStackParamList } from '../../stacks/types';
|
import { ProfileStackParamList } from '../../stacks/types';
|
||||||
import { INotificationPreferences } from '../../definitions';
|
import { INotificationPreferences, IUser } from '../../definitions';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
pickerText: {
|
pickerText: {
|
||||||
|
@ -34,9 +34,7 @@ interface IUserNotificationPreferencesViewState {
|
||||||
interface IUserNotificationPreferencesViewProps {
|
interface IUserNotificationPreferencesViewProps {
|
||||||
navigation: StackNavigationProp<ProfileStackParamList, 'UserNotificationPrefView'>;
|
navigation: StackNavigationProp<ProfileStackParamList, 'UserNotificationPrefView'>;
|
||||||
theme: string;
|
theme: string;
|
||||||
user: {
|
user: IUser;
|
||||||
id: string;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class UserNotificationPreferencesView extends React.Component<
|
class UserNotificationPreferencesView extends React.Component<
|
||||||
|
|
Loading…
Reference in New Issue