Share extension
This commit is contained in:
parent
009de40200
commit
ffda9e528f
|
@ -30,6 +30,9 @@ import { KEY_COMMAND } from './commands';
|
|||
import Tablet, { initTabletNav } from './tablet';
|
||||
import { SplitContext } from './split';
|
||||
import AppContainer from './AppContainer';
|
||||
import TwoFactor from './containers/TwoFactor';
|
||||
import ScreenLockedView from './views/ScreenLockedView';
|
||||
import ChangePasscodeView from './views/ChangePasscodeView';
|
||||
|
||||
RNScreens.enableScreens();
|
||||
|
||||
|
@ -188,6 +191,9 @@ export default class Root extends React.Component {
|
|||
}}
|
||||
>
|
||||
{content}
|
||||
<TwoFactor />
|
||||
<ScreenLockedView />
|
||||
<ChangePasscodeView />
|
||||
</ThemeContext.Provider>
|
||||
</Provider>
|
||||
</AppearanceProvider>
|
||||
|
|
|
@ -1,21 +1,12 @@
|
|||
import { NavigationActions } from '@react-navigation/native';
|
||||
import * as React from 'react';
|
||||
|
||||
let _shareNavigator;
|
||||
const navigationRef = React.createRef();
|
||||
|
||||
function setTopLevelNavigator(navigatorRef) {
|
||||
_shareNavigator = navigatorRef;
|
||||
}
|
||||
|
||||
function navigate(routeName, params) {
|
||||
_shareNavigator.dispatch(
|
||||
NavigationActions.navigate({
|
||||
routeName,
|
||||
params
|
||||
})
|
||||
);
|
||||
function navigate(name, params) {
|
||||
navigationRef.current?.navigate(name, params);
|
||||
}
|
||||
|
||||
export default {
|
||||
navigate,
|
||||
setTopLevelNavigator
|
||||
navigationRef,
|
||||
navigate
|
||||
};
|
||||
|
|
106
app/share.js
106
app/share.js
|
@ -1,5 +1,6 @@
|
|||
import React, { useState, useContext } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import { AppearanceProvider } from 'react-native-appearance';
|
||||
import { createStackNavigator } from '@react-navigation/stack';
|
||||
|
@ -14,14 +15,12 @@ import {
|
|||
} from './utils/theme';
|
||||
import Navigation from './lib/ShareNavigation';
|
||||
import store from './lib/createStore';
|
||||
import sharedStyles from './views/Styles';
|
||||
import { isNotch, isIOS, supportSystemTheme } from './utils/deviceInfo';
|
||||
import { isIOS, supportSystemTheme } from './utils/deviceInfo';
|
||||
import { defaultHeader, onNavigationStateChange, themedHeader } from './utils/navigation';
|
||||
import RocketChat, { THEME_PREFERENCES_KEY } from './lib/rocketchat';
|
||||
import { ThemeContext } from './theme';
|
||||
|
||||
// Outside Stack
|
||||
import AuthLoadingView from './views/AuthLoadingView';
|
||||
import WithoutServersView from './views/WithoutServersView';
|
||||
|
||||
// Inside Stack
|
||||
|
@ -33,17 +32,25 @@ const Inside = createStackNavigator();
|
|||
const InsideStack = () => {
|
||||
const { theme } = useContext(ThemeContext);
|
||||
|
||||
const screenOptions = {
|
||||
...defaultHeader,
|
||||
...themedHeader(theme)
|
||||
};
|
||||
screenOptions.headerStyle = {
|
||||
...screenOptions.headerStyle,
|
||||
// TODO: fix on multiple files PR :)
|
||||
height: 57
|
||||
};
|
||||
|
||||
return (
|
||||
<Inside.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme) }}>
|
||||
<Inside.Navigator screenOptions={screenOptions}>
|
||||
<Inside.Screen
|
||||
name='ShareListView'
|
||||
component={ShareListView}
|
||||
options={props => ShareListView.navigationOptions({ ...props, theme })}
|
||||
/>
|
||||
<Inside.Screen
|
||||
name='ShareView'
|
||||
component={ShareView}
|
||||
options={ShareView.navigationOptions}
|
||||
/>
|
||||
<Inside.Screen
|
||||
name='SelectServerView'
|
||||
|
@ -69,48 +76,47 @@ const OutsideStack = () => {
|
|||
);
|
||||
};
|
||||
|
||||
const AuthContext = React.createContext();
|
||||
|
||||
// App
|
||||
const Stack = createStackNavigator();
|
||||
export const App = () => {
|
||||
const [loading] = useState(false);
|
||||
export const App = ({ root }) => {
|
||||
if (!root) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={{}}>
|
||||
<Stack.Navigator screenOptions={{ headerShown: false }}>
|
||||
{loading ? (
|
||||
<Stack.Navigator screenOptions={{ headerShown: false }}>
|
||||
<>
|
||||
{root === 'outside' ? (
|
||||
<Stack.Screen
|
||||
name='AuthLoading'
|
||||
component={AuthLoadingView}
|
||||
name='OutsideStack'
|
||||
component={OutsideStack}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<Stack.Screen
|
||||
name='OutsideStack'
|
||||
component={OutsideStack}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name='InsideStack'
|
||||
component={InsideStack}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Stack.Navigator>
|
||||
</AuthContext.Provider>
|
||||
) : null}
|
||||
{root === 'inside' ? (
|
||||
<Stack.Screen
|
||||
name='InsideStack'
|
||||
component={InsideStack}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
</Stack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
App.propTypes = {
|
||||
root: PropTypes.string
|
||||
};
|
||||
|
||||
class Root extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isLandscape: false,
|
||||
theme: defaultTheme(),
|
||||
themePreferences: {
|
||||
currentTheme: supportSystemTheme() ? 'automatic' : 'light',
|
||||
darkLevel: 'dark'
|
||||
}
|
||||
},
|
||||
root: ''
|
||||
};
|
||||
this.init();
|
||||
}
|
||||
|
@ -129,10 +135,10 @@ class Root extends React.Component {
|
|||
const token = await RNUserDefaults.get(RocketChat.TOKEN_KEY);
|
||||
|
||||
if (currentServer && token) {
|
||||
await Navigation.navigate('InsideStack');
|
||||
this.setState({ root: 'inside' });
|
||||
await RocketChat.shareExtensionInit(currentServer);
|
||||
} else {
|
||||
await Navigation.navigate('OutsideStack');
|
||||
this.setState({ root: 'outside' });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,32 +151,20 @@ class Root extends React.Component {
|
|||
});
|
||||
}
|
||||
|
||||
handleLayout = (event) => {
|
||||
const { width, height } = event.nativeEvent.layout;
|
||||
this.setState({ isLandscape: width > height });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isLandscape, theme } = this.state;
|
||||
const { theme, root } = this.state;
|
||||
return (
|
||||
<AppearanceProvider>
|
||||
<View
|
||||
style={[sharedStyles.container, isLandscape && isNotch ? sharedStyles.notchLandscapeContainer : {}]}
|
||||
onLayout={this.handleLayout}
|
||||
>
|
||||
<Provider store={store}>
|
||||
<ThemeContext.Provider value={{ theme }}>
|
||||
<NavigationContainer
|
||||
ref={(navigatorRef) => {
|
||||
Navigation.setTopLevelNavigator(navigatorRef);
|
||||
}}
|
||||
onNavigationStateChange={onNavigationStateChange}
|
||||
>
|
||||
<App />
|
||||
</NavigationContainer>
|
||||
</ThemeContext.Provider>
|
||||
</Provider>
|
||||
</View>
|
||||
<Provider store={store}>
|
||||
<ThemeContext.Provider value={{ theme }}>
|
||||
<NavigationContainer
|
||||
ref={Navigation.navigationRef}
|
||||
onNavigationStateChange={onNavigationStateChange}
|
||||
>
|
||||
<App root={root} />
|
||||
</NavigationContainer>
|
||||
</ThemeContext.Provider>
|
||||
</Provider>
|
||||
</AppearanceProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ import { TransitionPresets } from '@react-navigation/stack';
|
|||
|
||||
import { analytics, leaveBreadcrumb } from './log';
|
||||
import { themes } from '../constants/colors';
|
||||
import { isIOS } from './deviceInfo';
|
||||
|
||||
export const defaultHeader = {
|
||||
headerBackTitleVisible: false,
|
||||
|
|
|
@ -36,14 +36,14 @@ class SelectServerView extends React.Component {
|
|||
|
||||
static propTypes = {
|
||||
server: PropTypes.string,
|
||||
navigation: PropTypes.object,
|
||||
route: PropTypes.object,
|
||||
theme: PropTypes.string
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const { navigation } = this.props;
|
||||
const servers = navigation.getParam('servers', []);
|
||||
const { route } = this.props;
|
||||
const servers = route.params?.servers ?? [];
|
||||
const filteredServers = servers.filter(server => server.roomsUpdatedAt);
|
||||
this.state = {
|
||||
servers: filteredServers
|
||||
|
|
|
@ -12,7 +12,7 @@ import { Q } from '@nozbe/watermelondb';
|
|||
|
||||
import Navigation from '../../lib/ShareNavigation';
|
||||
import database from '../../lib/database';
|
||||
import { isIOS, isAndroid } from '../../utils/deviceInfo';
|
||||
import { isIOS } from '../../utils/deviceInfo';
|
||||
import I18n from '../../i18n';
|
||||
import { CustomIcon } from '../../lib/Icons';
|
||||
import log from '../../utils/log';
|
||||
|
@ -35,53 +35,6 @@ const getItemLayout = (data, index) => ({ length: ROW_HEIGHT, offset: ROW_HEIGHT
|
|||
const keyExtractor = item => item.rid;
|
||||
|
||||
class ShareListView extends React.Component {
|
||||
static navigationOptions = ({ route, theme }) => {
|
||||
const searching = route.params?.searching;
|
||||
const initSearch = route.params?.initSearch ?? (() => {});
|
||||
const cancelSearch = route.params?.cancelSearch ?? (() => {});
|
||||
const search = route.params?.search ?? (() => {});
|
||||
|
||||
if (isIOS) {
|
||||
return {
|
||||
headerStyle: { backgroundColor: themes[theme].headerBackground },
|
||||
headerTitle: () => (
|
||||
<ShareListHeader
|
||||
searching={searching}
|
||||
initSearch={initSearch}
|
||||
cancelSearch={cancelSearch}
|
||||
search={search}
|
||||
theme={theme}
|
||||
/>
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
headerLeft: () => (searching
|
||||
? (
|
||||
<CustomHeaderButtons left>
|
||||
<Item title='cancel' iconName='cross' onPress={cancelSearch} />
|
||||
</CustomHeaderButtons>
|
||||
)
|
||||
: (
|
||||
<CancelModalButton
|
||||
onPress={ShareExtension.close}
|
||||
testID='share-extension-close'
|
||||
/>
|
||||
)),
|
||||
headerTitle: () => <ShareListHeader searching={searching} search={search} theme={theme} />,
|
||||
headerRight: () => (
|
||||
searching
|
||||
? null
|
||||
: (
|
||||
<CustomHeaderButtons>
|
||||
{isAndroid ? <Item title='search' iconName='magnifier' onPress={initSearch} /> : null}
|
||||
</CustomHeaderButtons>
|
||||
)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
navigation: PropTypes.object,
|
||||
server: PropTypes.string,
|
||||
|
@ -107,18 +60,13 @@ class ShareListView extends React.Component {
|
|||
loading: true,
|
||||
serverInfo: null
|
||||
};
|
||||
this.setHeader();
|
||||
this.didFocusListener = props.navigation.addListener('didFocus', () => BackHandler.addEventListener('hardwareBackPress', this.handleBackPress));
|
||||
this.willBlurListener = props.navigation.addListener('willBlur', () => BackHandler.addEventListener('hardwareBackPress', this.handleBackPress));
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { navigation, server } = this.props;
|
||||
navigation.setParams({
|
||||
initSearch: this.initSearch,
|
||||
cancelSearch: this.cancelSearch,
|
||||
search: this.search
|
||||
});
|
||||
|
||||
const { server } = this.props;
|
||||
setTimeout(async() => {
|
||||
try {
|
||||
const { value, type } = await ShareExtension.data();
|
||||
|
@ -181,6 +129,51 @@ class ShareListView extends React.Component {
|
|||
return false;
|
||||
}
|
||||
|
||||
setHeader = () => {
|
||||
const { searching } = this.state;
|
||||
const { navigation, theme } = this.props;
|
||||
|
||||
if (isIOS) {
|
||||
navigation.setOptions({
|
||||
header: () => (
|
||||
<ShareListHeader
|
||||
searching={searching}
|
||||
initSearch={this.initSearch}
|
||||
cancelSearch={this.cancelSearch}
|
||||
search={this.search}
|
||||
theme={theme}
|
||||
/>
|
||||
)
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
navigation.setOptions({
|
||||
headerLeft: () => (searching
|
||||
? (
|
||||
<CustomHeaderButtons left>
|
||||
<Item title='cancel' iconName='cross' onPress={this.cancelSearch} />
|
||||
</CustomHeaderButtons>
|
||||
)
|
||||
: (
|
||||
<CancelModalButton
|
||||
onPress={ShareExtension.close}
|
||||
testID='share-extension-close'
|
||||
/>
|
||||
)),
|
||||
headerTitle: () => <ShareListHeader searching={searching} search={this.search} theme={theme} />,
|
||||
headerRight: () => (
|
||||
searching
|
||||
? null
|
||||
: (
|
||||
<CustomHeaderButtons>
|
||||
<Item title='search' iconName='magnifier' onPress={this.initSearch} />
|
||||
</CustomHeaderButtons>
|
||||
)
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/sort-comp
|
||||
internalSetState = (...args) => {
|
||||
const { navigation } = this.props;
|
||||
|
@ -259,15 +252,11 @@ class ShareListView extends React.Component {
|
|||
|
||||
initSearch = () => {
|
||||
const { chats } = this.state;
|
||||
const { navigation } = this.props;
|
||||
this.setState({ searching: true, searchResults: chats });
|
||||
navigation.setParams({ searching: true });
|
||||
this.setState({ searching: true, searchResults: chats }, () => this.setHeader());
|
||||
}
|
||||
|
||||
cancelSearch = () => {
|
||||
const { navigation } = this.props;
|
||||
this.internalSetState({ searching: false, searchResults: [], searchText: '' });
|
||||
navigation.setParams({ searching: false });
|
||||
this.internalSetState({ searching: false, searchResults: [], searchText: '' }, () => this.setHeader());
|
||||
Keyboard.dismiss();
|
||||
}
|
||||
|
||||
|
|
|
@ -18,29 +18,9 @@ import { isReadOnly } from '../../utils/isReadOnly';
|
|||
import { withTheme } from '../../theme';
|
||||
|
||||
class ShareView extends React.Component {
|
||||
static navigationOptions = ({ route }) => {
|
||||
const canSend = route.params?.canSend ?? true;
|
||||
|
||||
return ({
|
||||
title: I18n.t('Share'),
|
||||
headerRight:
|
||||
() => (canSend
|
||||
? (
|
||||
<CustomHeaderButtons>
|
||||
<Item
|
||||
title={I18n.t('Send')}
|
||||
onPress={route.params?.sendMessage}
|
||||
testID='send-message-share-view'
|
||||
buttonStyle={styles.send}
|
||||
/>
|
||||
</CustomHeaderButtons>
|
||||
)
|
||||
: null)
|
||||
});
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
navigation: PropTypes.object,
|
||||
route: PropTypes.object,
|
||||
theme: PropTypes.string,
|
||||
user: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
|
@ -52,13 +32,13 @@ class ShareView extends React.Component {
|
|||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const { navigation } = this.props;
|
||||
const rid = navigation.getParam('rid', '');
|
||||
const name = navigation.getParam('name', '');
|
||||
const value = navigation.getParam('value', '');
|
||||
const isMedia = navigation.getParam('isMedia', false);
|
||||
const fileInfo = navigation.getParam('fileInfo', {});
|
||||
const room = navigation.getParam('room', { rid });
|
||||
const { route } = this.props;
|
||||
const rid = route.params?.rid;
|
||||
const name = route.params?.name;
|
||||
const value = route.params?.value;
|
||||
const isMedia = route.params?.isMedia ?? false;
|
||||
const fileInfo = route.params?.fileInfo ?? {};
|
||||
const room = route.params?.room ?? { rid };
|
||||
|
||||
this.state = {
|
||||
rid,
|
||||
|
@ -72,30 +52,49 @@ class ShareView extends React.Component {
|
|||
file: {
|
||||
name: fileInfo ? fileInfo.name : '',
|
||||
description: ''
|
||||
}
|
||||
},
|
||||
canSend: false
|
||||
};
|
||||
|
||||
this.setReadOnly();
|
||||
this.setHeader();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
setHeader = () => {
|
||||
const { canSend } = this.state;
|
||||
const { navigation } = this.props;
|
||||
navigation.setParams({ sendMessage: this._sendMessage });
|
||||
|
||||
navigation.setOptions({
|
||||
title: I18n.t('Share'),
|
||||
headerRight:
|
||||
() => (canSend
|
||||
? (
|
||||
<CustomHeaderButtons>
|
||||
<Item
|
||||
title={I18n.t('Send')}
|
||||
onPress={this.sendMessage}
|
||||
testID='send-message-share-view'
|
||||
buttonStyle={styles.send}
|
||||
/>
|
||||
</CustomHeaderButtons>
|
||||
)
|
||||
: null)
|
||||
});
|
||||
}
|
||||
|
||||
setReadOnly = async() => {
|
||||
const { room } = this.state;
|
||||
const { navigation, user } = this.props;
|
||||
const { user } = this.props;
|
||||
const { username } = user;
|
||||
const readOnly = await isReadOnly(room, { username });
|
||||
|
||||
this.setState({ readOnly });
|
||||
navigation.setParams({ canSend: !(readOnly || isBlocked(room)) });
|
||||
this.setState({ readOnly, canSend: !(readOnly || isBlocked(room)) });
|
||||
this.setHeader();
|
||||
}
|
||||
|
||||
bytesToSize = bytes => `${ (bytes / 1048576).toFixed(2) }MB`;
|
||||
|
||||
_sendMessage = async() => {
|
||||
sendMessage = async() => {
|
||||
const { isMedia, loading } = this.state;
|
||||
if (loading) {
|
||||
return;
|
||||
|
|
|
@ -31,6 +31,7 @@ const styles = StyleSheet.create({
|
|||
|
||||
class WithoutServerView extends React.Component {
|
||||
static navigationOptions = {
|
||||
title: 'Rocket.Chat',
|
||||
headerLeft: () => (
|
||||
<CancelModalButton
|
||||
onPress={ShareExtension.close}
|
||||
|
|
2
index.js
2
index.js
|
@ -18,7 +18,7 @@ if (__DEV__) {
|
|||
}
|
||||
|
||||
AppRegistry.registerComponent(appName, () => require('./app/index.js').default);
|
||||
// AppRegistry.registerComponent(shareName, () => require('./app/share.js').default);
|
||||
AppRegistry.registerComponent(shareName, () => require('./app/share.js').default);
|
||||
|
||||
// For storybook, comment everything above and uncomment below
|
||||
// import './storybook';
|
||||
|
|
Loading…
Reference in New Issue