[RN] Replace chat modal with SlidingView
This commit is contained in:
parent
2a5adfc601
commit
13212a5980
|
@ -1,178 +0,0 @@
|
||||||
// @flow
|
|
||||||
|
|
||||||
import React, { PureComponent, type Node } from 'react';
|
|
||||||
import { Animated, TouchableWithoutFeedback, View } from 'react-native';
|
|
||||||
|
|
||||||
import styles, { SIDEBAR_WIDTH } from './styles';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of the React {@code Component} props of {@link SideBar}.
|
|
||||||
*/
|
|
||||||
type Props = {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The children of {@code SideBar}.
|
|
||||||
*/
|
|
||||||
children: Node,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback to notify the containing {@code Component} that the sidebar is
|
|
||||||
* closing.
|
|
||||||
*/
|
|
||||||
onHide: Function,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the menu (of the {@code SideBar}?) is displayed/rendered/shown.
|
|
||||||
*/
|
|
||||||
show: boolean
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of the React {@code Component} state of {@link SideBar}.
|
|
||||||
*/
|
|
||||||
type State = {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the side overlay should be displayed/rendered/shown.
|
|
||||||
*/
|
|
||||||
showOverlay: boolean,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The native animation object.
|
|
||||||
*/
|
|
||||||
sliderAnimation: Animated.Value
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A generic animated side bar to be used for left-side, hamburger-style menus.
|
|
||||||
*/
|
|
||||||
export default class SideBar extends PureComponent<Props, State> {
|
|
||||||
/**
|
|
||||||
* Implements React's {@link Component#getDerivedStateFromProps()}.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
static getDerivedStateFromProps(props: Props, prevState: State) {
|
|
||||||
return {
|
|
||||||
showOverlay: props.show || prevState.showOverlay
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes a new {@code SideBar} instance.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
constructor(props: Props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
showOverlay: false,
|
|
||||||
sliderAnimation: new Animated.Value(0)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Bind event handlers so they are only bound once per instance.
|
|
||||||
this._onHideMenu = this._onHideMenu.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements React's {@link Component#componentDidMount()}.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
componentDidMount() {
|
|
||||||
this._setShow(this.props.show);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements React's {@link Component#componentDidUpdate()}.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
componentDidUpdate() {
|
|
||||||
this._setShow(this.props.show);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements React's {@link Component#render()}.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
pointerEvents = 'box-none'
|
|
||||||
style = { styles.sideMenuContainer } >
|
|
||||||
{
|
|
||||||
this.state.showOverlay
|
|
||||||
&& <TouchableWithoutFeedback
|
|
||||||
onPress = { this._onHideMenu } >
|
|
||||||
<View style = { styles.sideMenuShadow } />
|
|
||||||
</TouchableWithoutFeedback>
|
|
||||||
}
|
|
||||||
<Animated.View style = { this._getContentStyle() }>
|
|
||||||
{ this.props.children }
|
|
||||||
</Animated.View>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_getContentStyle: () => Array<Object>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assembles a style array for the sidebar content.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {Array<Object>}
|
|
||||||
*/
|
|
||||||
_getContentStyle() {
|
|
||||||
return [
|
|
||||||
styles.sideMenuContent,
|
|
||||||
{ transform: [ { translateX: this.state.sliderAnimation } ] }
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
_onHideMenu: () => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hides the side menu.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_onHideMenu() {
|
|
||||||
this._setShow(false);
|
|
||||||
|
|
||||||
const { onHide } = this.props;
|
|
||||||
|
|
||||||
onHide && onHide();
|
|
||||||
}
|
|
||||||
|
|
||||||
_setShow: (boolean) => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows/hides the side menu.
|
|
||||||
*
|
|
||||||
* @param {boolean} show - If the side menu is to be made visible,
|
|
||||||
* {@code true}; otherwise, {@code false}.
|
|
||||||
* @private
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_setShow(show) {
|
|
||||||
Animated
|
|
||||||
.timing(
|
|
||||||
/* value */ this.state.sliderAnimation,
|
|
||||||
/* config */ {
|
|
||||||
toValue: show ? SIDEBAR_WIDTH : 0,
|
|
||||||
useNativeDriver: true
|
|
||||||
})
|
|
||||||
.start(({ finished }) => {
|
|
||||||
finished && !show && this.setState({ showOverlay: false });
|
|
||||||
|
|
||||||
// XXX Technically, the arrow function can further be simplified
|
|
||||||
// by removing the {} and returning the boolean expression
|
|
||||||
// above. Practically and unfortunately though, Flow freaks out
|
|
||||||
// and states that Animated.timing doesn't exist!?
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,265 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import React, { PureComponent, type Node } from 'react';
|
||||||
|
import {
|
||||||
|
Animated,
|
||||||
|
Dimensions,
|
||||||
|
TouchableWithoutFeedback,
|
||||||
|
View
|
||||||
|
} from 'react-native';
|
||||||
|
|
||||||
|
import { type StyleType } from '../../../styles';
|
||||||
|
|
||||||
|
import styles from './slidingviewstyles';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the React {@code Component} props of {@link SlidingView}.
|
||||||
|
*/
|
||||||
|
type Props = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The children of {@code SlidingView}.
|
||||||
|
*/
|
||||||
|
children: Node,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback to notify the containing {@code Component} that the view is
|
||||||
|
* closing.
|
||||||
|
*/
|
||||||
|
onHide: Function,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Position of the SlidingView: 'left', 'right', 'top', 'bottom'.
|
||||||
|
* later).
|
||||||
|
*/
|
||||||
|
position: string,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the {@code SlidingView} is to be displayed/rendered/shown or not.
|
||||||
|
*/
|
||||||
|
show: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Style of the animated view.
|
||||||
|
*/
|
||||||
|
style: StyleType
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the React {@code Component} state of {@link SlidingView}.
|
||||||
|
*/
|
||||||
|
type State = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the sliding overlay should be displayed/rendered/shown.
|
||||||
|
*/
|
||||||
|
showOverlay: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The native animation object.
|
||||||
|
*/
|
||||||
|
sliderAnimation: Animated.Value,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset to move the view out of the screen.
|
||||||
|
*/
|
||||||
|
positionOffset: number
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A generic animated slider view to be used for animated menus.
|
||||||
|
*/
|
||||||
|
export default class SlidingView extends PureComponent<Props, State> {
|
||||||
|
/**
|
||||||
|
* True if the component is mounted.
|
||||||
|
*/
|
||||||
|
_mounted: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements React's {@link Component#getDerivedStateFromProps()}.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
static getDerivedStateFromProps(props: Props, prevState: State) {
|
||||||
|
return {
|
||||||
|
showOverlay: props.show || prevState.showOverlay
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a new {@code SlidingView} instance.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
const { height, width } = Dimensions.get('window');
|
||||||
|
const { position } = props;
|
||||||
|
|
||||||
|
let positionOffset = height;
|
||||||
|
|
||||||
|
if (position === 'left' || position === 'right') {
|
||||||
|
positionOffset = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
showOverlay: false,
|
||||||
|
sliderAnimation: new Animated.Value(0),
|
||||||
|
positionOffset
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bind event handlers so they are only bound once per instance.
|
||||||
|
this._onHideMenu = this._onHideMenu.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements React's {@link Component#componentDidMount()}.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
componentDidMount() {
|
||||||
|
this._mounted = true;
|
||||||
|
this._setShow(this.props.show);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements React's {@link Component#componentDidUpdate()}.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
componentDidUpdate() {
|
||||||
|
this._setShow(this.props.show);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements React's {@link Component#componentWillUnmount()}.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
componentWillUnmount() {
|
||||||
|
this._mounted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements React's {@link Component#render()}.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
const { showOverlay } = this.state;
|
||||||
|
|
||||||
|
if (!showOverlay) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
pointerEvents = 'box-none'
|
||||||
|
style = { styles.sliderViewContainer } >
|
||||||
|
<TouchableWithoutFeedback
|
||||||
|
onPress = { this._onHideMenu } >
|
||||||
|
<View style = { styles.sliderViewShadow } />
|
||||||
|
</TouchableWithoutFeedback>
|
||||||
|
<Animated.View
|
||||||
|
style = { this._getContentStyle() }>
|
||||||
|
{ this.props.children }
|
||||||
|
</Animated.View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_getContentStyle: () => Array<Object>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assembles a style array for the SlideView content.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {Array<Object>}
|
||||||
|
*/
|
||||||
|
_getContentStyle() {
|
||||||
|
const style = {
|
||||||
|
...this.props.style,
|
||||||
|
...styles.sliderViewContent
|
||||||
|
};
|
||||||
|
const { positionOffset } = this.state;
|
||||||
|
|
||||||
|
switch (this.props.position) {
|
||||||
|
case 'bottom':
|
||||||
|
Object.assign(style, {
|
||||||
|
bottom: -positionOffset,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
top: positionOffset
|
||||||
|
}, {
|
||||||
|
transform: [ { translateY: this.state.sliderAnimation } ]
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'left':
|
||||||
|
Object.assign(style, {
|
||||||
|
bottom: 0,
|
||||||
|
left: -positionOffset,
|
||||||
|
right: positionOffset,
|
||||||
|
top: 0
|
||||||
|
}, {
|
||||||
|
transform: [ { translateX: this.state.sliderAnimation } ]
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
|
||||||
|
_onHideMenu: () => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides the slider.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_onHideMenu() {
|
||||||
|
this._setShow(false);
|
||||||
|
|
||||||
|
const { onHide } = this.props;
|
||||||
|
|
||||||
|
onHide && onHide();
|
||||||
|
}
|
||||||
|
|
||||||
|
_setShow: (boolean) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows/hides the slider menu.
|
||||||
|
*
|
||||||
|
* @param {boolean} show - If the slider view is to be made visible,
|
||||||
|
* {@code true}; otherwise, {@code false}.
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_setShow(show) {
|
||||||
|
if (!this._mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { positionOffset } = this.state;
|
||||||
|
const { position } = this.props;
|
||||||
|
let toValue = positionOffset;
|
||||||
|
|
||||||
|
if (position === 'bottom' || position === 'right') {
|
||||||
|
toValue = -positionOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
Animated
|
||||||
|
.timing(
|
||||||
|
/* value */ this.state.sliderAnimation,
|
||||||
|
/* config */ {
|
||||||
|
duration: 200,
|
||||||
|
toValue: show ? toValue : 0,
|
||||||
|
useNativeDriver: true
|
||||||
|
})
|
||||||
|
.start(({ finished }) => {
|
||||||
|
finished && this._mounted && !show
|
||||||
|
&& this.setState({ showOverlay: false });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,7 @@ export { default as NavigateSectionListSectionHeader }
|
||||||
export { default as PagedList } from './PagedList';
|
export { default as PagedList } from './PagedList';
|
||||||
export { default as Pressable } from './Pressable';
|
export { default as Pressable } from './Pressable';
|
||||||
export { default as SectionList } from './SectionList';
|
export { default as SectionList } from './SectionList';
|
||||||
export { default as SideBar } from './SideBar';
|
export { default as SlidingView } from './SlidingView';
|
||||||
export { default as Switch } from './Switch';
|
export { default as Switch } from './Switch';
|
||||||
export { default as Text } from './Text';
|
export { default as Text } from './Text';
|
||||||
export { default as TintedView } from './TintedView';
|
export { default as TintedView } from './TintedView';
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
import { OVERLAY_Z_INDEX } from '../../constants';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* The topmost container of the side bar.
|
||||||
|
*/
|
||||||
|
sliderViewContainer: {
|
||||||
|
...StyleSheet.absoluteFillObject,
|
||||||
|
zIndex: OVERLAY_Z_INDEX
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The container of the actual content of the side menu.
|
||||||
|
*/
|
||||||
|
sliderViewContent: {
|
||||||
|
position: 'absolute'
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The opaque area that covers the rest of the screen, when the side bar is
|
||||||
|
* open.
|
||||||
|
*/
|
||||||
|
sliderViewShadow: {
|
||||||
|
...StyleSheet.absoluteFillObject,
|
||||||
|
backgroundColor: 'rgba(0, 0, 0, 0.5)'
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,7 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import { StyleSheet } from 'react-native';
|
|
||||||
|
|
||||||
import { BoxModel, ColorPalette, createStyleSheet } from '../../../styles';
|
import { BoxModel, ColorPalette, createStyleSheet } from '../../../styles';
|
||||||
|
|
||||||
const AVATAR_OPACITY = 0.4;
|
const AVATAR_OPACITY = 0.4;
|
||||||
|
@ -9,7 +7,6 @@ const OVERLAY_FONT_COLOR = 'rgba(255, 255, 255, 0.6)';
|
||||||
const SECONDARY_ACTION_BUTTON_SIZE = 30;
|
const SECONDARY_ACTION_BUTTON_SIZE = 30;
|
||||||
|
|
||||||
export const AVATAR_SIZE = 65;
|
export const AVATAR_SIZE = 65;
|
||||||
export const SIDEBAR_WIDTH = 250;
|
|
||||||
export const UNDERLAY_COLOR = 'rgba(255, 255, 255, 0.2)';
|
export const UNDERLAY_COLOR = 'rgba(255, 255, 255, 0.2)';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -241,35 +238,6 @@ const SECTION_LIST_STYLES = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const SIDEBAR_STYLES = {
|
|
||||||
/**
|
|
||||||
* The topmost container of the side bar.
|
|
||||||
*/
|
|
||||||
sideMenuContainer: {
|
|
||||||
...StyleSheet.absoluteFillObject
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The container of the actual content of the side menu.
|
|
||||||
*/
|
|
||||||
sideMenuContent: {
|
|
||||||
bottom: 0,
|
|
||||||
left: -SIDEBAR_WIDTH,
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
width: SIDEBAR_WIDTH
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The opaque area that covers the rest of the screen, when the side bar is
|
|
||||||
* open.
|
|
||||||
*/
|
|
||||||
sideMenuShadow: {
|
|
||||||
...StyleSheet.absoluteFillObject,
|
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.5)'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const TINTED_VIEW_DEFAULT = {
|
export const TINTED_VIEW_DEFAULT = {
|
||||||
backgroundColor: ColorPalette.appBackground,
|
backgroundColor: ColorPalette.appBackground,
|
||||||
opacity: 0.8
|
opacity: 0.8
|
||||||
|
@ -281,6 +249,5 @@ export const TINTED_VIEW_DEFAULT = {
|
||||||
*/
|
*/
|
||||||
export default createStyleSheet({
|
export default createStyleSheet({
|
||||||
...PAGED_LIST_STYLES,
|
...PAGED_LIST_STYLES,
|
||||||
...SECTION_LIST_STYLES,
|
...SECTION_LIST_STYLES
|
||||||
...SIDEBAR_STYLES
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Z-index for components that are to be rendered like an overlay, to be over
|
||||||
|
* everything, such as modal-type of components, or dialogs.
|
||||||
|
*/
|
||||||
|
export const OVERLAY_Z_INDEX = 1000;
|
|
@ -1,31 +1,28 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { SafeAreaView } from 'react-native';
|
import { SafeAreaView, View } from 'react-native';
|
||||||
import { GiftedChat } from 'react-native-gifted-chat';
|
import { GiftedChat } from 'react-native-gifted-chat';
|
||||||
|
|
||||||
import { translate } from '../../../base/i18n';
|
import { translate } from '../../../base/i18n';
|
||||||
import { BackButton, Header, HeaderLabel, Modal } from '../../../base/react';
|
|
||||||
|
import {
|
||||||
|
BackButton,
|
||||||
|
Header,
|
||||||
|
HeaderLabel,
|
||||||
|
SlidingView
|
||||||
|
} from '../../../base/react';
|
||||||
import { connect } from '../../../base/redux';
|
import { connect } from '../../../base/redux';
|
||||||
|
|
||||||
import AbstractChat, {
|
import AbstractChat, {
|
||||||
_mapDispatchToProps,
|
_mapDispatchToProps,
|
||||||
_mapStateToProps as _abstractMapStateToProps,
|
_mapStateToProps,
|
||||||
type Props as AbstractProps
|
type Props
|
||||||
} from '../AbstractChat';
|
} from '../AbstractChat';
|
||||||
|
|
||||||
import ChatMessage from './ChatMessage';
|
import ChatMessage from './ChatMessage';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
type Props = AbstractProps & {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* True if the chat window should have a solid BG render.
|
|
||||||
*/
|
|
||||||
_solidBackground: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements a React native component that renders the chat window (modal) of
|
* Implements a React native component that renders the chat window (modal) of
|
||||||
* the mobile client.
|
* the mobile client.
|
||||||
|
@ -55,32 +52,24 @@ class Chat extends AbstractChat<Props> {
|
||||||
// of messages.
|
// of messages.
|
||||||
const messages
|
const messages
|
||||||
= this.props._messages.map(this._transformMessage).reverse();
|
= this.props._messages.map(this._transformMessage).reverse();
|
||||||
const modalStyle = [
|
|
||||||
styles.modalBackdrop
|
|
||||||
];
|
|
||||||
|
|
||||||
if (this.props._solidBackground) {
|
|
||||||
// We only use a transparent background, when we are in a video
|
|
||||||
// meeting to give a user a glympse of what's happening. Otherwise
|
|
||||||
// we use a non-transparent background.
|
|
||||||
modalStyle.push(styles.solidModalBackdrop);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<SlidingView
|
||||||
onRequestClose = { this.props._onToggleChat }
|
position = 'bottom'
|
||||||
visible = { this.props._isOpen }>
|
show = { this.props._isOpen } >
|
||||||
|
<View style = { styles.chatContainer }>
|
||||||
<Header>
|
<Header>
|
||||||
<BackButton onPress = { this.props._onToggleChat } />
|
<BackButton onPress = { this.props._onToggleChat } />
|
||||||
<HeaderLabel labelKey = 'chat.title' />
|
<HeaderLabel labelKey = 'chat.title' />
|
||||||
</Header>
|
</Header>
|
||||||
<SafeAreaView style = { modalStyle }>
|
<SafeAreaView style = { styles.backdrop }>
|
||||||
<GiftedChat
|
<GiftedChat
|
||||||
messages = { messages }
|
messages = { messages }
|
||||||
onSend = { this._onSend }
|
onSend = { this._onSend }
|
||||||
renderMessage = { this._renderMessage } />
|
renderMessage = { this._renderMessage } />
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
</Modal>
|
</View>
|
||||||
|
</SlidingView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,21 +135,4 @@ class Chat extends AbstractChat<Props> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps part of the Redux state to the props of this component.
|
|
||||||
*
|
|
||||||
* @param {Object} state - The Redux state.
|
|
||||||
* @returns {{
|
|
||||||
* _solidBackground: boolean
|
|
||||||
* }}
|
|
||||||
*/
|
|
||||||
function _mapStateToProps(state) {
|
|
||||||
const abstractReduxProps = _abstractMapStateToProps(state);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...abstractReduxProps,
|
|
||||||
_solidBackground: state['features/base/conference'].audioOnly
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default translate(connect(_mapStateToProps, _mapDispatchToProps)(Chat));
|
export default translate(connect(_mapStateToProps, _mapDispatchToProps)(Chat));
|
||||||
|
|
|
@ -8,8 +8,8 @@ import { Avatar } from '../../../base/participants';
|
||||||
import { connect } from '../../../base/redux';
|
import { connect } from '../../../base/redux';
|
||||||
|
|
||||||
import AbstractChatMessage, {
|
import AbstractChatMessage, {
|
||||||
_mapStateToProps as _abstractMapStateToProps,
|
_mapStateToProps,
|
||||||
type Props as AbstractProps
|
type Props
|
||||||
} from '../AbstractChatMessage';
|
} from '../AbstractChatMessage';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
|
@ -23,14 +23,6 @@ const AVATAR_SIZE = 32;
|
||||||
*/
|
*/
|
||||||
const TIMESTAMP_FORMAT = 'H:mm';
|
const TIMESTAMP_FORMAT = 'H:mm';
|
||||||
|
|
||||||
type Props = AbstractProps & {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* True if the chat window has a solid BG so then we have to adopt in style.
|
|
||||||
*/
|
|
||||||
_solidBackground: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders a single chat message.
|
* Renders a single chat message.
|
||||||
*/
|
*/
|
||||||
|
@ -54,9 +46,6 @@ class ChatMessage extends AbstractChatMessage<Props> {
|
||||||
const textWrapperStyle = [
|
const textWrapperStyle = [
|
||||||
styles.textWrapper
|
styles.textWrapper
|
||||||
];
|
];
|
||||||
const timeTextStyles = [
|
|
||||||
styles.timeText
|
|
||||||
];
|
|
||||||
|
|
||||||
if (localMessage) {
|
if (localMessage) {
|
||||||
// The wrapper needs to be aligned to the right.
|
// The wrapper needs to be aligned to the right.
|
||||||
|
@ -69,10 +58,6 @@ class ChatMessage extends AbstractChatMessage<Props> {
|
||||||
textWrapperStyle.push(styles.systemTextWrapper);
|
textWrapperStyle.push(styles.systemTextWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props._solidBackground) {
|
|
||||||
timeTextStyles.push(styles.solidBGTimeText);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style = { styles.messageWrapper } >
|
<View style = { styles.messageWrapper } >
|
||||||
{
|
{
|
||||||
|
@ -92,7 +77,7 @@ class ChatMessage extends AbstractChatMessage<Props> {
|
||||||
{ message.text }
|
{ message.text }
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text style = { timeTextStyles }>
|
<Text style = { styles.timeText }>
|
||||||
{ timeStamp }
|
{ timeStamp }
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
@ -133,20 +118,4 @@ class ChatMessage extends AbstractChatMessage<Props> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps part of the Redux state to the props of this component.
|
|
||||||
*
|
|
||||||
* @param {Object} state - The Redux state.
|
|
||||||
* @param {Props} ownProps - The own props of the component.
|
|
||||||
* @returns {{
|
|
||||||
* _solidBackground: boolean
|
|
||||||
* }}
|
|
||||||
*/
|
|
||||||
function _mapStateToProps(state, ownProps) {
|
|
||||||
return {
|
|
||||||
..._abstractMapStateToProps(state, ownProps),
|
|
||||||
_solidBackground: state['features/base/conference'].audioOnly
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default translate(connect(_mapStateToProps)(ChatMessage));
|
export default translate(connect(_mapStateToProps)(ChatMessage));
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import {
|
import { ColorPalette } from '../../../base/styles';
|
||||||
ColorPalette,
|
|
||||||
createStyleSheet
|
|
||||||
} from '../../../base/styles';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The styles of the feature chat.
|
* The styles of the feature chat.
|
||||||
|
@ -13,7 +10,7 @@ import {
|
||||||
* need to extract the brand colors and sizes into a branding feature (planned
|
* need to extract the brand colors and sizes into a branding feature (planned
|
||||||
* for the future).
|
* for the future).
|
||||||
*/
|
*/
|
||||||
export default createStyleSheet({
|
export default {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper View for the avatar.
|
* Wrapper View for the avatar.
|
||||||
|
@ -22,6 +19,19 @@ export default createStyleSheet({
|
||||||
marginRight: 8
|
marginRight: 8
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Background of the chat screen.
|
||||||
|
*/
|
||||||
|
backdrop: {
|
||||||
|
backgroundColor: ColorPalette.white,
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
|
||||||
|
chatContainer: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'column'
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper for the details together, such as name, message and time.
|
* Wrapper for the details together, such as name, message and time.
|
||||||
*/
|
*/
|
||||||
|
@ -58,16 +68,6 @@ export default createStyleSheet({
|
||||||
marginVertical: 4
|
marginVertical: 4
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Background of the chat screen. Currently it's set to a transparent value
|
|
||||||
* as the idea is that the participant would still want to see at least a
|
|
||||||
* part of the video when he/she is in the chat window.
|
|
||||||
*/
|
|
||||||
modalBackdrop: {
|
|
||||||
backgroundColor: 'rgba(127, 127, 127, 0.8)',
|
|
||||||
flex: 1
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Style modifier for the {@code detailsWrapper} for own messages.
|
* Style modifier for the {@code detailsWrapper} for own messages.
|
||||||
*/
|
*/
|
||||||
|
@ -84,17 +84,6 @@ export default createStyleSheet({
|
||||||
borderTopRightRadius: 0
|
borderTopRightRadius: 0
|
||||||
},
|
},
|
||||||
|
|
||||||
solidBGTimeText: {
|
|
||||||
color: 'rgb(164, 184, 209)'
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Style modifier for the chat window when we're in audio only mode.
|
|
||||||
*/
|
|
||||||
solidModalBackdrop: {
|
|
||||||
backgroundColor: ColorPalette.white
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Style modifier for system (error) messages.
|
* Style modifier for system (error) messages.
|
||||||
*/
|
*/
|
||||||
|
@ -118,7 +107,7 @@ export default createStyleSheet({
|
||||||
* Text node for the timestamp.
|
* Text node for the timestamp.
|
||||||
*/
|
*/
|
||||||
timeText: {
|
timeText: {
|
||||||
color: ColorPalette.white,
|
color: 'rgb(164, 184, 209)',
|
||||||
fontSize: 13
|
fontSize: 13
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
} from '../../base/participants';
|
} from '../../base/participants';
|
||||||
import {
|
import {
|
||||||
Header,
|
Header,
|
||||||
SideBar
|
SlidingView
|
||||||
} from '../../base/react';
|
} from '../../base/react';
|
||||||
import { connect } from '../../base/redux';
|
import { connect } from '../../base/redux';
|
||||||
import { setSettingsViewVisible } from '../../settings';
|
import { setSettingsViewVisible } from '../../settings';
|
||||||
|
@ -83,9 +83,11 @@ class WelcomePageSideBar extends Component<Props> {
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<SideBar
|
<SlidingView
|
||||||
onHide = { this._onHideSideBar }
|
onHide = { this._onHideSideBar }
|
||||||
show = { this.props._visible }>
|
position = 'left'
|
||||||
|
show = { this.props._visible }
|
||||||
|
style = { styles.sideBar } >
|
||||||
<Header style = { styles.sideBarHeader }>
|
<Header style = { styles.sideBarHeader }>
|
||||||
<Avatar
|
<Avatar
|
||||||
size = { SIDEBAR_AVATAR_SIZE }
|
size = { SIDEBAR_AVATAR_SIZE }
|
||||||
|
@ -116,7 +118,7 @@ class WelcomePageSideBar extends Component<Props> {
|
||||||
url = { SEND_FEEDBACK_URL } />
|
url = { SEND_FEEDBACK_URL } />
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
</SideBar>
|
</SlidingView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -171,6 +171,13 @@ export default createStyleSheet({
|
||||||
flexDirection: 'column'
|
flexDirection: 'column'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container of the side bar.
|
||||||
|
*/
|
||||||
|
sideBar: {
|
||||||
|
width: 250
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The body of the side bar where the items are.
|
* The body of the side bar where the items are.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue