Compare commits

...

18 Commits

Author SHA1 Message Date
Djorkaeff Alexandre 8142851fde [REGRESSION] Jitsi Call doesn't send message link (#2277) 2020-07-10 10:58:30 -03:00
Diego Mello 0ecfe0f780 [FIX] getSettings not catching errors (#2271) 2020-07-09 10:34:12 -03:00
Djorkaeff Alexandre b9a3828781 [FIX] Create discussion not working from MessageActions (#2265) 2020-07-08 17:57:28 -03:00
Diego Mello fd3f0a3417 [FIX] Notification preferences update crashing the app (#2262) 2020-07-08 17:54:39 -03:00
Diego Mello fa2c93fa2b [FIX] Mime type check crashing the app (#2264) 2020-07-08 17:54:34 -03:00
Diego Mello 118b1352c4 [FIX] Action sheet cutting emojis on the header (#2263)
* [FIX] Action sheet cutting emojis on the header

* fix tablet case

Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>
2020-07-08 17:54:26 -03:00
Diego Mello 3d987cc988 [FIX] Navigation object undefined when tapping sidebar's user header on tablet (#2259) 2020-07-08 17:54:18 -03:00
Diego Mello 766dd8f5e4 [FIX] Android stack animation throwing illegal node ID (#2260) 2020-07-08 17:54:11 -03:00
Djorkaeff Alexandre be718db8a5 [FIX] ImageViewer not recognising gestures after zoomed (#2261)
* [FIX] Zoomed in images must react to gestures

* AnimatedFastImage -> AnimatedImage

Co-authored-by: Diego Mello <diegolmello@gmail.com>
2020-07-08 17:53:50 -03:00
Djorkaeff Alexandre 3be67f5a4f [FIX] Get active route returning undefined (#2257)
Co-authored-by: Diego Mello <diegolmello@gmail.com>
2020-07-08 17:53:45 -03:00
Djorkaeff Alexandre 77917f66f0 [FIX] Register crashing when error data is undefined (#2256)
Co-authored-by: Diego Mello <diegolmello@gmail.com>
2020-07-08 17:53:39 -03:00
Djorkaeff Alexandre 7d2f4afaae [FIX] ThreadMessagesView throwing error when subscription wasn't found (#2255)
Co-authored-by: Diego Mello <diegolmello@gmail.com>
2020-07-08 17:53:34 -03:00
Djorkaeff Alexandre 5cf9291fc9 [FIX] Command previews crashing when API returns an error (#2254)
Co-authored-by: Diego Mello <diegolmello@gmail.com>
2020-07-08 17:53:29 -03:00
Djorkaeff Alexandre db0520ed7b [FIX] AttachmentView crashing during title decode (#2253)
Co-authored-by: Diego Mello <diegolmello@gmail.com>
2020-07-08 17:53:25 -03:00
Djorkaeff Alexandre 0583de7158 [FIX] Scroll to top crashing when ref is undefined (#2252) 2020-07-08 17:53:17 -03:00
Djorkaeff Alexandre 33edacf087 [FIX] Emoji keyboard not showing custom and frequently used emojis on Share Extension (#2251) 2020-07-08 17:51:24 -03:00
Diego Mello c3a80b6f1e Bump version to 4.8.1 2020-07-08 17:51:09 -03:00
Youssef Muhamad f15c755e03 [FIX] Check for UI_Use_Real_Name when sorting rooms (#2230)
Co-authored-by: Diego Mello <diegolmello@gmail.com>
2020-07-08 17:50:50 -03:00
22 changed files with 159 additions and 116 deletions

View File

@ -139,7 +139,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode VERSIONCODE as Integer
versionName "4.8.0"
versionName "4.8.1"
vectorDrawables.useSupportLibrary = true
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
missingDimensionStrategy "RNNotifications.reactNativeVersion", "reactNative60" // See note below!

View File

@ -29,7 +29,8 @@ export default StyleSheet.create({
},
handle: {
justifyContent: 'center',
alignItems: 'center'
alignItems: 'center',
paddingBottom: 8
},
handleIndicator: {
width: 40,

View File

@ -14,17 +14,20 @@ import { Button } from '../ActionSheet';
import { useDimensions } from '../../dimensions';
export const HEADER_HEIGHT = 36;
const ITEM_SIZE = 36;
const CONTAINER_MARGIN = 8;
const ITEM_MARGIN = 8;
const styles = StyleSheet.create({
container: {
alignItems: 'center',
marginHorizontal: 8
marginHorizontal: CONTAINER_MARGIN
},
headerItem: {
height: 36,
width: 36,
borderRadius: 20,
marginHorizontal: 8,
height: ITEM_SIZE,
width: ITEM_SIZE,
borderRadius: ITEM_SIZE / 2,
marginHorizontal: ITEM_MARGIN,
justifyContent: 'center',
alignItems: 'center'
},
@ -84,7 +87,7 @@ HeaderFooter.propTypes = {
};
const Header = React.memo(({
handleReaction, server, message, theme
handleReaction, server, message, isMasterDetail, theme
}) => {
const [items, setItems] = useState([]);
const { width, height } = useDimensions();
@ -96,8 +99,8 @@ const Header = React.memo(({
let freqEmojis = await freqEmojiCollection.query().fetch();
const isLandscape = width > height;
const size = isLandscape ? width / 2 : width;
const quantity = (size / 50) - 1;
const size = (isLandscape || isMasterDetail ? width / 2 : width) - (CONTAINER_MARGIN * 2);
const quantity = (size / (ITEM_SIZE + (ITEM_MARGIN * 2))) - 1;
freqEmojis = freqEmojis.concat(DEFAULT_EMOJIS).slice(0, quantity);
setItems(freqEmojis);
@ -135,6 +138,7 @@ Header.propTypes = {
handleReaction: PropTypes.func,
server: PropTypes.string,
message: PropTypes.object,
isMasterDetail: PropTypes.bool,
theme: PropTypes.string
};
export default withTheme(Header);

View File

@ -32,7 +32,8 @@ const MessageActions = React.memo(forwardRef(({
Message_AllowEditing_BlockEditInMinutes,
Message_AllowPinning,
Message_AllowStarring,
Message_Read_Receipt_Store_Users
Message_Read_Receipt_Store_Users,
isMasterDetail
}, ref) => {
let permissions = {};
const { showActionSheet, hideActionSheet } = useActionSheet();
@ -116,7 +117,12 @@ const MessageActions = React.memo(forwardRef(({
const handleEdit = message => editInit(message);
const handleCreateDiscussion = (message) => {
Navigation.navigate('CreateDiscussionView', { message, channel: room });
const params = { message, channel: room, showCloseModal: true };
if (isMasterDetail) {
Navigation.navigate('ModalStackNavigator', { screen: 'CreateDiscussionView', params });
} else {
Navigation.navigate('NewMessageStackNavigator', { screen: 'CreateDiscussionView', params });
}
};
const handleUnread = async(message) => {
@ -377,6 +383,7 @@ const MessageActions = React.memo(forwardRef(({
<Header
server={server}
handleReaction={handleReaction}
isMasterDetail={isMasterDetail}
message={message}
/>
) : null)
@ -412,7 +419,8 @@ const mapStateToProps = state => ({
Message_AllowEditing_BlockEditInMinutes: state.settings.Message_AllowEditing_BlockEditInMinutes,
Message_AllowPinning: state.settings.Message_AllowPinning,
Message_AllowStarring: state.settings.Message_AllowStarring,
Message_Read_Receipt_Store_Users: state.settings.Message_Read_Receipt_Store_Users
Message_Read_Receipt_Store_Users: state.settings.Message_Read_Receipt_Store_Users,
isMasterDetail: state.app.isMasterDetail
});
export default connect(mapStateToProps, null, null, { forwardRef: true })(MessageActions);

View File

@ -17,7 +17,7 @@ export default class EmojiKeyboard extends React.PureComponent {
constructor(props) {
super(props);
const state = store.getState();
this.baseUrl = state.server.server;
this.baseUrl = state.share.server || state.server.server;
}
onEmojiSelected = (emoji) => {

View File

@ -541,12 +541,14 @@ class MessageBox extends Component {
setCommandPreview = async(command, name, params) => {
const { rid } = this.props;
try {
const { preview } = await RocketChat.getCommandPreview(name, rid, params);
this.setState({ commandPreview: preview.items, showCommandPreview: true, command });
const { success, preview } = await RocketChat.getCommandPreview(name, rid, params);
if (success) {
return this.setState({ commandPreview: preview?.items, showCommandPreview: true, command });
}
} catch (e) {
this.setState({ commandPreview: [], showCommandPreview: true, command: {} });
log(e);
}
this.setState({ commandPreview: [], showCommandPreview: true, command: {} });
}
setInput = (text) => {

View File

@ -110,7 +110,9 @@ class DB {
Thread,
ThreadMessage,
Upload,
Permission
Permission,
CustomEmoji,
FrequentlyUsedEmoji
],
actionsEnabled: true
});

View File

@ -1,4 +1,3 @@
import { InteractionManager } from 'react-native';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import { Q } from '@nozbe/watermelondb';
@ -132,48 +131,47 @@ export default async function() {
const filteredSettingsIds = filteredSettings.map(s => s._id);
reduxStore.dispatch(addSettings(this.parseSettings(filteredSettings)));
InteractionManager.runAfterInteractions(async() => {
// filter server info
const serverInfo = filteredSettings.filter(i1 => serverInfoKeys.includes(i1._id));
const iconSetting = data.find(item => item._id === 'Assets_favicon_512');
await serverInfoUpdate(serverInfo, iconSetting);
await db.action(async() => {
const settingsCollection = db.collections.get('settings');
const allSettingsRecords = await settingsCollection
.query(Q.where('id', Q.oneOf(filteredSettingsIds)))
.fetch();
// filter server info
const serverInfo = filteredSettings.filter(i1 => serverInfoKeys.includes(i1._id));
const iconSetting = data.find(item => item._id === 'Assets_favicon_512');
await serverInfoUpdate(serverInfo, iconSetting);
// filter settings
let settingsToCreate = filteredSettings.filter(i1 => !allSettingsRecords.find(i2 => i1._id === i2.id));
let settingsToUpdate = allSettingsRecords.filter(i1 => filteredSettings.find(i2 => i1.id === i2._id));
await db.action(async() => {
const settingsCollection = db.collections.get('settings');
const allSettingsRecords = await settingsCollection
.query(Q.where('id', Q.oneOf(filteredSettingsIds)))
.fetch();
// Create
settingsToCreate = settingsToCreate.map(setting => settingsCollection.prepareCreate(protectedFunction((s) => {
s._raw = sanitizedRaw({ id: setting._id }, settingsCollection.schema);
Object.assign(s, setting);
})));
// filter settings
let settingsToCreate = filteredSettings.filter(i1 => !allSettingsRecords.find(i2 => i1._id === i2.id));
let settingsToUpdate = allSettingsRecords.filter(i1 => filteredSettings.find(i2 => i1.id === i2._id));
// Update
settingsToUpdate = settingsToUpdate.map((setting) => {
const newSetting = filteredSettings.find(s => s._id === setting.id);
return setting.prepareUpdate(protectedFunction((s) => {
Object.assign(s, newSetting);
}));
});
// Create
settingsToCreate = settingsToCreate.map(setting => settingsCollection.prepareCreate(protectedFunction((s) => {
s._raw = sanitizedRaw({ id: setting._id }, settingsCollection.schema);
Object.assign(s, setting);
})));
const allRecords = [
...settingsToCreate,
...settingsToUpdate
];
try {
await db.batch(...allRecords);
} catch (e) {
log(e);
}
return allRecords.length;
// Update
settingsToUpdate = settingsToUpdate.map((setting) => {
const newSetting = filteredSettings.find(s => s._id === setting.id);
return setting.prepareUpdate(protectedFunction((s) => {
Object.assign(s, newSetting);
}));
});
const allRecords = [
...settingsToCreate,
...settingsToUpdate
];
try {
await db.batch(...allRecords);
} catch (e) {
log(e);
}
return allRecords.length;
});
} catch (e) {
log(e);

View File

@ -288,6 +288,8 @@ const RocketChat = {
const serversDB = database.servers;
reduxStore.dispatch(shareSelectServer(server));
RocketChat.setCustomEmojis();
// set User info
try {
const userId = await RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ server }`);
@ -320,7 +322,7 @@ const RocketChat = {
updateJitsiTimeout(roomId) {
// RC 0.74.0
return this.post('jitsi.updateTimeout', { roomId });
return this.post('video-conference/jitsi.update-timeout', { roomId });
},
register(credentials) {

View File

@ -260,6 +260,21 @@ function bouncy(
const WIDTH = 300;
const HEIGHT = 300;
class Image extends React.PureComponent {
static propTypes = {
imageComponentType: PropTypes.string
}
render() {
const { imageComponentType } = this.props;
const Component = ImageComponent(imageComponentType);
return <Component {...this.props} />;
}
}
const AnimatedImage = Animated.createAnimatedComponent(Image);
// it was picked from https://github.com/software-mansion/react-native-reanimated/tree/master/Example/imageViewer
// and changed to use FastImage animated component
export class ImageViewer extends React.Component {
@ -386,12 +401,9 @@ export class ImageViewer extends React.Component {
render() {
const {
uri, width, height, theme, imageComponentType, ...props
uri, width, height, imageComponentType, theme, ...props
} = this.props;
const Component = ImageComponent(imageComponentType);
const AnimatedFastImage = Animated.createAnimatedComponent(Component);
// The below two animated values makes it so that scale appears to be done
// from the top left corner of the image view instead of its center. This
// is required for the "scale focal point" math to work correctly
@ -416,7 +428,7 @@ export class ImageViewer extends React.Component {
onGestureEvent={this._onPanEvent}
onHandlerStateChange={this._onPanEvent}
>
<AnimatedFastImage
<AnimatedImage
style={[
styles.image,
{
@ -435,6 +447,7 @@ export class ImageViewer extends React.Component {
]
}
]}
imageComponentType={imageComponentType}
resizeMode='contain'
source={{ uri }}
{...props}

View File

@ -54,8 +54,7 @@ export const FadeFromCenterModal = {
const forStackAndroid = ({
current,
inverted,
layouts: { screen },
closing
layouts: { screen }
}) => {
const translateX = multiply(
current.progress.interpolate({
@ -65,13 +64,12 @@ const forStackAndroid = ({
inverted
);
const opacity = conditional(
closing,
current.progress,
const opacity = multiply(
current.progress.interpolate({
inputRange: [0, 1],
outputRange: [0, 1]
})
}),
inverted
);
return {

View File

@ -46,9 +46,9 @@ export const navigationTheme = (theme) => {
// Gets the current screen from navigation state
export const getActiveRoute = (state) => {
const route = state.routes[state.index];
const route = state?.routes[state?.index];
if (route.state) {
if (route?.state) {
// Dive into nested navigators
return getActiveRoute(route.state);
}
@ -56,4 +56,4 @@ export const getActiveRoute = (state) => {
return route;
};
export const getActiveRouteName = state => getActiveRoute(state).name;
export const getActiveRouteName = state => getActiveRoute(state)?.name;

View File

@ -70,10 +70,15 @@ class AttachmentView extends React.Component {
setHeader = () => {
const { route, navigation, theme } = this.props;
const attachment = route.params?.attachment;
const { title } = attachment;
let { title } = attachment;
try {
title = decodeURI(title);
} catch {
// Do nothing
}
const options = {
title,
headerLeft: () => <CloseModalButton testID='close-attachment-view' navigation={navigation} buttonStyle={{ color: themes[theme].previewTintColor }} />,
title: decodeURI(title),
headerRight: () => <SaveButton testID='save-image' onPress={this.handleSave} buttonStyle={{ color: themes[theme].previewTintColor }} />,
headerBackground: () => <View style={{ flex: 1, backgroundColor: themes[theme].previewBackground }} />,
headerTintColor: themes[theme].previewTintColor,

View File

@ -16,6 +16,7 @@ import RocketChat from '../../lib/rocketchat';
import { withTheme } from '../../theme';
import protectedFunction from '../../lib/methods/helpers/protectedFunction';
import SafeAreaView from '../../containers/SafeAreaView';
import log from '../../utils/log';
const SectionTitle = React.memo(({ title, theme }) => (
<Text
@ -183,26 +184,30 @@ class NotificationPreferencesView extends React.Component {
const { room } = this.state;
const db = database.active;
await db.action(async() => {
await room.update(protectedFunction((r) => {
r[key] = value;
}));
});
try {
const result = await RocketChat.saveNotificationSettings(this.rid, params);
if (result.success) {
return;
}
} catch {
// do nothing
}
await db.action(async() => {
await room.update(protectedFunction((r) => {
r[key] = value;
}));
});
await db.action(async() => {
await room.update(protectedFunction((r) => {
r[key] = room[key];
}));
});
try {
const result = await RocketChat.saveNotificationSettings(this.rid, params);
if (result.success) {
return;
}
} catch {
// do nothing
}
await db.action(async() => {
await room.update(protectedFunction((r) => {
r[key] = room[key];
}));
});
} catch (e) {
log(e);
}
}
onValueChangeSwitch = (key, value) => this.saveNotificationSettings(key, value, { [key]: value ? '1' : '0' });

View File

@ -145,10 +145,12 @@ class RegisterView extends React.Component {
await loginRequest({ user: email, password });
}
} catch (e) {
if (e.data && e.data.errorType === 'username-invalid') {
if (e.data?.errorType === 'username-invalid') {
return loginRequest({ user: email, password });
}
showErrorAlert(e.data.error, I18n.t('Oops'));
if (e.data?.error) {
showErrorAlert(e.data.error, I18n.t('Oops'));
}
}
this.setState({ saving: false });
}

View File

@ -755,7 +755,7 @@ class RoomView extends React.Component {
if (handleCommandScroll(event)) {
const offset = input === 'UIKeyInputUpArrow' ? 100 : -100;
this.offset += offset;
this.flatList.scrollToOffset({ offset: this.offset });
this.flatList?.scrollToOffset({ offset: this.offset });
} else if (handleCommandRoomActions(event)) {
this.goRoomActionsView();
} else if (handleCommandSearchMessages(event)) {

View File

@ -411,7 +411,7 @@ class RoomsListView extends React.Component {
let tempChats = [];
let chats = [];
if (sortBy === 'alphabetical') {
chats = orderBy(data, ['name'], ['asc']);
chats = orderBy(data, [`${ this.useRealName ? 'fname' : 'name' }`], ['asc']);
} else {
chats = orderBy(data, ['roomUpdatedAt'], ['desc']);
}
@ -504,12 +504,7 @@ class RoomsListView extends React.Component {
closeSearchHeader();
}
setTimeout(() => {
const offset = isAndroid ? 0 : SCROLL_OFFSET;
if (this.scroll.scrollTo) {
this.scroll.scrollTo({ x: 0, y: offset, animated: true });
} else if (this.scroll.scrollToOffset) {
this.scroll.scrollToOffset({ offset });
}
this.scrollToTop();
}, 200);
});
};
@ -538,9 +533,7 @@ class RoomsListView extends React.Component {
search: result,
searching: true
});
if (this.scroll && this.scroll.scrollTo) {
this.scroll.scrollTo({ x: 0, y: 0, animated: true });
}
this.scrollToTop();
}, 300);
getRoomTitle = item => RocketChat.getRoomTitle(item)
@ -561,15 +554,17 @@ class RoomsListView extends React.Component {
this.goRoom({ item, isMasterDetail });
};
scrollToTop = () => {
const offset = isAndroid ? 0 : SCROLL_OFFSET;
if (this.scroll?.scrollToOffset) {
this.scroll.scrollToOffset({ offset });
}
}
toggleSort = () => {
const { toggleSortDropdown } = this.props;
const offset = isAndroid ? 0 : SCROLL_OFFSET;
if (this.scroll.scrollTo) {
this.scroll.scrollTo({ x: 0, y: offset, animated: true });
} else if (this.scroll.scrollToOffset) {
this.scroll.scrollToOffset({ offset });
}
this.scrollToTop();
setTimeout(() => {
toggleSortDropdown();
}, 100);

View File

@ -123,7 +123,7 @@ class ShareView extends Component {
item.error = error;
// get video thumbnails
if (item.mime?.match(/video/)) {
if (item.mime?.match?.(/video/)) {
try {
const { uri } = await VideoThumbnails.getThumbnailAsync(item.path);
item.uri = uri;

View File

@ -141,7 +141,15 @@ class Sidebar extends Component {
get currentItemKey() {
const { state } = this.props;
return state.routeNames[state.index];
return state?.routeNames[state?.index];
}
onPressUser = () => {
const { navigation, isMasterDetail } = this.props;
if (isMasterDetail) {
return;
}
navigation.closeDrawer();
}
renderAdmin = () => {
@ -210,7 +218,7 @@ class Sidebar extends Component {
render() {
const {
user, Site_Name, baseUrl, useRealName, allowStatusMessage, isMasterDetail, theme, navigation
user, Site_Name, baseUrl, useRealName, allowStatusMessage, isMasterDetail, theme
} = this.props;
if (!user) {
@ -229,7 +237,7 @@ class Sidebar extends Component {
]}
{...scrollPersistTaps}
>
<TouchableWithoutFeedback onPress={() => navigation.closeDrawer()} testID='sidebar-close-drawer'>
<TouchableWithoutFeedback onPress={this.onPressUser} testID='sidebar-close-drawer'>
<View style={styles.header} theme={theme}>
<Avatar
text={user.username}

View File

@ -127,7 +127,7 @@ class ThreadMessagesView extends React.Component {
// eslint-disable-next-line react/sort-comp
init = () => {
if (!this.subscription) {
this.load();
return this.load();
}
try {
const lastThreadSync = new Date();

View File

@ -19,7 +19,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>4.8.0</string>
<string>4.8.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>

View File

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>4.8.0</string>
<string>4.8.1</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSAppTransportSecurity</key>