Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/chat/ChatScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { canSendToNarrow } from '../utils/narrow';
import { getLoading, getSession } from '../directSelectors';
import { getFetchingForNarrow } from './fetchingSelectors';
import { getShownMessagesForNarrow, isNarrowValid as getIsNarrowValid } from './narrowsSelectors';
import { getTitleBackgroundColor } from '../title/titleSelectors';

type Props = $ReadOnly<{|
navigation: AppNavigationProp<'chat'>,
Expand Down Expand Up @@ -112,11 +113,13 @@ export default function ChatScreen(props: Props) {
const sayNoMessages = haveNoMessages && !isFetching;
const showComposeBox = canSendToNarrow(narrow) && !showMessagePlaceholders;

const titleBackgroundColor = useSelector(state => getTitleBackgroundColor(state, narrow));

return (
<ActionSheetProvider>
<View style={[componentStyles.screen, { backgroundColor }]}>
<KeyboardAvoider style={styles.flexed} behavior="padding">
<ZulipStatusBar narrow={narrow} />
<ZulipStatusBar backgroundColor={titleBackgroundColor} />
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, this is much nicer!

<ChatNavBar narrow={narrow} editMessage={editMessage} />
<OfflineNotice />
<UnreadNotice narrow={narrow} />
Expand Down
19 changes: 6 additions & 13 deletions src/common/ZulipStatusBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import { compose } from 'redux';
import { EdgeInsets } from 'react-native-safe-area-context';

import { withSafeAreaInsets } from '../react-native-safe-area-context';
import type { Narrow, Orientation, ThemeName, Dispatch } from '../types';
import type { Orientation, ThemeName, Dispatch } from '../types';
import { connect } from '../react-redux';
import { DEFAULT_TITLE_BACKGROUND_COLOR, getTitleBackgroundColor } from '../title/titleSelectors';
import { DEFAULT_TITLE_BACKGROUND_COLOR } from '../title/titleSelectors';
import { foregroundColorFromBackground } from '../utils/color';
import { getSession, getSettings } from '../selectors';

Expand All @@ -29,37 +29,31 @@ export const getStatusBarStyle = (statusBarColor: string): BarStyle =>

type SelectorProps = $ReadOnly<{|
theme: ThemeName,
narrowTitleBackgroundColor: string,
orientation: Orientation,
|}>;

type Props = $ReadOnly<{
insets: EdgeInsets,

backgroundColor?: string,
narrow?: Narrow,
backgroundColor: string,
hidden: boolean,

dispatch: Dispatch,
...SelectorProps,
}>;

/**
* Controls the status bar settings depending on platform
* and current navigation position.
* If narrowed to a stream or topic the color of the status bar
* matches that of the stream.
*
* @prop [narrow] - Currently active narrow.
* Applies `hidden` and `backgroundColor` in platform-specific ways.
*/
class ZulipStatusBar extends PureComponent<Props> {
static defaultProps = {
hidden: false,
backgroundColor: DEFAULT_TITLE_BACKGROUND_COLOR,
};

render() {
const { theme, hidden, insets, orientation } = this.props;
const backgroundColor = this.props.backgroundColor ?? this.props.narrowTitleBackgroundColor;
const backgroundColor = this.props.backgroundColor;
const style = { height: hidden ? 0 : insets.top, backgroundColor };
const statusBarColor = getStatusBarColor(backgroundColor, theme);
return (
Expand All @@ -81,7 +75,6 @@ class ZulipStatusBar extends PureComponent<Props> {
export default compose(
connect<SelectorProps, _, _>((state, props) => ({
theme: getSettings(state).theme,
narrowTitleBackgroundColor: getTitleBackgroundColor(state, props.narrow),
orientation: getSession(state).orientation,
})),
withSafeAreaInsets,
Expand Down
173 changes: 78 additions & 95 deletions src/lightbox/Lightbox.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
/* @flow strict-local */

import React, { PureComponent } from 'react';
import React, { useState, useCallback } from 'react';
import { View, Dimensions, Easing } from 'react-native';
import PhotoView from 'react-native-photo-view';
import { connectActionSheet } from '@expo/react-native-action-sheet';
import { useActionSheet } from '@expo/react-native-action-sheet';

import * as NavigationService from '../nav/NavigationService';
import type { Auth, Dispatch, Message } from '../types';
import { connect } from '../react-redux';
import type { Message } from '../types';
import { useSelector } from '../react-redux';
import type { ShowActionSheetWithOptions } from '../message/messageActionSheet';
import { getAuth } from '../selectors';
import { getResource } from '../utils/url';
Expand Down Expand Up @@ -38,106 +38,89 @@ const styles = createStyleSheet({
});

type Props = $ReadOnly<{|
auth: Auth,
dispatch: Dispatch,
src: string,
message: Message,
showActionSheetWithOptions: ShowActionSheetWithOptions,
|}>;

type State = {|
movement: 'in' | 'out',
|};
export default function Lightbox(props: Props) {
const [movement, setMovement] = useState<'in' | 'out'>('out');
const showActionSheetWithOptions: ShowActionSheetWithOptions = useActionSheet()
.showActionSheetWithOptions;
const auth = useSelector(getAuth);

class Lightbox extends PureComponent<Props, State> {
state = {
movement: 'out',
};

handleImagePress = () => {
this.setState(({ movement }, props) => ({
movement: movement === 'out' ? 'in' : 'out',
}));
};

handleOptionsPress = () => {
const options = constructActionSheetButtons();
const cancelButtonIndex = options.length - 1;
const { showActionSheetWithOptions, src, auth } = this.props;
showActionSheetWithOptions(
{
options,
cancelButtonIndex,
},
buttonIndex => {
executeActionSheetAction({
title: options[buttonIndex],
src,
auth,
});
},
);
};
// Pulled out here just because this function is used twice.
const handleImagePress = useCallback(() => {
setMovement(m => (m === 'out' ? 'in' : 'out'));
}, [setMovement]);

handlePressBack = () => {
NavigationService.dispatch(navigateBack());
};
const { src, message } = props;
const footerMessage =
message.type === 'stream'
? `Shared in #${streamNameOfStreamMessage(message)}`
: 'Shared with you';
const resource = getResource(src, auth);
const { width: windowWidth, height: windowHeight } = Dimensions.get('window');

getAnimationProps = () => ({
const animationProps = {
easing: Easing.bezier(0.075, 0.82, 0.165, 1),
duration: 300,
movement: this.state.movement,
});

render() {
const { src, message, auth } = this.props;
const footerMessage =
message.type === 'stream'
? `Shared in #${streamNameOfStreamMessage(message)}`
: 'Shared with you';
const resource = getResource(src, auth);
const { width, height } = Dimensions.get('window');
movement,
};

return (
<View style={styles.container}>
<PhotoView
source={resource}
style={[styles.img, { width }]}
resizeMode="contain"
onTap={this.handleImagePress}
onViewTap={this.handleImagePress}
return (
<View style={styles.container}>
<PhotoView
source={resource}
style={[styles.img, { width: windowWidth }]}
resizeMode="contain"
onTap={handleImagePress}
onViewTap={handleImagePress}
/>
<SlideAnimationView
property="translateY"
style={[styles.overlay, styles.header, { width: windowWidth }]}
from={-NAVBAR_SIZE}
to={0}
{...animationProps}
>
<LightboxHeader
onPressBack={() => {
NavigationService.dispatch(navigateBack());
}}
timestamp={message.timestamp}
avatarUrl={message.avatar_url}
senderName={message.sender_full_name}
senderEmail={message.sender_email}
/>
<SlideAnimationView
property="translateY"
style={[styles.overlay, styles.header, { width }]}
from={-NAVBAR_SIZE}
to={0}
{...this.getAnimationProps()}
>
<LightboxHeader
onPressBack={this.handlePressBack}
timestamp={message.timestamp}
avatarUrl={message.avatar_url}
senderName={message.sender_full_name}
senderEmail={message.sender_email}
/>
</SlideAnimationView>
<SlideAnimationView
property="translateY"
style={[styles.overlay, { width, bottom: height - 44 }]}
from={height}
to={height - 44}
{...this.getAnimationProps()}
>
<LightboxFooter displayMessage={footerMessage} onOptionsPress={this.handleOptionsPress} />
</SlideAnimationView>
</View>
);
}
</SlideAnimationView>
<SlideAnimationView
property="translateY"
style={[styles.overlay, { width: windowWidth, bottom: windowHeight - 44 }]}
from={windowHeight}
to={windowHeight - 44}
Comment on lines +98 to +100
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also notice, thanks to this cleanup calling my attention to it, that this longstanding bit of code is all unnecessarily complicated -- if you subtract windowHeight from all three of these vertical values, it gets a lot simpler. Maybe simpler still if you subtract windowHeight - 44, so that it's:

  • bottom: 0
  • from={44}
  • to={0}

But that might all be made moot by the changes you're already working on to replace this translateY animation entirely.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've got a commit that does exactly that simplification; about to send the PR. 😄

{...animationProps}
>
<LightboxFooter
displayMessage={footerMessage}
onOptionsPress={() => {
const options = constructActionSheetButtons();
const cancelButtonIndex = options.length - 1;
showActionSheetWithOptions(
{
options,
cancelButtonIndex,
},
buttonIndex => {
executeActionSheetAction({
title: options[buttonIndex],
src,
auth,
});
},
);
}}
/>
</SlideAnimationView>
</View>
);
}

export default connectActionSheet(
connect(state => ({
auth: getAuth(state),
}))(Lightbox),
);
24 changes: 11 additions & 13 deletions src/lightbox/LightboxScreen.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* @flow strict-local */
import React, { PureComponent } from 'react';
import React from 'react';
import { View } from 'react-native';
import { ActionSheetProvider } from '@expo/react-native-action-sheet';

Expand All @@ -22,16 +22,14 @@ type Props = $ReadOnly<{|
route: AppNavigationRouteProp<'lightbox'>,
|}>;

export default class LightboxScreen extends PureComponent<Props> {
render() {
const { src, message } = this.props.route.params;
return (
<View style={styles.screen}>
<ZulipStatusBar hidden backgroundColor="black" />
<ActionSheetProvider>
<Lightbox src={src} message={message} />
</ActionSheetProvider>
</View>
);
}
export default function LightboxScreen(props: Props) {
const { src, message } = props.route.params;
return (
<View style={styles.screen}>
<ZulipStatusBar hidden backgroundColor="black" />
<ActionSheetProvider>
<Lightbox src={src} message={message} />
</ActionSheetProvider>
</View>
);
}
Loading