diff --git a/__tests__/__snapshots__/Storyshots.test.js.snap b/__tests__/__snapshots__/Storyshots.test.js.snap
index d2bba5818..b30a37fed 100644
--- a/__tests__/__snapshots__/Storyshots.test.js.snap
+++ b/__tests__/__snapshots__/Storyshots.test.js.snap
@@ -6796,7 +6796,7 @@ exports[`Storyshots List with black theme 1`] = `
`;
-exports[`Storyshots List with custom color 1`] = `
+exports[`Storyshots List with custom colors 1`] = `
+
+
+
+ Press me!
+
+
+
+
`;
@@ -33112,33 +33171,6 @@ exports[`Storyshots Message list message 1`] = `
}
testID="message-thread-button-How are you?"
>
-
-
-
- 1 reply
+ Reply
-
- November 10, 2017
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- diego.mello
-
-
-
- 10:00 AM
-
-
-
-
-
- How are you?
-
-
-
-
-
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
-
-
-
- +999 replies
-
-
-
- November 10, 2017
-
-
-
-
-
-
+ "opacity": 1,
+ }
+ }
+ >
+
+
+
+
+
@@ -35583,33 +35342,6 @@ exports[`Storyshots Message list message 1`] = `
}
testID="message-thread-button-How are you?"
>
-
-
-
- 1 reply
+ Reply
-
- November 10, 2017
-
-
-
-
-
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
@@ -61221,6 +61066,1157 @@ exports[`Storyshots RoomItem list roomitem 1`] = `
`;
+exports[`Storyshots ServerItem content 1`] = `
+Array [
+
+
+
+
+
+
+
+ Rocket.Chat
+
+
+ https://open.rocket.chat/
+
+
+
+
+
+
+ ,
+
+
+
+
+
+
+
+ Super Long Server Name in Rocket.Chat
+
+
+ https://superlongservername.tologintoasuperlongservername/
+
+
+
+ ,
+
+
+
+
+
+
+
+ https://stable.rocket.chat/
+
+
+ https://stable.rocket.chat/
+
+
+
+ ,
+]
+`;
+
+exports[`Storyshots ServerItem themes 1`] = `
+Array [
+
+
+
+
+
+
+
+ Rocket.Chat
+
+
+ https://open.rocket.chat/
+
+
+
+
+
+
+ ,
+
+
+
+
+
+
+
+ Rocket.Chat
+
+
+ https://open.rocket.chat/
+
+
+
+
+
+
+ ,
+
+
+
+
+
+
+
+ Rocket.Chat
+
+
+ https://open.rocket.chat/
+
+
+
+
+
+
+ ,
+]
+`;
+
+exports[`Storyshots ServerItem touchable 1`] = `
+Array [
+
+
+
+
+
+
+
+ Rocket.Chat
+
+
+ https://open.rocket.chat/
+
+
+
+ ,
+
+
+
+
+
+
+
+ Rocket.Chat
+
+
+ https://open.rocket.chat/
+
+
+
+ ,
+
+
+
+
+
+
+
+ Rocket.Chat
+
+
+ https://open.rocket.chat/
+
+
+
+ ,
+]
+`;
+
exports[`Storyshots Thread Messages.Item badge 1`] = `
@@ -61238,144 +62234,85 @@ exports[`Storyshots Thread Messages.Item badge 1`] = `
}
/>
-
-
-
-
-
-
- rocket.cat
-
-
- November 10, 2020
-
-
-
- Message content
-
+
+
+
+
@@ -61384,80 +62321,29 @@ exports[`Storyshots Thread Messages.Item badge 1`] = `
Object {
"alignItems": "center",
"flexDirection": "row",
- "justifyContent": "center",
- "marginRight": 8,
+ "marginBottom": 2,
}
}
>
-
-
-
- 1
-
-
-
-
-
+ rocket.cat
- 1
-
-
-
-
-
-
-
+
+
+ Message content
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
- rocket.cat
-
-
- November 10, 2020
-
-
-
- Message content
-
+
+
+
+
@@ -61715,80 +62686,29 @@ exports[`Storyshots Thread Messages.Item badge 1`] = `
Object {
"alignItems": "center",
"flexDirection": "row",
- "justifyContent": "center",
- "marginRight": 8,
+ "marginBottom": 2,
}
}
>
-
-
-
- 1
-
-
-
-
-
+ rocket.cat
- 1
-
-
-
-
-
-
-
+
+
+ Message content
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
- rocket.cat
-
-
- November 10, 2020
-
-
-
- Message content
-
+
+
+
+
@@ -62046,80 +63051,29 @@ exports[`Storyshots Thread Messages.Item badge 1`] = `
Object {
"alignItems": "center",
"flexDirection": "row",
- "justifyContent": "center",
- "marginRight": 8,
+ "marginBottom": 2,
}
}
>
-
-
-
- 1
-
-
-
-
-
+ rocket.cat
- 1
-
-
-
-
-
-
-
+
+
+ Message content
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
- rocket.cat
-
-
- November 10, 2020
-
-
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
-
+
+
+
+
@@ -62364,80 +63403,29 @@ exports[`Storyshots Thread Messages.Item badge 1`] = `
Object {
"alignItems": "center",
"flexDirection": "row",
- "justifyContent": "center",
- "marginRight": 8,
+ "marginBottom": 2,
}
}
>
-
-
-
- 1
-
-
-
-
-
+ rocket.cat
- 1
-
-
-
-
-
-
-
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
- rocket.cat
-
-
- November 10, 2020
-
-
-
- Message content
-
+
+
+
+
@@ -62715,80 +63788,29 @@ exports[`Storyshots Thread Messages.Item content 1`] = `
Object {
"alignItems": "center",
"flexDirection": "row",
- "justifyContent": "center",
- "marginRight": 8,
+ "marginBottom": 2,
}
}
>
-
-
-
- 1
-
-
-
-
-
+ rocket.cat
- 1
-
-
-
-
-
-
-
+
+
+ Message content
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
@@ -62877,144 +64050,85 @@ exports[`Storyshots Thread Messages.Item content 1`] = `
}
/>
-
-
-
-
-
-
- rocket.cat
-
-
- November 10, 2020
-
-
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
-
+
+
+
+
@@ -63023,80 +64137,29 @@ exports[`Storyshots Thread Messages.Item content 1`] = `
Object {
"alignItems": "center",
"flexDirection": "row",
- "justifyContent": "center",
- "marginRight": 8,
+ "marginBottom": 2,
}
}
>
-
-
-
- 1
-
-
-
-
-
+ rocket.cat
- 1
-
-
-
-
-
-
-
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
@@ -63185,144 +64399,85 @@ exports[`Storyshots Thread Messages.Item content 1`] = `
}
/>
-
-
-
-
-
-
- rocket.cat
-
-
- November 10, 2020
-
-
-
- Message content
-
+
+
+
+
@@ -63331,80 +64486,29 @@ exports[`Storyshots Thread Messages.Item content 1`] = `
Object {
"alignItems": "center",
"flexDirection": "row",
- "justifyContent": "center",
- "marginRight": 8,
+ "marginBottom": 2,
}
}
>
-
-
-
- 1000
-
-
-
-
-
+ rocket.cat
- 1000
-
-
-
-
-
-
-
+
+
+ Message content
+
+
+
+
+
+
+
+
+
+ +999
+
+
+
+
+
+
+
+ +999
+
+
+
+
+
+
+
+
+
+
+
@@ -63493,144 +64748,85 @@ exports[`Storyshots Thread Messages.Item content 1`] = `
}
/>
-
-
-
-
-
-
- rocket.cat
-
-
- November 10, 2020
-
-
-
- Attachment title
-
+
+
+
+
@@ -63639,80 +64835,29 @@ exports[`Storyshots Thread Messages.Item content 1`] = `
Object {
"alignItems": "center",
"flexDirection": "row",
- "justifyContent": "center",
- "marginRight": 8,
+ "marginBottom": 2,
}
}
>
-
-
-
- 1
-
-
-
-
-
+ rocket.cat
- 1
-
-
-
-
-
-
-
+
+
+ Attachment title
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
@@ -63801,144 +65097,85 @@ exports[`Storyshots Thread Messages.Item content 1`] = `
}
/>
-
-
-
-
-
-
- Rocket Cat
-
-
- November 10, 2020
-
-
-
- Message content
-
+
+
+
+
@@ -63947,80 +65184,29 @@ exports[`Storyshots Thread Messages.Item content 1`] = `
Object {
"alignItems": "center",
"flexDirection": "row",
- "justifyContent": "center",
- "marginRight": 8,
+ "marginBottom": 2,
}
}
>
-
-
-
- 1
-
-
-
-
-
+ Rocket Cat
- 1
-
-
-
-
-
-
-
+
+
+ Message content
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
@@ -64129,144 +65466,85 @@ exports[`Storyshots Thread Messages.Item themes 1`] = `
}
/>
-
-
-
-
-
-
- rocket.cat
-
-
- November 10, 2020
-
-
-
- Message content
-
+
+
+
+
@@ -64275,80 +65553,29 @@ exports[`Storyshots Thread Messages.Item themes 1`] = `
Object {
"alignItems": "center",
"flexDirection": "row",
- "justifyContent": "center",
- "marginRight": 8,
+ "marginBottom": 2,
}
}
>
-
-
-
- 1
-
-
-
-
-
+ rocket.cat
- 1
-
-
-
-
-
-
-
+
+
+ Message content
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
- rocket.cat
-
-
- November 10, 2020
-
-
-
- Message content
-
+
+
+
+
@@ -64593,80 +65905,29 @@ exports[`Storyshots Thread Messages.Item themes 1`] = `
Object {
"alignItems": "center",
"flexDirection": "row",
- "justifyContent": "center",
- "marginRight": 8,
+ "marginBottom": 2,
}
}
>
-
-
-
- 1
-
-
-
-
-
+ rocket.cat
- 1
-
-
-
-
-
-
-
+
+
+ Message content
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
- rocket.cat
-
-
- November 10, 2020
-
-
-
- Message content
-
+
+
+
+
@@ -64911,80 +66257,29 @@ exports[`Storyshots Thread Messages.Item themes 1`] = `
Object {
"alignItems": "center",
"flexDirection": "row",
- "justifyContent": "center",
- "marginRight": 8,
+ "marginBottom": 2,
}
}
>
-
-
-
- 1
-
-
-
-
-
+ rocket.cat
- 1
-
-
-
-
-
-
-
+
+
+ Message content
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
-
-
-
diff --git a/android/app/src/main/java/chat/rocket/reactnative/MainActivity.java b/android/app/src/main/java/chat/rocket/reactnative/MainActivity.java
index 5eefab57a..e93eb3f96 100644
--- a/android/app/src/main/java/chat/rocket/reactnative/MainActivity.java
+++ b/android/app/src/main/java/chat/rocket/reactnative/MainActivity.java
@@ -14,7 +14,6 @@ import com.facebook.react.ReactFragmentActivity;
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
import com.zoontek.rnbootsplash.RNBootSplash;
-import com.tencent.mmkv.MMKV;
import com.google.gson.Gson;
class ThemePreferences {
@@ -36,61 +35,11 @@ public class MainActivity extends ReactFragmentActivity {
// https://github.com/software-mansion/react-native-screens/issues/17#issuecomment-424704067
super.onCreate(null);
RNBootSplash.init(R.drawable.launch_screen, MainActivity.this);
+ }
- MMKV.initialize(MainActivity.this);
-
- // Start the MMKV container
- MMKV defaultMMKV = MMKV.defaultMMKV();
- boolean alreadyMigrated = defaultMMKV.decodeBool("alreadyMigrated");
-
- if (!alreadyMigrated) {
- // MMKV Instance that will be used by JS
- MMKV mmkv = MMKV.mmkvWithID("default");
-
- // SharedPreferences -> MMKV (Migration)
- SharedPreferences sharedPreferences = getSharedPreferences("react-native", Context.MODE_PRIVATE);
- mmkv.importFromSharedPreferences(sharedPreferences);
-
- // SharedPreferences only save strings, so we saved this value as a String and now we'll need to cast into a MMKV object
-
- // Theme preferences object
- String THEME_PREFERENCES_KEY = "RC_THEME_PREFERENCES_KEY";
- String themeJson = sharedPreferences.getString(THEME_PREFERENCES_KEY, "");
- if (!themeJson.isEmpty()) {
- ThemePreferences themePreferences = new Gson().fromJson(themeJson, ThemePreferences.class);
- WritableMap themeMap = new Arguments().createMap();
- themeMap.putString("currentTheme", themePreferences.currentTheme);
- themeMap.putString("darkLevel", themePreferences.darkLevel);
- Bundle bundle = Arguments.toBundle(themeMap);
- mmkv.encode(THEME_PREFERENCES_KEY, bundle);
- }
-
- // Sort preferences object
- String SORT_PREFS_KEY = "RC_SORT_PREFS_KEY";
- String sortJson = sharedPreferences.getString(SORT_PREFS_KEY, "");
- if (!sortJson.isEmpty()) {
- SortPreferences sortPreferences = new Gson().fromJson(sortJson, SortPreferences.class);
- WritableMap sortMap = new Arguments().createMap();
- sortMap.putString("sortBy", sortPreferences.sortBy);
- if (sortPreferences.groupByType != null) {
- sortMap.putBoolean("groupByType", sortPreferences.groupByType);
- }
- if (sortPreferences.showFavorites != null) {
- sortMap.putBoolean("showFavorites", sortPreferences.showFavorites);
- }
- if (sortPreferences.showUnread != null) {
- sortMap.putBoolean("showUnread", sortPreferences.showUnread);
- }
- Bundle bundle = Arguments.toBundle(sortMap);
- mmkv.encode(SORT_PREFS_KEY, bundle);
- }
-
- // Remove all our keys of SharedPreferences
- sharedPreferences.edit().clear().commit();
-
- // Mark migration complete
- defaultMMKV.encode("alreadyMigrated", true);
- }
+ @Override
+ public void invokeDefaultOnBackPressed() {
+ moveTaskToBack(true);
}
/**
diff --git a/android/app/src/main/res/drawable-hdpi/logo.png b/android/app/src/main/res/drawable-hdpi/logo.png
deleted file mode 100755
index b4d275593..000000000
Binary files a/android/app/src/main/res/drawable-hdpi/logo.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-mdpi/logo.png b/android/app/src/main/res/drawable-mdpi/logo.png
deleted file mode 100755
index 9b3c622a0..000000000
Binary files a/android/app/src/main/res/drawable-mdpi/logo.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xhdpi/logo.png b/android/app/src/main/res/drawable-xhdpi/logo.png
deleted file mode 100755
index b1d639a32..000000000
Binary files a/android/app/src/main/res/drawable-xhdpi/logo.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/logo.png b/android/app/src/main/res/drawable-xxhdpi/logo.png
deleted file mode 100755
index 86ef8a102..000000000
Binary files a/android/app/src/main/res/drawable-xxhdpi/logo.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/logo.png b/android/app/src/main/res/drawable-xxxhdpi/logo.png
deleted file mode 100755
index c08988975..000000000
Binary files a/android/app/src/main/res/drawable-xxxhdpi/logo.png and /dev/null differ
diff --git a/android/app/src/play/java/chat/rocket/reactnative/CustomPushNotification.java b/android/app/src/play/java/chat/rocket/reactnative/CustomPushNotification.java
index 23179fd60..9311f7ebd 100644
--- a/android/app/src/play/java/chat/rocket/reactnative/CustomPushNotification.java
+++ b/android/app/src/play/java/chat/rocket/reactnative/CustomPushNotification.java
@@ -257,7 +257,6 @@ public class CustomPushNotification extends PushNotification {
Notification.MessagingStyle messageStyle;
Gson gson = new Gson();
- Ejson ejson = gson.fromJson(bundle.getString("ejson", "{}"), Ejson.class);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
messageStyle = new Notification.MessagingStyle("");
@@ -278,25 +277,19 @@ public class CustomPushNotification extends PushNotification {
long timestamp = data.getLong("time");
String message = data.getString("message");
- String username = data.getString("username");
String senderId = data.getString("senderId");
String avatarUri = data.getString("avatarUri");
-
+ Ejson ejson = gson.fromJson(data.getString("ejson", "{}"), Ejson.class);
String m = extractMessage(message, ejson);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
- messageStyle.addMessage(m, timestamp, username);
+ messageStyle.addMessage(m, timestamp, ejson.senderName);
} else {
Bitmap avatar = getAvatar(avatarUri);
- String name = username;
- if (ejson.senderName != null) {
- name = ejson.senderName;
- }
-
Person.Builder sender = new Person.Builder()
.setKey(senderId)
- .setName(name);
+ .setName(ejson.senderName);
if (avatar != null) {
sender.setIcon(Icon.createWithBitmap(avatar));
diff --git a/app/AppContainer.js b/app/AppContainer.js
index 4023635f7..9ec17ad43 100644
--- a/app/AppContainer.js
+++ b/app/AppContainer.js
@@ -7,7 +7,7 @@ import { connect } from 'react-redux';
import Navigation from './lib/Navigation';
import { defaultHeader, getActiveRouteName, navigationTheme } from './utils/navigation';
import {
- ROOT_LOADING, ROOT_OUTSIDE, ROOT_NEW_SERVER, ROOT_INSIDE, ROOT_SET_USERNAME, ROOT_BACKGROUND
+ ROOT_LOADING, ROOT_OUTSIDE, ROOT_NEW_SERVER, ROOT_INSIDE, ROOT_SET_USERNAME
} from './actions/app';
// Stacks
@@ -65,7 +65,7 @@ const App = React.memo(({ root, isMasterDetail }) => {
>
<>
- {root === ROOT_LOADING || root === ROOT_BACKGROUND ? (
+ {root === ROOT_LOADING ? (
(
onPress(props.title)}
- style={{ backgroundColor: themes[props.theme].backgroundColor }}
+ style={{ backgroundColor: backgroundColor || themes[props.theme].backgroundColor }}
+ underlayColor={underlayColor}
enabled={!props.disabled}
theme={props.theme}
>
@@ -99,7 +97,7 @@ const ListItem = React.memo(({ ...props }) => {
return ;
}
return (
-
+
);
@@ -107,7 +105,8 @@ const ListItem = React.memo(({ ...props }) => {
ListItem.propTypes = {
onPress: PropTypes.func,
- theme: PropTypes.string
+ theme: PropTypes.string,
+ backgroundColor: PropTypes.string
};
ListItem.displayName = 'List.Item';
@@ -137,7 +136,9 @@ Button.propTypes = {
title: PropTypes.string,
onPress: PropTypes.func,
disabled: PropTypes.bool,
- theme: PropTypes.string
+ theme: PropTypes.string,
+ backgroundColor: PropTypes.string,
+ underlayColor: PropTypes.string
};
Button.defaultProps = {
diff --git a/app/containers/MessageBox/index.js b/app/containers/MessageBox/index.js
index f39839059..12fad3f15 100644
--- a/app/containers/MessageBox/index.js
+++ b/app/containers/MessageBox/index.js
@@ -1,7 +1,7 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
- View, Alert, Keyboard, NativeModules, Text, InteractionManager
+ View, Alert, Keyboard, NativeModules, Text
} from 'react-native';
import { connect } from 'react-redux';
import { KeyboardAccessoryView } from 'react-native-ui-lib/keyboard';
@@ -224,12 +224,12 @@ class MessageBox extends Component {
this.unsubscribeFocus = navigation.addListener('focus', () => {
// didFocus
// We should wait pushed views be dismissed
- InteractionManager.runAfterInteractions(() => {
+ this.trackingTimeout = setTimeout(() => {
if (this.tracking && this.tracking.resetTracking) {
// Reset messageBox keyboard tracking
this.tracking.resetTracking();
}
- });
+ }, 500);
});
this.unsubscribeBlur = navigation.addListener('blur', () => {
this.component?.blur();
@@ -258,6 +258,10 @@ class MessageBox extends Component {
} else if (!nextProps.message) {
this.clearInput();
}
+ if (this.trackingTimeout) {
+ clearTimeout(this.trackingTimeout);
+ this.trackingTimeout = false;
+ }
}
shouldComponentUpdate(nextProps, nextState) {
@@ -932,7 +936,7 @@ class MessageBox extends Component {
/>
this.component = component}
- style={styles.textBoxInput}
+ style={[styles.textBoxInput, { color: themes[theme].bodyText }]}
returnKeyType='default'
keyboardType='twitter'
blurOnSubmit={false}
diff --git a/app/containers/ThreadDetails.js b/app/containers/ThreadDetails.js
new file mode 100644
index 000000000..e567a4cf8
--- /dev/null
+++ b/app/containers/ThreadDetails.js
@@ -0,0 +1,103 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { View, Text, StyleSheet } from 'react-native';
+import Touchable from 'react-native-platform-touchable';
+
+import { CustomIcon } from '../lib/Icons';
+import { themes } from '../constants/colors';
+import sharedStyles from '../views/Styles';
+import { withTheme } from '../theme';
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ flexDirection: 'row',
+ alignItems: 'center'
+ },
+ detailsContainer: {
+ flex: 1,
+ flexDirection: 'row'
+ },
+ detailContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginRight: 8
+ },
+ detailText: {
+ fontSize: 10,
+ marginLeft: 2,
+ ...sharedStyles.textSemibold
+ },
+ badgeContainer: {
+ flexDirection: 'row',
+ alignItems: 'center'
+ },
+ badge: {
+ width: 8,
+ height: 8,
+ borderRadius: 4,
+ marginRight: 8
+ }
+});
+
+const ThreadDetails = ({
+ item,
+ user,
+ badgeColor,
+ toggleFollowThread,
+ style,
+ theme
+}) => {
+ let { tcount } = item;
+ if (tcount >= 1000) {
+ tcount = '+999';
+ } else if (tcount >= 100) {
+ tcount = '+99';
+ }
+
+ let replies = item?.replies?.length ?? 0;
+ if (replies >= 1000) {
+ replies = '+999';
+ } else if (replies >= 100) {
+ replies = '+99';
+ }
+
+ const isFollowing = item.replies?.find(u => u === user?.id);
+
+ return (
+
+
+
+
+ {tcount}
+
+
+
+
+ {replies}
+
+
+
+
+ {badgeColor ? : null }
+ toggleFollowThread?.(isFollowing, item.id)}>
+
+
+
+
+ );
+};
+ThreadDetails.propTypes = {
+ item: PropTypes.object,
+ user: PropTypes.object,
+ badgeColor: PropTypes.string,
+ toggleFollowThread: PropTypes.func,
+ style: PropTypes.object,
+ theme: PropTypes.string
+};
+
+export default withTheme(ThreadDetails);
diff --git a/app/containers/message/Thread.js b/app/containers/message/Thread.js
index 320bb3cc3..47d9c8692 100644
--- a/app/containers/message/Thread.js
+++ b/app/containers/message/Thread.js
@@ -1,15 +1,12 @@
import React, { useContext } from 'react';
import { View, Text } from 'react-native';
import PropTypes from 'prop-types';
-import Touchable from 'react-native-platform-touchable';
-import { formatMessageCount } from './utils';
import styles from './styles';
-import { CustomIcon } from '../../lib/Icons';
-import { THREAD } from './constants';
import { themes } from '../../constants/colors';
-import { formatDateThreads } from '../../utils/room';
import MessageContext from './Context';
+import ThreadDetails from '../ThreadDetails';
+import I18n from '../../i18n';
const Thread = React.memo(({
msg, tcount, tlm, isThreadRoom, theme, id
@@ -21,28 +18,26 @@ const Thread = React.memo(({
const {
threadBadgeColor, toggleFollowThread, user, replies
} = useContext(MessageContext);
- const time = formatDateThreads(tlm);
- const buttonText = formatMessageCount(tcount, THREAD);
- const isFollowing = replies?.find(u => u === user.id);
return (
-
- {buttonText}
+ {I18n.t('Reply')}
- {time}
- {threadBadgeColor ? : null}
- toggleFollowThread(isFollowing, id)}>
-
-
+
);
}, (prevProps, nextProps) => {
diff --git a/app/containers/message/styles.js b/app/containers/message/styles.js
index 74e2b0001..dd0dff627 100644
--- a/app/containers/message/styles.js
+++ b/app/containers/message/styles.js
@@ -176,5 +176,9 @@ export default StyleSheet.create({
},
encrypted: {
justifyContent: 'center'
+ },
+ threadDetails: {
+ flex: 1,
+ marginLeft: 12
}
});
diff --git a/app/ee/omnichannel/containers/OmnichannelStatus.js b/app/ee/omnichannel/containers/OmnichannelStatus.js
index 7ae99a61e..148f398cf 100644
--- a/app/ee/omnichannel/containers/OmnichannelStatus.js
+++ b/app/ee/omnichannel/containers/OmnichannelStatus.js
@@ -1,12 +1,8 @@
import React, { memo, useState, useEffect } from 'react';
-import {
- View, Text, StyleSheet, Switch
-} from 'react-native';
+import { View, Switch } from 'react-native';
import PropTypes from 'prop-types';
-import Touch from '../../../utils/touch';
-import { CustomIcon } from '../../../lib/Icons';
-import I18n from '../../../i18n';
+import * as List from '../../../containers/List';
import styles from '../../../views/RoomsListView/styles';
import { themes, SWITCH_TRACK_COLOR } from '../../../constants/colors';
import { withTheme } from '../../../theme';
@@ -36,35 +32,32 @@ const OmnichannelStatus = memo(({
};
return (
-
-
-
- {I18n.t('Omnichannel')}
- {inquiryEnabled
- ? (
-
+ }
+ color={themes[theme].auxiliaryText}
+ onPress={goQueue}
+ right={() => (
+
+ {inquiryEnabled
+ ? (
+
+ )
+ : null}
+
- )
- : null}
-
-
-
+
+ )}
+ />
+
+ >
);
});
diff --git a/app/i18n/index.js b/app/i18n/index.js
index 5217e48da..b7436640b 100644
--- a/app/i18n/index.js
+++ b/app/i18n/index.js
@@ -57,6 +57,10 @@ export const LANGUAGES = [
label: 'العربية',
value: 'ar',
file: require('./locales/ar').default
+ }, {
+ label: 'Türkçe',
+ value: 'tr',
+ file: require('./locales/tr').default
}
];
diff --git a/app/i18n/locales/ar.js b/app/i18n/locales/ar.js
index 6cb0ff979..a80b52485 100644
--- a/app/i18n/locales/ar.js
+++ b/app/i18n/locales/ar.js
@@ -102,7 +102,7 @@ export default {
Apply_Your_Certificate: 'طبق شهادتك',
Applying_a_theme_will_change_how_the_app_looks: 'سيؤدي تطبيق السمة إلى تغيير شكل التطبيق',
ARCHIVE: 'أرشفة',
- archive: 'أرشيف',
+ archive: 'أرشفة',
are_typing: 'يكتب',
Are_you_sure_question_mark: 'هل أنت متأكد؟',
Are_you_sure_you_want_to_leave_the_room: 'متأكد من مغادرة الغرفة {{room}}؟',
@@ -129,8 +129,8 @@ export default {
creating_invite: 'إنشاء دعوة',
Channel_Name: 'اسم القناة',
Channels: 'قنوات',
- Chats: 'محادثات',
- Call_already_ended: 'انتهت المكالمة بالفعل!',
+ Chats: 'الرسائل',
+ Call_already_ended: 'تم انهاء المكالمة بالفعل !',
Clear_cookies_alert: 'هل تريد حذف جميع ملفات تعريف الإرتباط؟',
Clear_cookies_desc: 'هذا الإجراء سيحذف ملفات تعريف الإرتباط الخاصة بتسجيل الدخول مما يسمح بتسجيل الدخول لحسابات أخرى',
Clear_cookies_yes: 'نعم، مسح ملفات الإرتباط',
@@ -478,8 +478,8 @@ export default {
Send_to: 'إرسال إلى...',
Sending_to: 'يتم الإرسال إلى',
Sent_an_attachment: 'تم إرسال المرفق',
- Server: 'خادم',
- Servers: 'خوادم',
+ Server: 'سرفر',
+ Servers: 'سرفرات',
Server_version: 'نسخة الخادم: {{version}}',
Set_username_subtitle: 'يتم استخدام اسم المستخدم للسماح للآخرين بذكرك في الرسائل',
Set_custom_status: 'حدد حالة خاصة',
@@ -562,7 +562,6 @@ export default {
Username: 'اسم المستخدم',
Username_or_email: 'اسم المستخدم أو البريد الالكتروني',
Uses_server_configuration: 'يستخدم إعداد الخادم',
- Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: 'عادةً، النقاش يبدأ بسؤال، على سبيل المثال: كيف أرفع صورة؟',
Validating: 'يتم التحقق',
Registration_Succeeded: 'تم التسجيل بنجاح',
Verify: 'تحقق',
@@ -597,7 +596,6 @@ export default {
You_need_to_access_at_least_one_RocketChat_server_to_share_something: 'تحتاج إلى الوصول إلى خادم Rocket.Chat واحد على الأقل لمشاركة شيء ما',
You_need_to_verifiy_your_email_address_to_get_notications: 'يجب تأكيد البريد الإلكتروني حتى تصلك الإشعارات',
Your_certificate: 'شهادتك',
- Your_message: 'رسالتك',
Your_invite_link_will_expire_after__usesLeft__uses: 'سوف تنتهي صلاحية رابط الدعوة الخاص بك بعد {{usesLeft}} استخدامات',
Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: 'ستنتهي صلاحية رابط الدعوة الخاص بك في {{date}} أو بعد {{usesLeft}} استخدامات',
Your_invite_link_will_expire_on__date__: 'ستنتهي صلاحية رابط الدعوة الخاص بك في {{date}}',
diff --git a/app/i18n/locales/de.js b/app/i18n/locales/de.js
index 2413ee66c..02e98bec6 100644
--- a/app/i18n/locales/de.js
+++ b/app/i18n/locales/de.js
@@ -280,6 +280,8 @@ export default {
Invite_Link: 'Einladungs-Link',
Invite_users: 'Benutzer einladen',
Join: 'Beitreten',
+ Join_Code: 'Beitrittscode',
+ Insert_Join_Code: 'Beitrittscode eingeben',
Join_our_open_workspace: 'Tritt unserem offenen Arbeitsbereich bei',
Join_your_workspace: 'Tritt deinem Arbeitsbereich bei',
Just_invited_people_can_access_this_channel: 'Nur eingeladene Personen können auf diesen Kanal zugreifen',
@@ -318,7 +320,7 @@ export default {
Message_Reported: 'Nachricht gemeldet',
Microphone_Permission_Message: 'Rocket.Chat benötigt Zugriff auf das Mikrofon, damit du eine Audionachricht senden kannst.',
Microphone_Permission: 'Mikrofonberechtigung',
- Mute: 'Stumm',
+ Mute: 'Diesem Benutzer das Chatten verbieten',
muted: 'stummgeschaltet',
My_servers: 'Meine Server',
N_people_reacted: '{{n}} Leute haben reagiert',
@@ -563,7 +565,6 @@ export default {
Username: 'Benutzername',
Username_or_email: 'Benutzername oder E-Mail-Adresse',
Uses_server_configuration: 'Nutzt Servereinstellungen',
- Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: 'Üblicherweise beginnt eine Diskussion mit einer Frage, beispielsweise: "Wie lade ich ein Bild hoch?"',
Validating: 'Validierung',
Registration_Succeeded: 'Registrierung erfolgreich!',
Verify: 'Überprüfen',
@@ -598,7 +599,6 @@ export default {
You_need_to_access_at_least_one_RocketChat_server_to_share_something: 'Du benötigst Zugang zu mindestens einem Rocket.Chat-Server um etwas zu teilen.',
You_need_to_verifiy_your_email_address_to_get_notications: 'Du musst deine Email-Adresse bestätigen um Benachrichtigungen zu erhalten.',
Your_certificate: 'Dein Zertifikat',
- Your_message: 'Deine Nachricht',
Your_invite_link_will_expire_after__usesLeft__uses: 'Dein Einladungs-Link wird nach {{usesLeft}} Benutzungen ablaufen.',
Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: 'Dein Einladungs-Link wird am {{date}} oder nach {{usesLeft}} Benutzungen ablaufen.',
Your_invite_link_will_expire_on__date__: 'Dein Einladungs-Link wird am {{date}} ablaufen.',
@@ -680,5 +680,29 @@ export default {
No_threads: 'Es gibt keine Threads',
No_threads_following: 'Du folgst keinen Threads',
No_threads_unread: 'Es gibt keine ungelesenen Threads',
- Messagebox_Send_to_channel: 'an Kanal senden'
+ Messagebox_Send_to_channel: 'an Kanal senden',
+ Set_as_leader: 'Zum Diskussionsleiter ernennen',
+ Set_as_moderator: 'Zum Moderator ernennen',
+ Set_as_owner: 'Zum Besitzer machen',
+ Remove_as_leader: 'Als Diskussionsleiter entfernen',
+ Remove_as_moderator: 'Moderatorenrechte entfernen',
+ Remove_as_owner: 'Als Eigentümer entfernen',
+ Remove_from_room: 'Aus dem Raum entfernen',
+ Ignore: 'Ignorieren',
+ Unignore: 'Nicht mehr ignorieren',
+ User_has_been_ignored: 'Benutzer wurde stumm geschaltet',
+ User_has_been_unignored: 'Benutzer nicht mehr stumm geschaltet',
+ User_has_been_removed_from_s: 'Benutzer wurde aus {{s}} entfernt',
+ User__username__is_now_a_leader_of__room_name_: 'Benutzer {{username}} ist nun Diskussionsleiter von {{room_name}}',
+ User__username__is_now_a_moderator_of__room_name_: 'Benutzer {{username}} ist nun Moderator von {{room_name}}',
+ User__username__is_now_a_owner_of__room_name_: 'Benutzer {{username}} ist nun Eigentümer von {{room_name}}',
+ User__username__removed_from__room_name__leaders: 'Benutzer {{username}} als Diskussionsleiter von {{room_name}} entfernt',
+ User__username__removed_from__room_name__moderators: 'Benutzer {{username}} als Moderator von {{room_name}} entfernt',
+ User__username__removed_from__room_name__owners: 'Benutzer {{username}} als Eigentümer von {{room_name}} entfernt',
+ The_user_will_be_removed_from_s: 'Der Benutzer wird aus {{s}} entfernt',
+ Yes_remove_user: 'Ja, Benutzer entfernen!',
+ Direct_message: 'Direktnachricht',
+ Message_Ignored: 'Nachricht ignoriert. Antippen um sie zu zeigen.',
+ Enter_workspace_URL: 'Arbeitsbereich-URL',
+ Workspace_URL_Example: 'z.B. https://rocketchat.deine-firma.de'
};
diff --git a/app/i18n/locales/en.js b/app/i18n/locales/en.js
index cbc226098..c35da810b 100644
--- a/app/i18n/locales/en.js
+++ b/app/i18n/locales/en.js
@@ -223,7 +223,7 @@ export default {
Enter_Your_Encryption_Password_desc1: 'This will allow you to access your encrypted private groups and direct messages.',
Enter_Your_Encryption_Password_desc2: 'You need to enter the password to encode/decode messages every place you use the chat.',
Encryption_error_title: 'Your encryption password seems wrong',
- Encryption_error_desc: 'Wasn\'t possible to decode your encryption key to be imported.',
+ Encryption_error_desc: 'It wasn\'t possible to decode your encryption key to be imported.',
Everyone_can_access_this_channel: 'Everyone can access this channel',
Error_uploading: 'Error uploading',
Expiration_Days: 'Expiration (Days)',
@@ -565,7 +565,6 @@ export default {
Username: 'Username',
Username_or_email: 'Username or email',
Uses_server_configuration: 'Uses server configuration',
- Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: 'Usually, a discussion starts with a question, like "How do I upload a picture?"',
Validating: 'Validating',
Registration_Succeeded: 'Registration Succeeded!',
Verify: 'Verify',
@@ -600,7 +599,6 @@ export default {
You_need_to_access_at_least_one_RocketChat_server_to_share_something: 'You need to access at least one Rocket.Chat server to share something.',
You_need_to_verifiy_your_email_address_to_get_notications: 'You need to verify your email address to get notifications',
Your_certificate: 'Your Certificate',
- Your_message: 'Your message',
Your_invite_link_will_expire_after__usesLeft__uses: 'Your invite link will expire after {{usesLeft}} uses.',
Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: 'Your invite link will expire on {{date}} or after {{usesLeft}} uses.',
Your_invite_link_will_expire_on__date__: 'Your invite link will expire on {{date}}.',
@@ -704,5 +702,7 @@ export default {
The_user_will_be_removed_from_s: 'The user will be removed from {{s}}',
Yes_remove_user: 'Yes, remove user!',
Direct_message: 'Direct message',
- Message_Ignored: 'Message ignored. Tap to display it.'
+ Message_Ignored: 'Message ignored. Tap to display it.',
+ Enter_workspace_URL: 'Enter workspace URL',
+ Workspace_URL_Example: 'Ex. your-company.rocket.chat'
};
diff --git a/app/i18n/locales/fr.js b/app/i18n/locales/fr.js
index 31a0e280d..71051fb90 100644
--- a/app/i18n/locales/fr.js
+++ b/app/i18n/locales/fr.js
@@ -531,7 +531,6 @@ export default {
Username: 'Nom d\'utilisateur',
Username_or_email: 'Nom d\'utilisateur ou address e-mail',
Uses_server_configuration: 'Utilise la configuration du serveur',
- Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: 'Habituellement, une discussion commence par une question, comme "Comment télécharger une image?"',
Validating: 'Validation',
Registration_Succeeded: 'Inscription réussie!',
Verify: 'Vérifier',
@@ -565,7 +564,6 @@ export default {
Logged_out_by_server: 'Vous avez été déconnecté par le serveur. Veuillez vous reconnecter.',
You_need_to_access_at_least_one_RocketChat_server_to_share_something: 'Vous devez accéder à au moins un serveur Rocket.Chat pour partager quelque chose.',
Your_certificate: 'Votre Certificat',
- Your_message: 'Votre message',
Your_invite_link_will_expire_after__usesLeft__uses: 'Votre lien d\'invitation expirera après {{usesLeft}} utilisations.',
Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: 'Votre lien d\'invitation expirera le {{date}} ou après {{usesLeft}} utilisations.',
Your_invite_link_will_expire_on__date__: 'Votre lien d\'invitation expirera le {{date}}.',
diff --git a/app/i18n/locales/it.js b/app/i18n/locales/it.js
index f5fdcdb54..c0a0c245e 100644
--- a/app/i18n/locales/it.js
+++ b/app/i18n/locales/it.js
@@ -197,6 +197,7 @@ export default {
Do_you_have_an_account: 'Hai un account?',
Do_you_have_a_certificate: 'Hai un certificato?',
Do_you_really_want_to_key_this_room_question_mark: 'Sei sicuro di voler {{key}} questa stanza?',
+ E2E_Encryption: 'Crittografia E2E',
E2E_How_It_Works_info1: 'Ora puoi creare gruppi e messaggi privati crittografati. Puoi anche crittografare quelli già esistenti.',
E2E_How_It_Works_info2: 'Questa è *crittografia end-to-end*, quindi la chiave per cifrare/decifrare i messaggi non verrà salvata sul server. Per questo motivo *devi salvare questa password in un luogo sicuro* dove poterla recuperare in seguito qualora necessario.',
E2E_How_It_Works_info3: 'Procedendo verrà generata automaticamente una password E2E.',
@@ -280,6 +281,8 @@ export default {
Invite_Link: 'Link di invito',
Invite_users: 'Invita utenti',
Join: 'Entra',
+ Join_Code: 'Codice d\'accesso',
+ Insert_Join_Code: 'Inserisci il codice d\'accesso',
Join_our_open_workspace: 'Unisciti al nostro workspace',
Join_your_workspace: 'Unisciti al tuo workspace',
Just_invited_people_can_access_this_channel: 'Solo le persone invitate possono accedere a questo canale',
@@ -435,6 +438,7 @@ export default {
Roles: 'Ruoli',
Room_actions: 'Azioni stanza',
Room_changed_announcement: 'Annuncio stanza cambiato in: {{announcement}} da {{userBy}}',
+ Room_changed_avatar: 'Immagine stanza cambiata da {{userBy}}',
Room_changed_description: 'Descrizione stanza cambiata in: {{description}} da {{userBy}}',
Room_changed_privacy: 'Tipo stanza cambiato in: {{type}} da {{userBy}}',
Room_changed_topic: 'Argomento stanza cambiato in: {{topic}} da {{userBy}}',
@@ -461,6 +465,7 @@ export default {
Search_global_users: 'Cerca utenti globali',
Search_global_users_description: 'Se attivi questa opzione, puoi cercare qualsiasi utente da altre aziende o server.',
Seconds: '{{second}} secondi',
+ Security_and_privacy: 'Sicurezza e privacy',
Select_Avatar: 'Seleziona avatar',
Select_Server: 'Seleziona server',
Select_Users: 'Seleziona utenti',
@@ -533,7 +538,7 @@ export default {
unarchive: 'rimuovi dall\'archivio',
UNARCHIVE: 'RIMUOVI DALL\'ARCHIVIO',
Unblock_user: 'Sblocca utente',
- Unfavorite: 'Rimuovi dai preferiti',
+ Unfavorite: 'Rimuovi preferito',
Unfollowed_thread: 'Non segui più il thread',
Unmute: 'Attiva notifiche',
unmuted: 'notifiche attivate',
@@ -560,7 +565,6 @@ export default {
Username: 'Username',
Username_or_email: 'Username o email',
Uses_server_configuration: 'Usa la configurazione del server',
- Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: 'Una discussione inizia solitamente con una domanda, ad esempio: "Come posso caricare una foto?"',
Validating: 'Validazione',
Registration_Succeeded: 'Registrazione completata!',
Verify: 'Verifica',
@@ -595,7 +599,6 @@ export default {
You_need_to_access_at_least_one_RocketChat_server_to_share_something: 'Devi accedere ad almeno un server Rocket.Chat prima di condividere qualcosa.',
You_need_to_verifiy_your_email_address_to_get_notications: 'Devi verificare il tuo indirizzo e-mail per ricevere le notifiche',
Your_certificate: 'Il tuo certificato',
- Your_message: 'Il tuo messaggio',
Your_invite_link_will_expire_after__usesLeft__uses: 'Il tuo link di invito scadrà dopo {{usesLeft}} utilizzi.',
Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: 'Il tuo link di invito scadrà il {{date}} oppure dopo {{usesLeft}} utilizzi.',
Your_invite_link_will_expire_on__date__: 'Il tuo link di invito scadrà il {{date}}.',
@@ -657,5 +660,49 @@ export default {
You_will_be_logged_out_from_other_locations: 'Verrai disconnesso dalle altre postazioni.',
Logged_out_of_other_clients_successfully: 'Disconnesso dalle altre postazioni con successo',
Logout_failed: 'Disconnessione fallita!',
- Log_analytics_events: 'Invia statistiche anonime'
+ Log_analytics_events: 'Invia statistiche anonime',
+ E2E_encryption_change_password_title: 'Cambia la password di cifratura',
+ E2E_encryption_change_password_description: 'Ora puoi creare gruppi e messaggi privati crittografati. Potrai, inoltre, crittografare i tuoi gruppi e messaggi privati esistenti. \nQuesta è crittografia end-to-end, la chiave per codificare/decodificare i tuoi messaggi non verrà salvata sul server. Per questo motivo devi salvarla in un luogo sicuro. Ti verrà richiesto di inserirla sugli altri dispositivi sui quali vuoi usare la crittografia e2e.',
+ E2E_encryption_change_password_error: 'Si è verificato un errore durante il cambio della password della chiave E2E!',
+ E2E_encryption_change_password_success: 'La password della chiave E2E è stata cambiata con successo!',
+ E2E_encryption_change_password_message: 'Assicurati di salvarla in un posto sicuro.',
+ E2E_encryption_change_password_confirmation: 'Si, cambiala',
+ E2E_encryption_reset_title: 'Ripristina la Chiave E2E',
+ E2E_encryption_reset_description: 'Questa opzione rimuoverà la tua chiave E2E corrente e sarai disconnesso. \nAl prossimo login, Rocket.Chat genererà una nuova chiave e ripristinerà l\'accesso a tutte le stanze crittografate con uno o più membri online. \nA causa della natura della crittografia E2E, Rocket.Chat non sarà in grado di ripristinare l\'accesso alle stanze senza membri online.',
+ E2E_encryption_reset_button: 'Ripristina',
+ E2E_encryption_reset_error: 'Si è verificato un errore durante il ripristino della chiave E2E!',
+ E2E_encryption_reset_message: 'Stai per essere disconnesso.',
+ E2E_encryption_reset_confirmation: 'Si, resettala',
+ Following: 'Seguiti',
+ Threads_displaying_all: 'Visualizza Tutti',
+ Threads_displaying_following: 'Visualizza Seguiti',
+ Threads_displaying_unread: 'Visualizza Non letti',
+ No_threads: 'Non ci sono thread',
+ No_threads_following: 'Non stai seguendo alcun thread',
+ No_threads_unread: 'Non ci sono thread non letti',
+ Messagebox_Send_to_channel: 'Invia sul canale',
+ Set_as_leader: 'Rendi leader',
+ Set_as_moderator: 'Rendi moderatore',
+ Set_as_owner: 'Rendi proprietario',
+ Remove_as_leader: 'Rimuovi come leader',
+ Remove_as_moderator: 'Rimuovi come moderatore',
+ Remove_as_owner: 'Rimuovi come proprietario',
+ Remove_from_room: 'Rimuovi dalla stanza',
+ Ignore: 'Ignora',
+ Unignore: 'Non ignorare',
+ User_has_been_ignored: 'Utente ignorato',
+ User_has_been_unignored: 'Utente non ignorato',
+ User_has_been_removed_from_s: 'L\'utente è stato rimosso da {{s}}',
+ User__username__is_now_a_leader_of__room_name_: 'L\'utente {{username}} è ora un leader di {{room_name}}',
+ User__username__is_now_a_moderator_of__room_name_: 'L\'utente {{username}} è ora un moderatore di {{room_name}}',
+ User__username__is_now_a_owner_of__room_name_: 'L\'utente {{username}} è ora un proprietario di {{room_name}}',
+ User__username__removed_from__room_name__leaders: 'L\'utente {{username}} non è più un leader di {{room_name}}',
+ User__username__removed_from__room_name__moderators: 'L\'utente {{username}} non è più un moderatore di {{room_name}}',
+ User__username__removed_from__room_name__owners: 'L\'utente {{username}} non è più un proprietario di {{room_name}}',
+ The_user_will_be_removed_from_s: 'L\'utente sarà rimosso da {{s}}',
+ Yes_remove_user: 'Si, rimuovi utente!',
+ Direct_message: 'Messaggio diretto',
+ Message_Ignored: 'Messaggio ignorato. Tocca per visualizzarlo.',
+ Enter_workspace_URL: 'Inserisci la url del workspace',
+ Workspace_URL_Example: 'Es. tua-azienda.rocket.chat'
};
diff --git a/app/i18n/locales/pt-BR.js b/app/i18n/locales/pt-BR.js
index e9625e964..3819a3b3b 100644
--- a/app/i18n/locales/pt-BR.js
+++ b/app/i18n/locales/pt-BR.js
@@ -522,7 +522,6 @@ export default {
Username: 'Usuário',
Username_or_email: 'Usuário ou email',
Uses_server_configuration: 'Usar configuração do servidor',
- Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: 'Normalmente, uma discussão começa com uma pergunta como: Como faço para enviar uma foto?',
Verify: 'Verificar',
Verify_email_title: 'Registrado com sucesso!',
Verify_email_desc: 'Nós lhe enviamos um e-mail para confirmar o seu registro. Se você não receber um e-mail em breve, por favor retorne e tente novamente.',
@@ -541,7 +540,6 @@ export default {
You_are_in_preview_mode: 'Está é uma prévia do canal',
You_are_offline: 'Você está offline',
You_can_search_using_RegExp_eg: 'Você pode usar expressões regulares, por exemplo `/^text$/i`',
- Your_message: 'Sua mensagem',
You_need_to_verifiy_your_email_address_to_get_notications: 'Você precisa confirmar seu endereço de e-mail para obter notificações',
You_colon: 'Você: ',
you_were_mentioned: 'você foi mencionado',
@@ -651,5 +649,7 @@ export default {
The_user_will_be_removed_from_s: 'O usuário será removido de {{s}}',
Yes_remove_user: 'Sim, remover usuário!',
Direct_message: 'Mensagem direta',
- Message_Ignored: 'Mensagem ignorada. Toque para mostrar.'
+ Message_Ignored: 'Mensagem ignorada. Toque para mostrar.',
+ Enter_workspace_URL: 'Digite a URL da sua workspace',
+ Workspace_URL_Example: 'Ex. sua-empresa.rocket.chat'
};
diff --git a/app/i18n/locales/ru.js b/app/i18n/locales/ru.js
index 6b82d1968..4d37232bf 100644
--- a/app/i18n/locales/ru.js
+++ b/app/i18n/locales/ru.js
@@ -563,7 +563,6 @@ export default {
Username: 'Имя пользователя',
Username_or_email: 'Имя пользователя или email',
Uses_server_configuration: 'Используется конфигурация сервера',
- Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: 'Обычно Обсуждение начинается с какого-либо вопроса, например, "Как мне загрузить эту картинку?"',
Validating: 'Проверка',
Registration_Succeeded: 'Регистрация Успешна!',
Verify: 'Проверить',
@@ -598,7 +597,6 @@ export default {
You_need_to_access_at_least_one_RocketChat_server_to_share_something: 'Вам нужно получить доступ как минимум к одному серверу Rocket.Chat, чтобы поделиться чем-то.',
You_need_to_verifiy_your_email_address_to_get_notications: 'Вам необходимо проверить ваш email адрес, чтобы получать уведомления',
Your_certificate: 'Ваш сертификат',
- Your_message: 'Ваше сообщение',
Your_invite_link_will_expire_after__usesLeft__uses: 'Ваша ссылка-приглашение станет не действительной после {{usesLeft}} ее использований.',
Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: 'Ваша ссылка-приглашение станет не действительной {{date}} или после {{usesLeft}} ее использований.',
Your_invite_link_will_expire_on__date__: 'Срок действия вашей ссылки-приглашения будет окончен {{date}}.',
diff --git a/app/i18n/locales/tr.js b/app/i18n/locales/tr.js
new file mode 100644
index 000000000..5ca3648e2
--- /dev/null
+++ b/app/i18n/locales/tr.js
@@ -0,0 +1,710 @@
+export default {
+ '1_person_reacted': '1 kişi tepki verdi.',
+ '1_user': '1 kullanıcı',
+ 'error-action-not-allowed': '{{action}}\'a izin verilmiyor!',
+ 'error-application-not-found': 'Uygulama bulunamadı!',
+ 'error-archived-duplicate-name': '{{room_name}} adında arşivlenmiş bir kanal var!',
+ 'error-avatar-invalid-url': 'Geçersiz avatar URL\'si: {{url}}',
+ 'error-avatar-url-handling': '{{username}} için bir URL\'den ({{url}}) avatar ayarı işlenirken hata oluştu!',
+ 'error-cant-invite-for-direct-room': 'Kullanıcı özel odalara davet edilemedi!',
+ 'error-could-not-change-email': 'E-posta değiştirilemedi!',
+ 'error-could-not-change-name': 'İsim değiştirilemedi!',
+ 'error-could-not-change-username': 'Kullanıcı adı değiştirilemedi!',
+ 'error-could-not-change-status': 'Durum değiştirilemedi!',
+ 'error-delete-protected-role': 'Korunan bir rol silinemez!',
+ 'error-department-not-found': 'Bölüm bulunamadı!',
+ 'error-direct-message-file-upload-not-allowed': 'Özel iletilerde dosya paylaşımına izin verilmiyor!',
+ 'error-duplicate-channel-name': '{{channel_name}} adında bir kanal var!',
+ 'error-email-domain-blacklisted': 'E-posta alan adı kara listeye alındı!',
+ 'error-email-send-failed': 'E-posta göndermeye çalışırken hata oluştu: {{message}}',
+ 'error-save-image': 'Görüntüyü kaydederken hata oluştu!',
+ 'error-save-video': 'Videoyu kaydederken hata oluştu!',
+ 'error-field-unavailable': '{{field}} zaten kullanılıyor! :(',
+ 'error-file-too-large': 'Dosya çok büyük!',
+ 'error-importer-not-defined': 'İçe aktarıcı doğru tanımlanmadı, "Import" sınıfı eksik!',
+ 'error-input-is-not-a-valid-field': '{{input}} geçerli bir {{field}} değil!',
+ 'error-invalid-actionlink': 'Geçersiz işlem bağlantısı!',
+ 'error-invalid-arguments': 'Geçersiz parametreler!',
+ 'error-invalid-asset': 'Geçersiz veri!',
+ 'error-invalid-channel': 'Geçersiz kanal.',
+ 'error-invalid-channel-start-with-chars': 'Geçersiz kanal! @ veya # ile başlayın.',
+ 'error-invalid-custom-field': 'Geçersiz özelleştirilmiş alan',
+ 'error-invalid-custom-field-name': 'Geçersiz özelleştirilmiş alan adı! Yalnızca harf, rakam, kısa çizgi ve alt çizgi kullanın.',
+ 'error-invalid-date': 'Geçersiz tarih!',
+ 'error-invalid-description': 'Geçersiz açıklama!',
+ 'error-invalid-domain': 'Geçersiz alan adı!',
+ 'error-invalid-email': 'Geçersiz e-posta {{email}}!',
+ 'error-invalid-email-address': 'Geçersiz e-posta adresi!',
+ 'error-invalid-file-height': 'Geçersiz fotoğraf yüksekliği!',
+ 'error-invalid-file-type': 'Geçersiz dosya türü!',
+ 'error-invalid-file-width': 'Geçersiz fotoğraf genişliği!',
+ 'error-invalid-from-address': 'Geçersiz bir KİMDEN adresi bildirdiniz!',
+ 'error-invalid-integration': 'Geçersiz entegrasyon',
+ 'error-invalid-message': 'Geçersiz ileti!',
+ 'error-invalid-method': 'Geçersiz metot!',
+ 'error-invalid-name': 'Geçersiz isim!',
+ 'error-invalid-password': 'Geçersiz şifre!',
+ 'error-invalid-redirectUri': 'Geçersiz yönlendirme bağlantısı!',
+ 'error-invalid-role': 'Geçersiz rol!',
+ 'error-invalid-room': 'Geçersiz oda!',
+ 'error-invalid-room-name': '{{room_name}}, geçerli bir oda adı değil!',
+ 'error-invalid-room-type': '{{type}}, geçerli bir oda türü değil!',
+ 'error-invalid-settings': 'Geçersiz ayar!',
+ 'error-invalid-subscription': 'Geçersiz başvuru!',
+ 'error-invalid-token': 'Geçersiz belirteç!',
+ 'error-invalid-triggerWords': 'Geçersiz tetikleyici parametreleri!',
+ 'error-invalid-urls': 'Geçersiz URL\'ler!',
+ 'error-invalid-user': 'Geçersiz kullanıcı!',
+ 'error-invalid-username': 'Geçersiz kullanıcı adı!',
+ 'error-invalid-webhook-response': 'İstek URL\'si 200\'den farklı bir durumla yanıt verdi!',
+ 'error-message-deleting-blocked': 'İleti silme engellendi!',
+ 'error-message-editing-blocked': 'İleti düzenleme engellendi',
+ 'error-message-size-exceeded': 'İleti boyutu, Message_MaxAllowedSize değerini aşıyor',
+ 'error-missing-unsubscribe-link': '[unsubscribe] bağlantısını belirtmelisiniz.',
+ 'error-no-tokens-for-this-user': 'Bu kullanıcı için belirteç (token) yok',
+ 'error-not-allowed': 'İzin verilmedi',
+ 'error-not-authorized': 'Yetkili değil',
+ 'error-push-disabled': 'Push devre dışı',
+ 'error-remove-last-owner': 'Lütfen bunu kaldırmadan önce yeni bir sahip belirleyin.',
+ 'error-role-in-use': 'Rol kullanımda olduğu için silinemiyor',
+ 'error-role-name-required': 'Rol adı gerekli',
+ 'error-the-field-is-required': '{{field}} alanı gereklidir.',
+ 'error-too-many-requests': 'Hata, çok fazla istek. Lütfen yavaşla. Tekrar denemeden önce {{seconds}} saniye beklemelisiniz.',
+ 'error-user-is-not-activated': 'Kullanıcı etkinleştirilmedi!',
+ 'error-user-has-no-roles': 'Kullanıcıya tanımlı rol yok!',
+ 'error-user-limit-exceeded': '#channel_name kanalına davet etmeye çalıştığınız kullanıcıların sayısı, yönetici tarafından belirlenen sınırı aşıyor!',
+ 'error-user-not-in-room': 'Kullanıcı bu odada değil!',
+ 'error-user-registration-custom-field': 'error-user-registration-custom-field',
+ 'error-user-registration-disabled': 'Kullanıcı kaydı devre dışı bırakıldı!',
+ 'error-user-registration-secret': 'Kullanıcı kaydına yalnızca Gizli URL aracılığıyla izin verilir!',
+ 'error-you-are-last-owner': 'Son sahibi sizsiniz. Lütfen odadan ayrılmadan önce yeni bir sahip belirleyin.',
+ Actions: 'İşlemler',
+ activity: 'etkinlik',
+ Activity: 'Etkinlik',
+ Add_Reaction: 'Tepki ekle',
+ Add_Server: 'Sunucu ekle',
+ Add_users: 'Kullanıcı ekle',
+ Admin_Panel: 'Yönetim Paneli',
+ Agent: 'Temsilci',
+ Alert: 'Uyarı',
+ alert: 'uyarı',
+ alerts: 'uyarılar',
+ All_users_in_the_channel_can_write_new_messages: 'Kanaldaki tüm kullanıcılar yeni ileti yazabilir!',
+ A_meaningful_name_for_the_discussion_room: 'Tartışma odası için anlamlı bir isim',
+ All: 'Tümü',
+ All_Messages: 'Tüm İletiler',
+ Allow_Reactions: 'Tüm Tepkiler',
+ Alphabetical: 'Alfabetik',
+ and_more: 've daha',
+ and: 've',
+ announcement: 'duyuru',
+ Announcement: 'Duyuru',
+ Apply_Your_Certificate: 'Sertifikanızı Onaylayın',
+ ARCHIVE: 'ARŞİVLE',
+ archive: 'arşivle',
+ are_typing: 'yazıyor',
+ Are_you_sure_question_mark: 'Emin misiniz?',
+ Are_you_sure_you_want_to_leave_the_room: '{{room}} odasından ayrılmak istediğinizden emin misiniz?',
+ Audio: 'Ses',
+ Authenticating: 'Doğrulanıyor',
+ Automatic: 'Otomatik',
+ Auto_Translate: 'Otomatik Çevir',
+ Avatar_changed_successfully: 'Profil fotoğrafı başarıyla değiştirildi!',
+ Avatar_Url: 'Profil fotoğrafı URL\'si',
+ Away: 'Uzakta',
+ Back: 'Geri',
+ Black: 'Koyu',
+ Block_user: 'Kullanıcıyı engelle',
+ Browser: 'Tarayıcı',
+ Broadcast_channel_Description: 'Yalnızca yetkili kullanıcılar yeni ileti yazabilir, ancak diğer kullanıcılar yanıt verebilir',
+ Broadcast_Channel: 'Kanala Yayınla',
+ Busy: 'Meşgul',
+ By_proceeding_you_are_agreeing: 'Devam ederek kabul ediyorsunuz: ',
+ Cancel_editing: 'Düzenlemeyi iptal et',
+ Cancel_recording: 'Kaydı iptal et',
+ Cancel: 'İptal et',
+ changing_avatar: 'profil fotoğrafı değiştirme',
+ creating_channel: 'kanal açılıyor',
+ creating_invite: 'davet üretiliyor',
+ Channel_Name: 'Kanal Adı',
+ Channels: 'Kanallar',
+ Chats: 'Sohbetler',
+ Call_already_ended: 'Çağrı sona erdi!',
+ Clear_cookies_alert: 'Tüm çerezleri temizlemek istiyor musunuz?',
+ Clear_cookies_desc: 'Bu işlem, tüm oturum açma çerezlerini temizleyerek diğer hesaplara giriş yapmanıza olanak tanır.',
+ Clear_cookies_yes: 'Evet, çerezleri temizle',
+ Clear_cookies_no: 'Hayır, çerezleri koru',
+ Click_to_join: 'Katılmak için tıklayın!',
+ Close: 'Kapat',
+ Close_emoji_selector: 'Emoji seçiciyi kapat',
+ Closing_chat: 'Sohbet kapatılıyor',
+ Change_language_loading: 'Dil değiştiriliyor',
+ Chat_closed_by_agent: 'Sohbet temsilci tarafından kapatıldı',
+ Choose: 'Seç',
+ Choose_from_library: 'Kütüphaneden seç',
+ Choose_file: 'Dosya seç',
+ Choose_where_you_want_links_be_opened: 'Bağlantıların açılmasını istediğiniz yeri seçin',
+ Code: 'Kod',
+ Code_or_password_invalid: 'Kod veya şifre geçersiz',
+ Collaborative: 'İşbirlikçi',
+ Confirm: 'Onayla',
+ Connect: 'Bağlan',
+ Connected: 'Bağlandı',
+ connecting_server: 'sunucuya bağlanıyor',
+ Connecting: 'Bağlanıyor...',
+ Contact_us: 'Bize ulaşın',
+ Contact_your_server_admin: 'Sunucu yöneticisiyle iletişime geçin.',
+ Continue_with: 'Devam et:',
+ Copied_to_clipboard: 'Panoya kopyalandı!',
+ Copy: 'Kopyala',
+ Conversation: 'Sohbet',
+ Permalink: 'Kalıcı bağlantı',
+ Certificate_password: 'Sertifika Şifresi',
+ Clear_cache: 'Yerel sunucu önbelleğini temizleyin',
+ Clear_cache_loading: 'Önbellek temizleniyor.',
+ Whats_the_password_for_your_certificate: 'Sertifikanızın şifresi nedir?',
+ Create_account: 'Hesap oluştur',
+ Create_Channel: 'Kanal Oluştur',
+ Create_Direct_Messages: 'Özel İleti Oluştur',
+ Create_Discussion: 'Tartışma Oluştur',
+ Created_snippet: 'kalıp oluşturdu',
+ Create_a_new_workspace: 'Çalışma alanı oluştur',
+ Create: 'Oluştur',
+ Custom_Status: 'Özelleştirilmiş durum',
+ Dark: 'Karanlık',
+ Dark_level: 'Karanlık Seviyesi',
+ Default: 'Varsayılan',
+ Default_browser: 'Varsayılan tarayıcı',
+ Delete_Room_Warning: 'Bir odanın silinmesi, oda içinde yayınlanan tüm iletileri silecektir. Bu geri alınamaz.',
+ Department: 'Bölüm',
+ delete: 'sil',
+ Delete: 'Sil',
+ DELETE: 'SİL',
+ deleting_room: 'oda siliniyor',
+ description: 'açıklama',
+ Description: 'Açıklama',
+ Desktop_Options: 'Masaüstü Seçenekleri',
+ Desktop_Notifications: 'Masaüstü Bildirimleri',
+ Desktop_Alert_info: 'Bu bildirimler masaüstünde teslim edilir',
+ Directory: 'Dizin',
+ Direct_Messages: 'Özel İletiler',
+ Disable_notifications: 'Bildirimleri devre dışı bırak',
+ Discussions: 'Tartışmalar',
+ Discussion_Desc: 'Neler olup bittiğini gözden geçirmeye yardımcı olun! Bir tartışma oluşturarak, seçtiğinizin bir alt kanalı oluşturulur ve her ikisi de bağlanır.',
+ Discussion_name: 'Tartışma adı',
+ Done: 'Tamamlandı',
+ Dont_Have_An_Account: 'Hesabın yok mu?',
+ Do_you_have_an_account: 'Hesabın var mı?',
+ Do_you_have_a_certificate: 'Sertifikanız var mı?',
+ Do_you_really_want_to_key_this_room_question_mark: 'Bu odayı gerçekten {{key}} istiyor musun?',
+ E2E_Encryption: 'Uçtan Uca Şifreleme (E2E)',
+ E2E_How_It_Works_info1: 'Artık şifrelenmiş özel gruplar ve doğrudan iletiler oluşturabilirsiniz. Mevcut özel grupları veya özel iletileri şifreli olarak da değiştirebilirsiniz.',
+ E2E_How_It_Works_info2: 'Bu, *Uçtan Uca Şifreleme\'dir (E2E)*, böylece iletilerinizi şifrelemek / çözmek için anahtar ve bunlar sunucuya kaydedilmez. Bu nedenle *bu şifreyi daha sonra ihtiyaç duyduğunuzda erişebileceğiniz güvenli bir yerde* saklamanız gerekir.',
+ E2E_How_It_Works_info3: 'Devam ederseniz, otomatik olarak bir Uçtan Uca Şifreleme (E2E) şifresi oluşturulacaktır.',
+ E2E_How_It_Works_info4: 'Mevcut Uçtan Uca Şifreleme (E2E) şifresini, herhangi bir tarayıcıdan istediğiniz zaman girdiğiniz şifreleme anahtarınız için yeni bir şifre de ayarlayabilirsiniz.',
+ edit: 'düzenle',
+ edited: 'düzenlendi',
+ Edit: 'Düzenle',
+ Edit_Status: 'Durumu Düzenle',
+ Edit_Invite: 'Daveti Düzenle',
+ End_to_end_encrypted_room: 'Uçtan uca şifreli oda',
+ end_to_end_encryption: 'uçtan uca şifreleme',
+ Email_Notification_Mode_All: 'Tüm Bahsetmeler/Özel İletiler',
+ Email_Notification_Mode_Disabled: 'Devre Dışı',
+ Email_or_password_field_is_empty: 'E-posta veya şifre alanı boş',
+ Email: 'E-posta',
+ email: 'e-posta',
+ Empty_title: 'Boş başlık',
+ Enable_Auto_Translate: 'Otomatik Çeviriyi Etkinleştir',
+ Enable_notifications: 'Bildirimleri etkinleştir',
+ Encrypted: 'Şifreli',
+ Encrypted_message: 'Şifreli ileti',
+ Enter_Your_E2E_Password: 'Uçtan Uca Şifreleme (E2E) Şifrenizi Girin',
+ Enter_Your_Encryption_Password_desc1: 'Bu, şifrelenmiş özel gruplarınıza ve doğrudan iletilerinize erişmenize izin verecektir.',
+ Enter_Your_Encryption_Password_desc2: 'Sohbeti kullandığınız her yerde iletileri şifrelemek / çözmek için şifre girmeniz gerekir.',
+ Encryption_error_title: 'Şifreleme şifreniz yanlış görünüyor',
+ Encryption_error_desc: 'İçe aktarılacak şifreleme anahtarınızın kodu çözülemedi.',
+ Everyone_can_access_this_channel: 'Bu kanala herkes erişebilir',
+ Error_uploading: 'Yükleme hatası',
+ Expiration_Days: 'Geçerlilik Süresi (Gün)',
+ Favorite: 'Favori',
+ Favorites: 'Favoriler',
+ Files: 'Dosyalar',
+ File_description: 'Dosya açıklaması',
+ File_name: 'Dosya adı',
+ Finish_recording: 'Kaydı bitir',
+ Following_thread: 'Konu takip ediliyor',
+ For_your_security_you_must_enter_your_current_password_to_continue: 'Güvenliğiniz için, devam etmek için mevcut şifrenizi girmelisiniz',
+ Forgot_password_If_this_email_is_registered: 'Bu e-posta kayıtlıysa, şifrenizi nasıl sıfırlayacağınıza dair talimatlar göndereceğiz. Kısa süre içinde bir e-posta almazsanız, lütfen geri gelin ve tekrar deneyin.',
+ Forgot_password: 'Parolanızı mı unuttunuz?',
+ Forgot_Password: 'Parolamı Unuttum',
+ Forward: 'İlet',
+ Forward_Chat: 'Sohbete İlet',
+ Forward_to_department: 'Bölüme İlet',
+ Forward_to_user: 'Kullanıcıya İlet',
+ Full_table: 'Tam tabloyu görmek için tıklayın',
+ Generate_New_Link: 'Yeni Bağlantı Oluştur',
+ Group_by_favorites: 'Favorilere göre grupla',
+ Group_by_type: 'Türe göre grupla',
+ Hide: 'Gizle',
+ Has_joined_the_channel: 'kanala katıldı',
+ Has_joined_the_conversation: 'sohbete katıldı',
+ Has_left_the_channel: 'kanaldan ayrıldı',
+ Hide_System_Messages: 'Sistem İletilerını Gizle',
+ Hide_type_messages: '"{{type}}" iletilerini gizle',
+ How_It_Works: 'Nasıl Çalışır',
+ Message_HideType_uj: 'Kullanıcı Katıldı',
+ Message_HideType_ul: 'Kullanıcı Ayrıldı',
+ Message_HideType_ru: 'Kullanıcı Kaldırıldı',
+ Message_HideType_au: 'Kullanıcı Eklendi',
+ Message_HideType_mute_unmute: 'Kullanıcı Sesi Kapatıldı / Sesi Açıldı',
+ Message_HideType_r: 'Oda Adı Değiştirildi',
+ Message_HideType_ut: 'Kullanıcı Sohbete Katıldı',
+ Message_HideType_wm: 'Hoşgeldiniz',
+ Message_HideType_rm: 'İleti Kaldırıldı',
+ Message_HideType_subscription_role_added: 'Rol Belirlendi',
+ Message_HideType_subscription_role_removed: 'Artık Kullanılmayan Rol',
+ Message_HideType_room_archived: 'Oda Arşivlendi',
+ Message_HideType_room_unarchived: 'Oda Arşivden Çıkarıldı',
+ I_Saved_My_E2E_Password: 'Uçtan Uca Şifreleme (E2E) Şifremi Kaydettim',
+ IP: 'IP',
+ In_app: 'Uygulama İçi',
+ In_App_And_Desktop: 'Uygulama İçi ve Masaüstü',
+ In_App_and_Desktop_Alert_info: 'Uygulama açıkken ekranın üst kısmında bir başlık görüntüler ve masaüstünde bir bildirim görüntüler',
+ Invisible: 'Görünmez',
+ Invite: 'Davet Et',
+ is_a_valid_RocketChat_instance: 'geçerli bir Rocket.Chat örneği',
+ is_not_a_valid_RocketChat_instance: 'geçerli bir Rocket.Chat örneği değil',
+ is_typing: 'yazıyor',
+ Invalid_or_expired_invite_token: 'Geçersiz veya süresi dolmuş davet belirteci (token)',
+ Invalid_server_version: 'Bağlanmaya çalıştığınız sunucu artık uygulama tarafından desteklenmeyen bir sürüm kullanıyor: {{currentVersion}}.\n\n{{minVersion}} sürümüne ihtiyacımız var',
+ Invite_Link: 'Davet Bağlantısı',
+ Invite_users: 'Kullanıcıları davet et',
+ Join: 'Katıl',
+ Join_Code: 'Katılım Kodu',
+ Insert_Join_Code: 'Katılım Kodu Girin',
+ Join_our_open_workspace: 'Açık çalışma alanımıza katılın',
+ Join_your_workspace: 'Çalışma alanınıza katılın',
+ Just_invited_people_can_access_this_channel: 'Yalnızca davet edilen kişiler bu kanala erişebilir',
+ Language: 'Dil',
+ last_message: 'son ileti',
+ Leave_channel: 'Kanaldan ayrıl',
+ leaving_room: 'odadan ayrılıyor',
+ leave: 'ayrıl',
+ Legal: 'Yasal',
+ Light: 'Açık',
+ License: 'Lisans',
+ Livechat: 'Canlı Sohbet',
+ Livechat_edit: 'Canlı sohbeti düzenle',
+ Login: 'Oturum aç',
+ Login_error: 'Kimlik bilgileriniz reddedildi! Lütfen tekrar deneyin.',
+ Login_with: 'ile giriş',
+ Logging_out: 'Çıkış Yapılıyor',
+ Logout: 'Çıkış Yap',
+ Max_number_of_uses: 'Maksimum kullanım sayısı',
+ Max_number_of_users_allowed_is_number: 'İzin verilen maksimum kullanıcı sayısı {{maxUsers}}',
+ members: 'üyeler',
+ Members: 'Üyeler',
+ Mentioned_Messages: 'Bahsedilen İletiler',
+ mentioned: 'bahsedildi',
+ Mentions: 'Bahsetmeler',
+ Message_accessibility: '{{user}} tarafından {{time}} itibarıyla ileti: {{message}}',
+ Message_actions: 'İleti işlemleri',
+ Message_pinned: 'İleti sabitlendi',
+ Message_removed: 'İleti kaldırıldı',
+ Message_starred: 'İletia yıldız eklendi',
+ Message_unstarred: 'İletiın yıldızı kaldırıldı',
+ message: 'ileti',
+ messages: 'iletiler',
+ Message: 'İleti',
+ Messages: 'İletiler',
+ Message_Reported: 'İleti bildirildi',
+ Microphone_Permission_Message: 'Rocket.Chat\'in mikrofonunuza erişmesi gerekiyor, böylece sesli ileti gönderebilirsiniz.',
+ Microphone_Permission: 'Mikrofon İzni',
+ Mute: 'Sessize Al',
+ muted: 'sessize alındı',
+ My_servers: 'Sunucularım',
+ N_people_reacted: '{{n}} kişi tepki verdi',
+ N_users: '{{n}} kullanıcı',
+ name: 'isim',
+ Name: 'İsim',
+ Navigation_history: 'Gezinti geçmişi',
+ Never: 'Asla',
+ New_Message: 'Yeni İleti',
+ New_Password: 'Yeni Şifre',
+ New_Server: 'Yeni Sunucu',
+ Next: 'Sonraki',
+ No_files: 'Dosya yok',
+ No_limit: 'Limit yok',
+ No_mentioned_messages: 'Belirtilen ileti yok',
+ No_pinned_messages: 'Sabitlenmiş ileti yok',
+ No_results_found: 'Sonuç bulunamadı',
+ No_starred_messages: 'Yıldızlı ileti yok',
+ No_thread_messages: 'Konu iletisi yok',
+ No_label_provided: 'Hiç {{label}} sağlanmadı.',
+ No_Message: 'İleti Yok',
+ No_messages_yet: 'Şu ana kadar ileti yok',
+ No_Reactions: 'Tepki Yok',
+ No_Read_Receipts: 'Okundu Bilgisi Yok',
+ Not_logged: 'Kayıtlı değil',
+ Not_RC_Server: 'Bu bir Rocket.Chat sunucusu değil.\n{{contact}}',
+ Nothing: 'Hiçbir Şey',
+ Nothing_to_save: 'Kaydedilecek bir şey yok!',
+ Notify_active_in_this_room: 'Bu odadaki çevrimiçi kullanıcıları bilgilendir',
+ Notify_all_in_this_room: 'Bu odadaki herkesi bilgilendir',
+ Notifications: 'Bildirimler',
+ Notification_Duration: 'Bildirim Süresi',
+ Notification_Preferences: 'Bildirim Tercihleri',
+ No_available_agents_to_transfer: 'Aktarılacak yemsilci yok',
+ Offline: 'Çevrimdışı',
+ Oops: 'Ahh!',
+ Omnichannel: 'Çoklu Kanal',
+ Open_Livechats: 'Devam Eden Sohbetler',
+ Omnichannel_enable_alert: 'Çoklu Kanal\'da mevcut değilsiniz. Erişilebilir olmak ister misiniz?',
+ Onboarding_description: 'Çalışma alanı, ekibinizin veya kuruluşunuzun işbirliği alanıdır. Çalışma alanı yöneticisinden bir ekibe katılmak veya bir ekip oluşturmak için yardım isteyin.',
+ Onboarding_join_workspace: 'Bir çalışma alanına katılın',
+ Onboarding_subtitle: 'Ekip İşbirliğinin Ötesinde',
+ Onboarding_title: 'Rocket.Chat\'e hoş geldiniz',
+ Onboarding_join_open_description: 'Rocket.Chat ekibi ve topluluğu ile sohbet etmek için açık çalışma alanımıza katılın.',
+ Onboarding_agree_terms: 'Devam ederek Rocket.Chat\'i kabul etmiş olursunuz',
+ Onboarding_less_options: 'Daha az seçenek',
+ Onboarding_more_options: 'Daha çok seçenek',
+ Online: 'Çevrimiçi',
+ Only_authorized_users_can_write_new_messages: 'Yalnızca yetkili kullanıcılar yeni ileti yazabilir',
+ Open_emoji_selector: 'Emoji seçiciyi aç',
+ Open_Source_Communication: 'Açık Kaynak İletişim',
+ Open_your_authentication_app_and_enter_the_code: 'Kimlik doğrulama uygulamanızı açın ve kodu girin.',
+ OR: 'OR',
+ OS: 'OS',
+ Overwrites_the_server_configuration_and_use_room_config: 'Sunucu yapılandırmasının üzerine yazar ve oda yapılandırmasını kullanır',
+ Password: 'Şifre',
+ Parent_channel_or_group: 'Üst kanal veya grup',
+ Permalink_copied_to_clipboard: 'Kalıcı bağlantı panoya kopyalandı!',
+ Phone: 'Telefon',
+ Pin: 'Sabitle',
+ Pinned_Messages: 'Sabitlenen İletiler',
+ pinned: 'sabitlendi',
+ Pinned: 'Sabitlendi',
+ Please_add_a_comment: 'Lütfen bir yorum ekleyin',
+ Please_enter_your_password: 'Lütfen şifrenizi giriniz',
+ Please_wait: 'Lütfen bekle.',
+ Preferences: 'Tercihler',
+ Preferences_saved: 'Tercihler kaydedildi!',
+ Privacy_Policy: ' Privacy Policy',
+ Private_Channel: 'Özel Kanal',
+ Private_Groups: 'Özel Gruplar',
+ Private: 'Özel',
+ Processing: 'İşleniyor...',
+ Profile_saved_successfully: 'Profil başarıyla kaydedildi!',
+ Profile: 'Profil',
+ Public_Channel: 'Herkese Açık Kanal',
+ Public: 'Herkese Açık',
+ Push_Notifications: 'Anlık Bildirimler',
+ Push_Notifications_Alert_Info: 'Bu bildirimler, uygulama açık olmadığında size teslim edilir',
+ Quote: 'Alıntı',
+ Reactions_are_disabled: 'Tepkiler devre dışı bırakıldı',
+ Reactions_are_enabled: 'Tepkiler etkinleştirildi',
+ Reactions: 'Tepkiler',
+ Read: 'Oku',
+ Read_External_Permission_Message: 'Rocket.Chat\'in cihazınızdaki fotoğraflara, medyaya ve dosyalara erişmesi gerekiyor',
+ Read_External_Permission: 'Medya Okuma İzni ',
+ Read_Only_Channel: 'Yazma Kısıtlı Kanal',
+ Read_Only: 'Yazma Kısıtlı',
+ Read_Receipt: 'Okundu Bilgisi',
+ Receive_Group_Mentions: 'Grup Bahsetmelerini Al',
+ Receive_Group_Mentions_Info: 'Grup bahsetmelerini al',
+ Register: 'Kayıt Ol',
+ Repeat_Password: 'Şifre Tekrarı',
+ Replied_on: 'Yanıtlandı:',
+ replies: 'yanıtlar',
+ reply: 'yanıtla',
+ Reply: 'Yanıtla',
+ Report: 'Bildir',
+ Receive_Notification: 'Bildirim Al',
+ Receive_notifications_from: '{{name}} bildirimlerini alın',
+ Resend: 'Yeniden yolla',
+ Reset_password: 'Şifre sıfırla',
+ resetting_password: 'şifre sıfırlanıyor',
+ RESET: 'SIFIRLA',
+ Return: 'Geri dön',
+ Review_app_title: 'Uygulama hoşunuza gitti mi?',
+ Review_app_desc: '{{store}} üzerinde bize 5 yıldız verin',
+ Review_app_yes: 'Elbette!',
+ Review_app_no: 'Hayır',
+ Review_app_later: 'Belki daha sonra',
+ Review_app_unable_store: '{{store}} açılamıyor!',
+ Review_this_app: 'Bu uygulamayı değerlendirin',
+ Remove: 'Kaldır',
+ Roles: 'Roller',
+ Room_actions: 'Oda işlemleri',
+ Room_changed_announcement: 'Oda duyurusu, {{userBy}} tarafından {{announcement}} olarak değiştirildi',
+ Room_changed_avatar: 'Oda profil fotoğrafı {{userBy}} tarafından değiştirildi',
+ Room_changed_description: 'Oda açıklaması, {{userBy}} tarafından {{description}} olarak değiştirildi',
+ Room_changed_privacy: 'Oda açıklaması, {{userBy}} tarafından {{description}} olarak değiştirildi',
+ Room_changed_topic: 'Oda konusu, {{userBy}} tarafından {{topic}} olarak değiştirildi',
+ Room_Files: 'Oda Dosyaları',
+ Room_Info_Edit: 'Oda Bilgilerini Düzenle',
+ Room_Info: 'Oda Bilgisi',
+ Room_Members: 'Oda Üyeleri',
+ Room_name_changed: 'Oda adı, {{userBy}} tarafından {{name}} olarak değiştirildi',
+ SAVE: 'KAYDET',
+ Save_Changes: 'Değişiklikleri Kaydet',
+ Save: 'Kaydet',
+ Saved: 'Kaydedildi',
+ saving_preferences: 'tercihler kaydediliyor',
+ saving_profile: 'profil kaydediliyor',
+ saving_settings: 'ayarlar kaydediliyor',
+ saved_to_gallery: 'Galeriye kaydedildi',
+ Save_Your_E2E_Password: '(E2E) Şifrenizi Kaydedin',
+ Save_Your_Encryption_Password: 'Şifreleme Parolanızı Kaydedin',
+ Save_Your_Encryption_Password_warning: 'Bu parola hiçbir yerde saklanmadığından başka bir yere dikkatlice kaydedin.',
+ Save_Your_Encryption_Password_info: 'Parolanızı kaybettiğinizde, kurtarmanın bir yolu olmadığını ve iletilerinize erişiminizi kaybedeceğinizi unutmayın.',
+ Search_Messages: 'İleti ara',
+ Search: 'Ara',
+ Search_by: 'Ara',
+ Search_global_users: 'Global kullanıcıları ara',
+ Search_global_users_description: 'Açarsanız, diğer şirketlerden veya sunuculardan herhangi bir kullanıcıyı arayabilirsiniz.',
+ Seconds: '{{second}} saniye',
+ Security_and_privacy: 'Güvenlik ve gizlilik',
+ Select_Avatar: 'Profil resmi seç',
+ Select_Server: 'Sunucu seç',
+ Select_Users: 'Kullanıcıları seç',
+ Select_a_Channel: 'Kanal Seç',
+ Select_a_Department: 'Bölüm Seç',
+ Select_an_option: 'Bir seçenek seçin',
+ Select_a_User: 'Kullanıcı Seç',
+ Send: 'Yolla',
+ Send_audio_message: 'Sesli ileti gönder',
+ Send_crash_report: 'Çökme raporu gönder',
+ Send_message: 'İleti gönder',
+ Send_me_the_code_again: 'Kodu tekrar gönder',
+ Send_to: 'Gönderiliyor...',
+ Sending_to: 'Gönderiliyor:',
+ Sent_an_attachment: 'Bir ek gönderildi',
+ Server: 'Sunucu',
+ Servers: 'Sunucular',
+ Server_version: 'Sunucu versiyonu: {{version}}',
+ Set_username_subtitle: 'Kullanıcı adı başkalarının iletilerde sizden bahsetmesine izin vermek için kullanılır',
+ Set_custom_status: 'Özelleştirilmiş durumu ayarlayın',
+ Set_status: 'Durumu ayarla',
+ Status_saved_successfully: 'Durum başarıyla kaydedildi!',
+ Settings: 'Ayarlar',
+ Settings_succesfully_changed: 'Ayarlar başarıyla değiştirildi!',
+ Share: 'Paylaş',
+ Share_Link: 'Bağlantı paylaş',
+ Share_this_app: 'Bu uygulamayı paylaş',
+ Show_more: 'Daha fazla göster..',
+ Show_Unread_Counter: 'Okunmamış Sayacını Göster',
+ Show_Unread_Counter_Info: 'Okunmamış sayacı, listede kanalın sağ tarafında bir rozet olarak görüntülenir',
+ Sign_in_your_server: 'Sunucunuzda oturum açın',
+ Sign_Up: 'Kaydol',
+ Some_field_is_invalid_or_empty: 'Bazı alanlar geçersiz veya boş',
+ Sorting_by: '{{key}} göre sıralanıyor',
+ Sound: 'Ses',
+ Star_room: 'Odayı Yıldızla',
+ Star: 'Yıldızla',
+ Starred_Messages: 'Yıldızlı İletiler',
+ starred: 'yıldızlandı',
+ Starred: 'Yıldızlandı',
+ Start_of_conversation: 'Konuşma başlangıcı',
+ Start_a_Discussion: 'Tartışma Başlat',
+ Started_discussion: 'Bir tartışma başlattı:',
+ Started_call: 'Arama {{userBy}} tarafından başlatıldı',
+ Submit: 'Kaydet',
+ Table: 'Tablo',
+ Tags: 'Etiketler',
+ Take_a_photo: 'Fotoğraf çek',
+ Take_a_video: 'Video çek',
+ Take_it: 'Al!',
+ tap_to_change_status: 'durumu değiştirmek için dokunun',
+ Tap_to_view_servers_list: 'Sunucu listesini görüntülemek için dokunun',
+ Terms_of_Service: ' Kullanım Şartları ',
+ Theme: 'Tema',
+ The_user_wont_be_able_to_type_in_roomName: 'Kullanıcı {{roomName}} içinde yazamayacak',
+ The_user_will_be_able_to_type_in_roomName: 'Kullanıcı {{roomName}} içinde yazabilecek',
+ There_was_an_error_while_action: '{{action}} sırasında bir hata oluştu!',
+ This_room_is_blocked: 'Bu oda engellendi',
+ This_room_is_read_only: 'Bu oda yazma kısıtlı',
+ Thread: 'Başlık',
+ Threads: 'Başlıklar',
+ Timezone: 'Saat dilimi',
+ To: 'Kime',
+ topic: 'konu',
+ Topic: 'Konu',
+ Translate: 'Çevir',
+ Try_again: 'Tekrar deneyin',
+ Two_Factor_Authentication: 'İki faktörlü Kimlik Doğrulama',
+ Type_the_channel_name_here: 'Kanal adını buraya yazın',
+ unarchive: 'arşivden çıkar',
+ UNARCHIVE: 'ARŞİVDEN ÇIKAR',
+ Unblock_user: 'Kullanıcının engelini kaldır',
+ Unfavorite: 'Favorilerden Çıkar',
+ Unfollowed_thread: 'Takip edilmeyen başlık',
+ Unmute: 'Sesi Aç',
+ unmuted: 'Sesi Açıldı',
+ Unpin: 'Sabitlemeyi kaldır',
+ unread_messages: 'okunmamış',
+ Unread: 'Okunmamış',
+ Unread_on_top: 'Okunmamışlar üstte',
+ Unstar: 'Yıldızı kaldır',
+ Updating: 'Güncelleniyor...',
+ Uploading: 'Gönderiliyor',
+ Upload_file_question_mark: 'Dosya gönderilsin mi?',
+ User: 'Kullanıcı',
+ Users: 'Kullanıcılar',
+ User_added_by: '{{userAdded}} adlı kullanıcı {{userBy}} tarafından eklendi',
+ User_Info: 'Kullanıcı bilgisi',
+ User_has_been_key: 'Kullanıcı {{key}} olmuştur',
+ User_is_no_longer_role_by_: '{{user}} artık {{role}} değil ({{userBy}} tarafından)',
+ User_muted_by: '{{userMuted}} adlı kullanıcının sesini {{userBy}} kapattı',
+ User_removed_by: '{{userRemoved}} kullanıcısı {{userBy}} tarafından kaldırıldı',
+ User_sent_an_attachment: '{{user}} bir ek gönderdi',
+ User_unmuted_by: '{{userUnmuted}} kullanıcısının sesi {{userBy}} tarafından açıldı',
+ User_was_set_role_by_: '{{user}}, {{userBy}} tarafından {{role}} ayarlandı',
+ Username_is_empty: 'Kullanıcı adı boş!',
+ Username: 'Kullanıcı adı',
+ Username_or_email: 'Kullanıcı adı ya da e-posta',
+ Uses_server_configuration: 'Sunucu yapılandırmasını kullanır',
+ Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: 'Genellikle tartışma, "Nasıl resim yüklerim?" gibi bir soruyla başlar.',
+ Validating: 'Doğrulanıyor',
+ Registration_Succeeded: 'Kayıt Başarılı!',
+ Verify: 'Onayla',
+ Verify_email_title: 'Kaydınızı onaylayın!',
+ Verify_email_desc: 'Kaydınızı onaylamak için size bir e-posta gönderdik. Kısa süre içinde bir e-posta almazsanız, lütfen geri gelin ve tekrar deneyin.',
+ Verify_your_email_for_the_code_we_sent: 'Gönderdiğimiz kod için e-postanızı doğrulayın',
+ Video_call: 'Görüntülü arama',
+ View_Original: 'Orijinali Görüntüle',
+ Voice_call: 'Sesli arama',
+ Waiting_for_network: 'Ağ bağlantısı bekleniyor ...',
+ Websocket_disabled: 'Bu sunucu için Websocket devre dışı bırakıldı.\n{{contact}}',
+ Welcome: 'Hoşgeldiniz',
+ What_are_you_doing_right_now: 'Şu an ne yapıyorsun?',
+ Whats_your_2fa: '2 faktör doğrulama (2FA) kodunuz nedir?',
+ Without_Servers: 'Sunucusuz',
+ Workspaces: 'Çalışma alanları',
+ Would_you_like_to_return_the_inquiry: 'Başvuruyu geri çevirmek ister misiniz?',
+ Write_External_Permission_Message: 'Rocket.Chat\'in galerinize erişmesi gerekiyor, böylece resimleri kaydedebilirsiniz.',
+ Write_External_Permission: 'Galeri İzni',
+ Yes: 'Evet',
+ Yes_action_it: 'Evet, {{action}}!',
+ Yesterday: 'Dün',
+ You_are_in_preview_mode: 'İzleme modundasınız',
+ You_are_offline: 'Çevrimdışısınız',
+ You_can_search_using_RegExp_eg: 'Düzenli ifadeleri (Regular Expressions) kullanabilirsiniz. Örneğin: `/^text$/i`',
+ You_colon: 'Siz: ',
+ you_were_mentioned: 'senden bahsedildi',
+ You_were_removed_from_channel: '{{channel}} kanalından çıkarıldınız',
+ you: 'siz',
+ You: 'Siz',
+ Logged_out_by_server: 'Sunucu tarafından çıkış yaptınız. Lütfen tekrar giriş yapın.',
+ You_need_to_access_at_least_one_RocketChat_server_to_share_something: 'Bir şeyler paylaşmak için Rocket.Chat sunucusuna erişmeniz gerekir.',
+ You_need_to_verifiy_your_email_address_to_get_notications: 'Bildirim almak için e-posta adresinizi doğrulamanız gerekiyor',
+ Your_certificate: 'Sertifikanız',
+ Your_message: 'İletiınız',
+ Your_invite_link_will_expire_after__usesLeft__uses: 'Davet bağlantınızın geçerliliği {{usesLeft}} kullanımdan sonra sona erecek.',
+ Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: 'Davet bağlantınızın geçerliliği {{date}} tarihinde veya {{usesLeft}} kullanımdan sonra sona erecek.',
+ Your_invite_link_will_expire_on__date__: 'Davet bağlantınızın geçerlilik süresi {{date}} tarihinde sona erecek.',
+ Your_invite_link_will_never_expire: 'Davet bağlantınızın geçerlilik süresi asla dolmayacak.',
+ Your_workspace: 'Çalışma alanınız',
+ Your_password_is: 'Şifreniz',
+ Version_no: 'Versiyon: {{version}}',
+ You_will_not_be_able_to_recover_this_message: 'Bu iletiyi kurtaramayacaksınız!',
+ You_will_unset_a_certificate_for_this_server: 'Bu sunucu için bir sertifika ayarını kaldıracaksınız',
+ Change_Language: 'Dili değiştir',
+ Crash_report_disclaimer: 'Sohbetlerinizin içeriğini asla takip etmiyoruz. Çökme raporu ve analiz olayları, sorunları tanımlamak ve düzeltmek için yalnızca bizim için ilgili bilgileri içerir.',
+ Type_message: 'İleti yaz',
+ Room_search: 'Oda arama',
+ Room_selection: 'Oda seçimi 1...9',
+ Next_room: 'Sonraki oda',
+ Previous_room: 'Önceki oda',
+ New_room: 'Yeni oda',
+ Upload_room: 'Odaya yükle',
+ Search_messages: 'İletilerda ara',
+ Scroll_messages: 'İletilerı kaydır',
+ Reply_latest: 'Sonuncuyu yanıtla',
+ Reply_in_Thread: 'Konu içinde cevapla',
+ Server_selection: 'Sunucu seçimi',
+ Server_selection_numbers: 'Sunucu seçimi 1...9',
+ Add_server: 'Sunucu ekle',
+ New_line: 'Yeni satır',
+ You_will_be_logged_out_of_this_application: 'Bu uygulamadan çıkış yapacaksınız.',
+ Clear: 'Temizle',
+ This_will_clear_all_your_offline_data: 'Bu, tüm çevrimdışı verilerinizi temizleyecektir.',
+ This_will_remove_all_data_from_this_server: 'Bu, bu sunucudaki tüm verileri kaldıracaktır.',
+ Mark_unread: 'Okunmadı olarak işaretle',
+ Wait_activation_warning: 'Giriş yapmadan önce, hesabınız bir yönetici tarafından manuel olarak etkinleştirilmelidir.',
+ Screen_lock: 'Ekran kilidi',
+ Local_authentication_biometry_title: 'Doğrula',
+ Local_authentication_biometry_fallback: 'Parola kullan',
+ Local_authentication_unlock_option: 'Şifre ile Kilidi Açın',
+ Local_authentication_change_passcode: 'Parolayı Değiştir',
+ Local_authentication_info: 'Not: Parolayı unutursanız, uygulamayı silmeniz ve yeniden yüklemeniz gerekir.',
+ Local_authentication_facial_recognition: 'yüz tanıma',
+ Local_authentication_fingerprint: 'parmak izi',
+ Local_authentication_unlock_with_label: '{{label}} ile kilidi açın',
+ Local_authentication_auto_lock_60: '1 dakika sonra',
+ Local_authentication_auto_lock_300: '5 dakika sonra',
+ Local_authentication_auto_lock_900: '15 dakika sonra',
+ Local_authentication_auto_lock_1800: '30 dakika sonra',
+ Local_authentication_auto_lock_3600: '1 saat sonra',
+ Passcode_enter_title: 'Şifrenizi giriniz',
+ Passcode_choose_title: 'Yeni şifrenizi yazın',
+ Passcode_choose_confirm_title: 'Yeni şifrenizi onaylayın',
+ Passcode_choose_error: 'Parolalar eşleşmiyor. Tekrar deneyin.',
+ Passcode_choose_force_set: 'Yönetici tarafından istenen şifre',
+ Passcode_app_locked_title: 'Uygulama kilitlendi',
+ Passcode_app_locked_subtitle: '{{timeLeft}} saniye içinde tekrar deneyin',
+ After_seconds_set_by_admin: '{{seconds}} saniye sonra (yönetici tarafından belirlenir)',
+ Dont_activate: 'Şimdi etkinleştirme',
+ Queued_chats: 'Sıralı sohbetler',
+ Queue_is_empty: 'Sıra boş',
+ Logout_from_other_logged_in_locations: 'Giriş yapılan diğer konumlardan çıkış yap',
+ You_will_be_logged_out_from_other_locations: 'Diğer konumlardan çıkış yapacaksınız.',
+ Logged_out_of_other_clients_successfully: 'Diğer istemcilerden başarıyla çıkış yapıldı',
+ Logout_failed: 'Oturum kapatma başarısız oldu!',
+ Log_analytics_events: 'Olayları günlüğe kaydet',
+ E2E_encryption_change_password_title: 'Şifreleme Parolasını Değiştir',
+ E2E_encryption_change_password_description: 'Artık şifrelenmiş özel gruplar ve doğrudan iletiler oluşturabilirsiniz. Mevcut özel grupları veya özel iletileri şifreli olarak da değiştirebilirsiniz. \nBu uçtan uca şifrelemedir, bu nedenle iletilerinizi şifrelemek / çözmek için kullanılan anahtar sunucuya kaydedilmez. Bu nedenle şifrenizi güvenli bir yerde saklamanız gerekir. Uçtan uca şifrelemeyi (E2E) kullanmak istediğiniz diğer cihazlara girmeniz istenecektir.',
+ E2E_encryption_change_password_error: 'Uçtan uca şifreleme (E2E) anahtar şifresi değiştirilirken hata!',
+ E2E_encryption_change_password_success: 'Uçtan uca şifreleme (E2E) anahtar şifresi başarıyla değiştirildi!',
+ E2E_encryption_change_password_message: 'Dikkatlice kaydettiğinizden emin olun.',
+ E2E_encryption_change_password_confirmation: 'Evet, değiştir!',
+ E2E_encryption_reset_title: 'Uçtan uca şifreleme (E2E) Anahtarını Sıfırla',
+ E2E_encryption_reset_description: 'Bu seçenek mevcut uçtan uca şifreleme (E2E) anahtarınızı kaldıracak ve oturumu kapatacaktır. \nTekrar oturum açtığınızda, Rocket.Chat size yeni bir anahtar oluşturacak ve çevrimiçi olarak bir veya daha fazla üyesi olan şifreli herhangi bir odaya erişiminizi geri yükleyecektir. \nUçtan uca şifrelemenin (E2E) doğası gereği Rocket.Chat, çevrimiçi üyesi olmayan hiçbir şifreli odaya erişimi geri yükleyemez.',
+ E2E_encryption_reset_button: 'Uçtan uca şifreleme (E2E) anahtarını sıfırla',
+ E2E_encryption_reset_error: 'Uçtan uca şifreleme (E2E) anahtarı sıfırlanırken hata!',
+ E2E_encryption_reset_message: 'Oturumunuz kapatılacak!',
+ E2E_encryption_reset_confirmation: 'Evet, sıfırla',
+ Following: 'Takip ediliyor',
+ Threads_displaying_all: 'Tüm konular görüntüleniyor',
+ Threads_displaying_following: 'Takip edilen konular görüntüleniyor',
+ Threads_displaying_unread: 'Okunmamış konular görüntüleniyor',
+ No_threads: 'Konu yok',
+ No_threads_following: 'Herhangi bir konuyu takip etmiyorsunuz',
+ No_threads_unread: 'Okunmamış konu yok',
+ Messagebox_Send_to_channel: 'Kanala gönder',
+ Set_as_leader: 'Lider olarak ayarla',
+ Set_as_moderator: 'Moderatör olarak ayarla',
+ Set_as_owner: 'Sahip olarak ayarla',
+ Remove_as_leader: 'Lider olarak kaldır',
+ Remove_as_moderator: 'Moderatör olarak kaldır',
+ Remove_as_owner: 'Sahip olarak kaldır',
+ Remove_from_room: 'Odadan çıkar',
+ Ignore: 'Yok say',
+ Unignore: 'Yok sayma',
+ User_has_been_ignored: 'Kullanıcı yok sayıldı.',
+ User_has_been_unignored: 'Kullanıcı artık yok sayılmıyor.',
+ User_has_been_removed_from_s: 'Kullanıcı {{s}} alanından kaldırıldı.',
+ User__username__is_now_a_leader_of__room_name_: '{{Username}} kullanıcısı artık {{room_name}} lideridir.',
+ User__username__is_now_a_moderator_of__room_name_: '{{Username}} kullanıcısı artık bir {{room_name}} moderatörüdür.',
+ User__username__is_now_a_owner_of__room_name_: '{{Username}} kullanıcısı artık {{room_name}} adlı odanın sahibidir.',
+ User__username__removed_from__room_name__leaders: '{{Username}} adlı kullanıcı, {{room_name}} liderlerinden kaldırıldı.',
+ User__username__removed_from__room_name__moderators: '{{Username}} adlı kullanıcı, {{room_name}} moderatörlerinden kaldırıldı.',
+ User__username__removed_from__room_name__owners: '{{Username}} adlı kullanıcı, {{room_name}} sahiplerinden kaldırıldı.',
+ The_user_will_be_removed_from_s: 'Kullanıcı, {{s}} alanından kaldırılacak!',
+ Yes_remove_user: 'Evet, kullanıcıyı kaldır!',
+ Direct_message: 'Özel ileti',
+ Message_Ignored: 'İleti yok sayıldı. Görüntülemek için dokunun.',
+ Enter_workspace_URL: 'Çalışma alanı URL\'nizi girin',
+ Workspace_URL_Example: 'Örn. sirketiniz.rocket.chat'
+};
diff --git a/app/i18n/locales/zh-CN.js b/app/i18n/locales/zh-CN.js
index 11f555ee7..62ee3da53 100644
--- a/app/i18n/locales/zh-CN.js
+++ b/app/i18n/locales/zh-CN.js
@@ -563,7 +563,6 @@ export default {
Username: '用户名',
Username_or_email: '用户名或邮箱',
Uses_server_configuration: '使用服务器设置',
- Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: '通常, 一个讨论会由一个问题开始, 例如 \\"如何上传图片?\\"',
Validating: '正在验证',
Registration_Succeeded: '注册成功',
Verify: '验证',
@@ -598,7 +597,6 @@ export default {
You_need_to_access_at_least_one_RocketChat_server_to_share_something: '您需要访问至少一台Rocket.Chat服务器才能共享某些内容。',
You_need_to_verifiy_your_email_address_to_get_notications: '您需要先验证您的邮箱以启用通知',
Your_certificate: '你的证书',
- Your_message: '你的信息',
Your_invite_link_will_expire_after__usesLeft__uses: '您的邀请链接将在{{usesLeft}}使用后到期。',
Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: '您的邀请链接将于{{date}}或{{usesLeft}}使用后到期。',
Your_invite_link_will_expire_on__date__: '您的邀请链接将于{{date}}到期。',
diff --git a/app/i18n/locales/zh-TW.js b/app/i18n/locales/zh-TW.js
index 366e7c7f1..7bfcc3b9d 100644
--- a/app/i18n/locales/zh-TW.js
+++ b/app/i18n/locales/zh-TW.js
@@ -563,7 +563,6 @@ export default {
Username: '使用者名稱',
Username_or_email: '使用者名稱或電子郵件',
Uses_server_configuration: '使用伺服器設定',
- Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: '通常,討論會由一個問題開始,像是 \\\'如何上傳一個圖片?\\\'',
Validating: '正在驗證',
Registration_Succeeded: '註冊成功',
Verify: '驗證',
@@ -598,7 +597,6 @@ export default {
You_need_to_access_at_least_one_RocketChat_server_to_share_something: '您需要至少連接一個 Rocket.Chat 伺服器才能共享某些内容。',
You_need_to_verifiy_your_email_address_to_get_notications: '您需要先驗證您的電子郵件以啟用通知',
Your_certificate: '你的證書',
- Your_message: '你的訊息',
Your_invite_link_will_expire_after__usesLeft__uses: '您的邀請連結將在{{usesLeft}}使用後到期。',
Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: '您的邀請連結將於{{date}}或{{usesLeft}}使用後到期。',
Your_invite_link_will_expire_on__date__: '您的邀請連結將於{{date}}到期。',
diff --git a/app/lib/encryption/constants.js b/app/lib/encryption/constants.js
index 216746a7b..d10539eda 100644
--- a/app/lib/encryption/constants.js
+++ b/app/lib/encryption/constants.js
@@ -2,7 +2,6 @@ export const E2E_MESSAGE_TYPE = 'e2e';
export const E2E_PUBLIC_KEY = 'RC_E2E_PUBLIC_KEY';
export const E2E_PRIVATE_KEY = 'RC_E2E_PRIVATE_KEY';
export const E2E_RANDOM_PASSWORD_KEY = 'RC_E2E_RANDOM_PASSWORD_KEY';
-export const E2E_REFRESH_MESSAGES_KEY = 'E2E_REFRESH_MESSAGES_KEY';
export const E2E_STATUS = {
PENDING: 'pending',
DONE: 'done'
diff --git a/app/lib/encryption/encryption.js b/app/lib/encryption/encryption.js
index 90cbe5310..972f2c588 100644
--- a/app/lib/encryption/encryption.js
+++ b/app/lib/encryption/encryption.js
@@ -68,10 +68,6 @@ class Encryption {
return this.readyPromise;
}
- get hasPrivateKey() {
- return !!this.privateKey;
- }
-
// Stop Encryption client
stop = () => {
this.userId = null;
diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js
index 5020ee4ad..721222754 100644
--- a/app/lib/rocketchat.js
+++ b/app/lib/rocketchat.js
@@ -596,25 +596,19 @@ const RocketChat = {
readMessages,
resendMessage,
- async search({ text, filterUsers = true, filterRooms = true }) {
+ async localSearch({ text, filterUsers = true, filterRooms = true }) {
const searchText = text.trim();
-
- if (this.oldPromise) {
- this.oldPromise('cancel');
- }
-
if (searchText === '') {
- delete this.oldPromise;
return [];
}
-
const db = database.active;
const likeString = sanitizeLikeString(searchText);
let data = await db.collections.get('subscriptions').query(
Q.or(
Q.where('name', Q.like(`%${ likeString }%`)),
Q.where('fname', Q.like(`%${ likeString }%`))
- )
+ ),
+ Q.experimentalSortBy('room_updated_at', Q.desc)
).fetch();
if (filterUsers && !filterRooms) {
@@ -627,18 +621,34 @@ const RocketChat = {
data = data.map((sub) => {
if (sub.t !== 'd') {
- return ({
+ return {
rid: sub.rid,
name: sub.name,
fname: sub.fname,
avatarETag: sub.avatarETag,
t: sub.t,
- search: true
- });
+ encrypted: sub.encrypted
+ };
}
return sub;
});
+ return data;
+ },
+
+ async search({ text, filterUsers = true, filterRooms = true }) {
+ const searchText = text.trim();
+
+ if (this.oldPromise) {
+ this.oldPromise('cancel');
+ }
+
+ if (searchText === '') {
+ return [];
+ }
+
+ const data = await this.localSearch({ text, filterUsers, filterRooms });
+
const usernames = data.map(sub => sub.name);
try {
if (data.length < 7) {
@@ -647,13 +657,18 @@ const RocketChat = {
new Promise((resolve, reject) => this.oldPromise = reject)
]);
if (filterUsers) {
- data = data.concat(users.map(user => ({
- ...user,
- rid: user.username,
- name: user.username,
- t: 'd',
- search: true
- })));
+ users
+ .filter((item1, index) => users.findIndex(item2 => item2._id === item1._id) === index) // Remove duplicated data from response
+ .filter(user => !data.some(sub => user.username === sub.name)) // Make sure to remove users already on local database
+ .forEach((user) => {
+ data.push({
+ ...user,
+ rid: user.username,
+ name: user.username,
+ t: 'd',
+ search: true
+ });
+ });
}
if (filterRooms) {
rooms.forEach((room) => {
@@ -697,11 +712,11 @@ const RocketChat = {
},
createDiscussion({
- prid, pmid, t_name, reply, users
+ prid, pmid, t_name, reply, users, encrypted
}) {
// RC 1.0.0
return this.post('rooms.createDiscussion', {
- prid, pmid, t_name, reply, users
+ prid, pmid, t_name, reply, users, encrypted
});
},
@@ -865,14 +880,7 @@ const RocketChat = {
methodCallWrapper(method, ...params) {
const { API_Use_REST_For_DDP_Calls } = reduxStore.getState().settings;
if (API_Use_REST_For_DDP_Calls) {
- return new Promise(async(resolve, reject) => {
- const data = await this.post(`method.call/${ method }`, { message: JSON.stringify({ method, params }) });
- const response = JSON.parse(data.message);
- if (response?.error) {
- return reject(response.error);
- }
- return resolve(response.result);
- });
+ return this.post(`method.call/${ method }`, { message: JSON.stringify({ method, params }) });
}
return this.methodCall(method, ...params);
},
@@ -1067,14 +1075,30 @@ const RocketChat = {
},
post(...args) {
return new Promise(async(resolve, reject) => {
+ const isMethodCall = args[0]?.startsWith('method.call/');
try {
const result = await this.sdk.post(...args);
+
+ /**
+ * if API_Use_REST_For_DDP_Calls is enabled and it's a method call,
+ * responses have a different object structure
+ */
+ if (isMethodCall) {
+ const response = JSON.parse(result.message);
+ if (response?.error) {
+ throw response.error;
+ }
+ return resolve(response.result);
+ }
return resolve(result);
} catch (e) {
- if (e.data && (e.data.errorType === 'totp-required' || e.data.errorType === 'totp-invalid')) {
- const { details } = e.data;
+ const errorType = isMethodCall ? e?.error : e?.data?.errorType;
+ const totpInvalid = 'totp-invalid';
+ const totpRequired = 'totp-required';
+ if ([totpInvalid, totpRequired].includes(errorType)) {
+ const { details } = isMethodCall ? e : e?.data;
try {
- await twoFactor({ method: details?.method, invalid: e.data.errorType === 'totp-invalid' });
+ await twoFactor({ method: details?.method, invalid: errorType === totpInvalid });
return resolve(this.post(...args));
} catch {
// twoFactor was canceled
diff --git a/app/lib/userPreferences.js b/app/lib/userPreferences.js
index 633a5198d..3377856d6 100644
--- a/app/lib/userPreferences.js
+++ b/app/lib/userPreferences.js
@@ -1,7 +1,5 @@
import MMKVStorage from 'react-native-mmkv-storage';
-import log from '../utils/log';
-
const MMKV = new MMKVStorage.Loader()
// MODES.MULTI_PROCESS = ACCESSIBLE BY APP GROUP (iOS)
.setProcessingMode(MMKVStorage.MODES.MULTI_PROCESS)
@@ -11,25 +9,6 @@ const MMKV = new MMKVStorage.Loader()
class UserPreferences {
constructor() {
this.mmkv = MMKV;
-
- this.encryptMigratedData();
- }
-
- // It should run only once
- async encryptMigratedData() {
- try {
- const encryptMigration = await this.getBoolAsync('encryptMigration');
-
- if (!encryptMigration) {
- // Encrypt the migrated data
- await this.mmkv.encryption.encrypt();
-
- // Mark as completed
- await this.setBoolAsync('encryptMigration', true);
- }
- } catch (e) {
- log(e);
- }
}
async getStringAsync(key) {
diff --git a/app/presentation/RoomItem/Touchable.js b/app/presentation/RoomItem/Touchable.js
index 00f44b788..37b45639d 100644
--- a/app/presentation/RoomItem/Touchable.js
+++ b/app/presentation/RoomItem/Touchable.js
@@ -48,7 +48,7 @@ class Touchable extends React.Component {
rowState: 0 // 0: closed, 1: right opened, -1: left opened
};
this._onGestureEvent = Animated.event(
- [{ nativeEvent: { translationX: this.dragX } }]
+ [{ nativeEvent: { translationX: this.dragX } }], { useNativeDriver: true }
);
this._value = 0;
}
diff --git a/app/presentation/ServerItem/index.js b/app/presentation/ServerItem/index.js
index 53163a43c..ab69020f2 100644
--- a/app/presentation/ServerItem/index.js
+++ b/app/presentation/ServerItem/index.js
@@ -1,23 +1,33 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { View, Text } from 'react-native';
+import { View, Text, Pressable } from 'react-native';
import FastImage from '@rocket.chat/react-native-fast-image';
-import Touch from '../../utils/touch';
import Check from '../../containers/Check';
import styles, { ROW_HEIGHT } from './styles';
import { themes } from '../../constants/colors';
+import { isIOS } from '../../utils/deviceInfo';
+import { withTheme } from '../../theme';
export { ROW_HEIGHT };
+const defaultLogo = require('../../static/images/logo.png');
+
const ServerItem = React.memo(({
- server, item, onPress, hasCheck, theme
+ item, onPress, onLongPress, hasCheck, theme
}) => (
- onLongPress?.()}
testID={`rooms-list-header-server-${ item.id }`}
- theme={theme}
+ android_ripple={{
+ color: themes[theme].bannerBackground
+ }}
+ style={({ pressed }) => ({
+ backgroundColor: isIOS && pressed
+ ? themes[theme].bannerBackground
+ : themes[theme].backgroundColor
+ })}
>
{item.iconURL
@@ -27,33 +37,33 @@ const ServerItem = React.memo(({
uri: item.iconURL,
priority: FastImage.priority.high
}}
- defaultSource={{ uri: 'logo' }}
+ defaultSource={defaultLogo}
style={styles.serverIcon}
onError={() => console.log('err_loading_server_icon')}
/>
)
: (
)
}
- {item.name || item.id}
- {item.id}
+ {item.name || item.id}
+ {item.id}
- {item.id === server && hasCheck ? : null}
+ {hasCheck ? : null}
-
+
));
ServerItem.propTypes = {
- onPress: PropTypes.func.isRequired,
item: PropTypes.object.isRequired,
+ onPress: PropTypes.func.isRequired,
+ onLongPress: PropTypes.func,
hasCheck: PropTypes.bool,
- server: PropTypes.string,
theme: PropTypes.string
};
-export default ServerItem;
+export default withTheme(ServerItem);
diff --git a/app/presentation/ServerItem/styles.js b/app/presentation/ServerItem/styles.js
index 34ed0c158..ece4105df 100644
--- a/app/presentation/ServerItem/styles.js
+++ b/app/presentation/ServerItem/styles.js
@@ -5,10 +5,6 @@ import sharedStyles from '../../views/Styles';
export const ROW_HEIGHT = 56;
export default StyleSheet.create({
- serverItem: {
- height: ROW_HEIGHT,
- justifyContent: 'center'
- },
serverItemContainer: {
flexDirection: 'row',
alignItems: 'center'
@@ -16,20 +12,22 @@ export default StyleSheet.create({
serverIcon: {
width: 44,
height: 44,
- marginHorizontal: 15,
- borderRadius: 4
+ margin: 12,
+ borderRadius: 4,
+ resizeMode: 'contain'
},
serverTextContainer: {
flex: 1,
flexDirection: 'column',
- justifyContent: 'center'
+ justifyContent: 'center',
+ paddingRight: 18
},
serverName: {
fontSize: 18,
...sharedStyles.textSemibold
},
serverUrl: {
- fontSize: 15,
+ fontSize: 16,
...sharedStyles.textRegular
}
});
diff --git a/app/reducers/encryption.js b/app/reducers/encryption.js
index 729c8be41..0145ae2d1 100644
--- a/app/reducers/encryption.js
+++ b/app/reducers/encryption.js
@@ -1,11 +1,18 @@
import { ENCRYPTION } from '../actions/actionsTypes';
const initialState = {
+ enabled: false,
banner: null
};
export default function encryption(state = initialState, action) {
switch (action.type) {
+ case ENCRYPTION.SET:
+ return {
+ ...state,
+ enabled: action.enabled,
+ banner: action.banner
+ };
case ENCRYPTION.SET_BANNER:
return {
...state,
diff --git a/app/reducers/server.js b/app/reducers/server.js
index 96b8d400c..2c5b98199 100644
--- a/app/reducers/server.js
+++ b/app/reducers/server.js
@@ -8,7 +8,8 @@ const initialState = {
version: null,
loading: true,
adding: false,
- previousServer: null
+ previousServer: null,
+ changingServer: false
};
@@ -34,7 +35,8 @@ export default function server(state = initialState, action) {
version: action.version,
connecting: true,
connected: false,
- loading: true
+ loading: true,
+ changingServer: action.changeServer
};
case SERVER.SELECT_SUCCESS:
return {
@@ -43,14 +45,16 @@ export default function server(state = initialState, action) {
version: action.version,
connecting: false,
connected: true,
- loading: false
+ loading: false,
+ changingServer: false
};
case SERVER.SELECT_FAILURE:
return {
...state,
connecting: false,
connected: false,
- loading: false
+ loading: false,
+ changingServer: false
};
case SERVER.INIT_ADD:
return {
diff --git a/app/sagas/encryption.js b/app/sagas/encryption.js
index 3aaed531c..f28c77834 100644
--- a/app/sagas/encryption.js
+++ b/app/sagas/encryption.js
@@ -2,7 +2,7 @@ import EJSON from 'ejson';
import { takeLatest, select, put } from 'redux-saga/effects';
import { ENCRYPTION } from '../actions/actionsTypes';
-import { encryptionSetBanner } from '../actions/encryption';
+import { encryptionSet } from '../actions/encryption';
import { Encryption } from '../lib/encryption';
import Navigation from '../lib/Navigation';
import {
@@ -52,14 +52,14 @@ const handleEncryptionInit = function* handleEncryptionInit() {
// A private key was received from the server, but it's not saved locally yet
// Show the banner asking for the password
if (!storedPrivateKey && keys?.privateKey) {
- yield put(encryptionSetBanner(E2E_BANNER_TYPE.REQUEST_PASSWORD));
+ yield put(encryptionSet(false, E2E_BANNER_TYPE.REQUEST_PASSWORD));
return;
}
// If the user has a private key stored, but never entered the password
const storedRandomPassword = yield UserPreferences.getStringAsync(`${ server }-${ E2E_RANDOM_PASSWORD_KEY }`);
if (storedRandomPassword) {
- yield put(encryptionSetBanner(E2E_BANNER_TYPE.SAVE_PASSWORD));
+ yield put(encryptionSet(true, E2E_BANNER_TYPE.SAVE_PASSWORD));
}
// Fetch stored public e2e key for this server
@@ -72,10 +72,11 @@ const handleEncryptionInit = function* handleEncryptionInit() {
if (storedPublicKey && storedPrivateKey) {
// Persist these keys
yield Encryption.persistKeys(server, storedPublicKey, storedPrivateKey);
+ yield put(encryptionSet(true));
} else {
// Create new keys since the user doesn't have any
yield Encryption.createKeys(user.id, server);
- yield put(encryptionSetBanner(E2E_BANNER_TYPE.SAVE_PASSWORD));
+ yield put(encryptionSet(true, E2E_BANNER_TYPE.SAVE_PASSWORD));
}
// Decrypt all pending messages/subscriptions
@@ -87,7 +88,7 @@ const handleEncryptionInit = function* handleEncryptionInit() {
const handleEncryptionStop = function* handleEncryptionStop() {
// Hide encryption banner
- yield put(encryptionSetBanner());
+ yield put(encryptionSet());
// Stop Encryption client
Encryption.stop();
};
@@ -112,7 +113,7 @@ const handleEncryptionDecodeKey = function* handleEncryptionDecodeKey({ password
Encryption.initialize(user.id);
// Hide encryption banner
- yield put(encryptionSetBanner());
+ yield put(encryptionSet(true));
Navigation.back();
} catch {
diff --git a/app/sagas/login.js b/app/sagas/login.js
index 8b883a626..cf487eed4 100644
--- a/app/sagas/login.js
+++ b/app/sagas/login.js
@@ -31,7 +31,6 @@ import UserPreferences from '../lib/userPreferences';
import { inquiryRequest, inquiryReset } from '../ee/omnichannel/actions/inquiry';
import { isOmnichannelStatusAvailable } from '../ee/omnichannel/lib';
-import { E2E_REFRESH_MESSAGES_KEY } from '../lib/encryption/constants';
import Navigation from '../lib/Navigation';
const getServer = state => state.server.server;
@@ -120,32 +119,7 @@ const fetchEnterpriseModules = function* fetchEnterpriseModules({ user }) {
}
};
-const fetchRooms = function* fetchRooms({ server }) {
- try {
- // Read the flag to check if refresh was already done
- const refreshed = yield UserPreferences.getBoolAsync(E2E_REFRESH_MESSAGES_KEY);
- if (!refreshed) {
- const serversDB = database.servers;
- const serversCollection = serversDB.collections.get('servers');
-
- const serverRecord = yield serversCollection.find(server);
-
- // We need to reset roomsUpdatedAt to request all rooms again
- // and save their respective E2EKeys to decrypt all pending messages and lastMessage
- // that are already inserted on local database by other app version
- yield serversDB.action(async() => {
- await serverRecord.update((s) => {
- s.roomsUpdatedAt = null;
- });
- });
-
- // Set the flag to indicate that already refreshed
- yield UserPreferences.setBoolAsync(E2E_REFRESH_MESSAGES_KEY, true);
- }
- } catch (e) {
- log(e);
- }
-
+const fetchRooms = function* fetchRooms() {
yield put(roomsRequest());
};
@@ -157,7 +131,7 @@ const handleLoginSuccess = function* handleLoginSuccess({ user }) {
RocketChat.getUserPresence(user.id);
const server = yield select(getServer);
- yield fork(fetchRooms, { server });
+ yield fork(fetchRooms);
yield fork(fetchPermissions);
yield fork(fetchCustomEmojis);
yield fork(fetchRoles);
diff --git a/app/sagas/rooms.js b/app/sagas/rooms.js
index 3f240f729..94670ea1f 100644
--- a/app/sagas/rooms.js
+++ b/app/sagas/rooms.js
@@ -61,10 +61,16 @@ const handleRoomsRequest = function* handleRoomsRequest({ params }) {
const subsToCreate = subscriptions.filter(i1 => !existingSubs.find(i2 => i1._id === i2._id));
const subsToDelete = existingSubs.filter(i1 => !subscriptions.find(i2 => i1._id === i2._id));
+ const openedRooms = yield select(state => state.room.rooms);
const lastMessages = subscriptions
+ /** Checks for opened rooms and filter them out.
+ * It prevents this process to try persisting the same last message on the room messages fetch.
+ * This race condition is easy to reproduce on push notification tap.
+ */
+ .filter(sub => !openedRooms.includes(sub.rid))
.map(sub => sub.lastMessage && buildMessage(sub.lastMessage))
.filter(lm => lm);
- const lastMessagesIds = lastMessages.map(lm => lm._id);
+ const lastMessagesIds = lastMessages.map(lm => lm._id).filter(lm => lm);
const existingMessages = yield messagesCollection.query(Q.where('id', Q.oneOf(lastMessagesIds))).fetch();
const messagesToUpdate = existingMessages.filter(i1 => lastMessages.find(i2 => i1.id === i2._id));
const messagesToCreate = lastMessages.filter(i1 => !existingMessages.find(i2 => i1._id === i2.id));
diff --git a/app/static/images/logo.png b/app/static/images/logo.png
index 4ea18f530..d03b30baa 100644
Binary files a/app/static/images/logo.png and b/app/static/images/logo.png differ
diff --git a/app/utils/log/events.js b/app/utils/log/events.js
index 52ef73838..4895ad8f2 100644
--- a/app/utils/log/events.js
+++ b/app/utils/log/events.js
@@ -112,6 +112,7 @@ export default {
CREATE_DISCUSSION_CREATE_F: 'create_discussion_create_f',
CREATE_DISCUSSION_SELECT_CHANNEL: 'create_discussion_select_channel',
CREATE_DISCUSSION_SELECT_USERS: 'create_discussion_select_users',
+ CREATE_DISCUSSION_TOGGLE_ENCRY: 'create_discussion_toggle_encry',
// PROFILE VIEW
PROFILE_PICK_AVATAR: 'profile_pick_avatar',
diff --git a/app/utils/touch.js b/app/utils/touch.js
index 505e6a02a..d5c22d4df 100644
--- a/app/utils/touch.js
+++ b/app/utils/touch.js
@@ -15,7 +15,7 @@ class Touch extends React.Component {
render() {
const {
- children, onPress, theme, ...props
+ children, onPress, theme, underlayColor, ...props
} = this.props;
return (
@@ -23,7 +23,7 @@ class Touch extends React.Component {
ref={this.getRef}
onPress={onPress}
activeOpacity={1}
- underlayColor={themes[theme].bannerBackground}
+ underlayColor={underlayColor || themes[theme].bannerBackground}
rippleColor={themes[theme].bannerBackground}
{...props}
>
@@ -36,7 +36,8 @@ class Touch extends React.Component {
Touch.propTypes = {
children: PropTypes.node,
onPress: PropTypes.func,
- theme: PropTypes.string
+ theme: PropTypes.string,
+ underlayColor: PropTypes.string
};
export default Touch;
diff --git a/app/views/CreateChannelView.js b/app/views/CreateChannelView.js
index 9afbc4e59..70982094d 100644
--- a/app/views/CreateChannelView.js
+++ b/app/views/CreateChannelView.js
@@ -85,7 +85,7 @@ class CreateChannelView extends React.Component {
error: PropTypes.object,
failure: PropTypes.bool,
isFetching: PropTypes.bool,
- e2eEnabled: PropTypes.bool,
+ encryptionEnabled: PropTypes.bool,
users: PropTypes.array.isRequired,
user: PropTypes.shape({
id: PropTypes.string,
@@ -107,7 +107,7 @@ class CreateChannelView extends React.Component {
channelName, type, readOnly, broadcast, encrypted
} = this.state;
const {
- users, isFetching, e2eEnabled, theme
+ users, isFetching, encryptionEnabled, theme
} = this.props;
if (nextProps.theme !== theme) {
return true;
@@ -130,7 +130,7 @@ class CreateChannelView extends React.Component {
if (nextProps.isFetching !== isFetching) {
return true;
}
- if (nextProps.e2eEnabled !== e2eEnabled) {
+ if (nextProps.encryptionEnabled !== encryptionEnabled) {
return true;
}
if (!equal(nextProps.users, users)) {
@@ -230,9 +230,9 @@ class CreateChannelView extends React.Component {
renderEncrypted() {
const { type, encrypted } = this.state;
- const { e2eEnabled } = this.props;
+ const { encryptionEnabled } = this.props;
- if (!e2eEnabled) {
+ if (!encryptionEnabled) {
return null;
}
@@ -366,7 +366,7 @@ class CreateChannelView extends React.Component {
const mapStateToProps = state => ({
baseUrl: state.server.server,
isFetching: state.createChannel.isFetching,
- e2eEnabled: state.settings.E2E_Enable,
+ encryptionEnabled: state.encryption.enabled,
users: state.selectedUsers.users,
user: getUserSelector(state)
});
diff --git a/app/views/CreateDiscussionView/SelectChannel.js b/app/views/CreateDiscussionView/SelectChannel.js
index fff60982d..c7b6acbfb 100644
--- a/app/views/CreateDiscussionView/SelectChannel.js
+++ b/app/views/CreateDiscussionView/SelectChannel.js
@@ -18,7 +18,7 @@ const SelectChannel = ({
const getChannels = debounce(async(keyword = '') => {
try {
- const res = await RocketChat.search({ text: keyword, filterUsers: false });
+ const res = await RocketChat.localSearch({ text: keyword });
setChannels(res);
} catch {
// do nothing
@@ -47,7 +47,7 @@ const SelectChannel = ({
value={initial && [initial]}
disabled={initial}
options={channels.map(channel => ({
- value: channel.rid,
+ value: channel,
text: { text: RocketChat.getRoomTitle(channel) },
imageUrl: getAvatar(channel)
}))}
diff --git a/app/views/CreateDiscussionView/index.js b/app/views/CreateDiscussionView/index.js
index afadb5e6b..43329f102 100644
--- a/app/views/CreateDiscussionView/index.js
+++ b/app/views/CreateDiscussionView/index.js
@@ -1,7 +1,7 @@
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
-import { ScrollView, Text } from 'react-native';
+import { ScrollView, Text, Switch } from 'react-native';
import isEqual from 'lodash/isEqual';
import Loading from '../../containers/Loading';
@@ -10,7 +10,7 @@ import scrollPersistTaps from '../../utils/scrollPersistTaps';
import I18n from '../../i18n';
import * as HeaderButton from '../../containers/HeaderButton';
import StatusBar from '../../containers/StatusBar';
-import { themes } from '../../constants/colors';
+import { SWITCH_TRACK_COLOR, themes } from '../../constants/colors';
import { withTheme } from '../../theme';
import { getUserSelector } from '../../selectors/login';
import TextInput from '../../containers/TextInput';
@@ -26,6 +26,7 @@ import styles from './styles';
import SafeAreaView from '../../containers/SafeAreaView';
import { goRoom } from '../../utils/goRoom';
import { logEvent, events } from '../../utils/log';
+import { E2E_ROOM_TYPES } from '../../lib/encryption/constants';
class CreateChannelView extends React.Component {
propTypes = {
@@ -41,7 +42,8 @@ class CreateChannelView extends React.Component {
theme: PropTypes.string,
isMasterDetail: PropTypes.bool,
blockUnauthenticatedAccess: PropTypes.bool,
- serverVersion: PropTypes.string
+ serverVersion: PropTypes.string,
+ encryptionEnabled: PropTypes.bool
}
constructor(props) {
@@ -54,7 +56,8 @@ class CreateChannelView extends React.Component {
message,
name: message?.msg || '',
users: [],
- reply: ''
+ reply: '',
+ encrypted: props.encryptionEnabled
};
this.setHeader();
}
@@ -109,13 +112,13 @@ class CreateChannelView extends React.Component {
submit = () => {
const {
- name: t_name, channel: { prid, rid }, message: { id: pmid }, reply, users
+ name: t_name, channel: { prid, rid }, message: { id: pmid }, reply, users, encrypted
} = this.state;
const { create } = this.props;
// create discussion
create({
- prid: prid || rid, pmid, t_name, reply, users
+ prid: prid || rid, pmid, t_name, reply, users, encrypted
});
};
@@ -134,7 +137,7 @@ class CreateChannelView extends React.Component {
selectChannel = ({ value }) => {
logEvent(events.CREATE_DISCUSSION_SELECT_CHANNEL);
- this.setState({ channel: { rid: value } });
+ this.setState({ channel: value, encrypted: value?.encrypted });
}
selectUsers = ({ value }) => {
@@ -142,10 +145,17 @@ class CreateChannelView extends React.Component {
this.setState({ users: value });
}
+ onEncryptedChange = (value) => {
+ logEvent(events.CREATE_DISCUSSION_TOGGLE_ENCRY);
+ this.setState({ encrypted: value });
+ }
+
render() {
- const { name, users } = this.state;
const {
- server, user, loading, blockUnauthenticatedAccess, theme, serverVersion
+ name, users, encrypted, channel
+ } = this.state;
+ const {
+ server, user, loading, blockUnauthenticatedAccess, theme, serverVersion, encryptionEnabled
} = this.props;
return (
- this.setState({ reply: text })}
- />
+ {encryptionEnabled && E2E_ROOM_TYPES[channel?.t]
+ ? (
+ <>
+ {I18n.t('Encrypted')}
+
+ >
+ ) : null}
@@ -211,7 +223,8 @@ const mapStateToProps = state => ({
result: state.createDiscussion.result,
blockUnauthenticatedAccess: state.settings.Accounts_AvatarBlockUnauthenticatedAccess ?? true,
serverVersion: state.share.server.version || state.server.version,
- isMasterDetail: state.app.isMasterDetail
+ isMasterDetail: state.app.isMasterDetail,
+ encryptionEnabled: state.encryption.enabled
});
const mapDispatchToProps = dispatch => ({
diff --git a/app/views/E2EEncryptionSecurityView.js b/app/views/E2EEncryptionSecurityView.js
index 8d246b5a0..661d6e84d 100644
--- a/app/views/E2EEncryptionSecurityView.js
+++ b/app/views/E2EEncryptionSecurityView.js
@@ -80,12 +80,18 @@ class E2EEncryptionSecurityView extends React.Component {
title: I18n.t('Are_you_sure_question_mark'),
message: I18n.t('E2E_encryption_reset_message'),
confirmationText: I18n.t('E2E_encryption_reset_confirmation'),
- onPress: () => {
+ onPress: async() => {
logEvent(events.E2E_SEC_RESET_OWN_KEY);
try {
- RocketChat.e2eResetOwnKey();
- const { logout } = this.props;
- logout();
+ const res = await RocketChat.e2eResetOwnKey();
+ /**
+ * It might return an empty object when TOTP is enabled,
+ * that's why we're using strict equality to boolean
+ */
+ if (res === true) {
+ const { logout } = this.props;
+ logout();
+ }
} catch (e) {
log(e);
showErrorAlert(I18n.t('E2E_encryption_reset_error'));
@@ -96,9 +102,8 @@ class E2EEncryptionSecurityView extends React.Component {
renderChangePassword = () => {
const { newPassword } = this.state;
- const { theme } = this.props;
- const { hasPrivateKey } = Encryption;
- if (!hasPrivateKey) {
+ const { theme, encryptionEnabled } = this.props;
+ if (!encryptionEnabled) {
return null;
}
return (
@@ -161,7 +166,8 @@ class E2EEncryptionSecurityView extends React.Component {
const mapStateToProps = state => ({
server: state.server.server,
- user: getUserSelector(state)
+ user: getUserSelector(state),
+ encryptionEnabled: state.encryption.enabled
});
const mapDispatchToProps = dispatch => ({
@@ -179,6 +185,7 @@ E2EEncryptionSecurityView.propTypes = {
id: PropTypes.string
}),
server: PropTypes.string,
+ encryptionEnabled: PropTypes.bool,
logout: PropTypes.func
};
diff --git a/app/views/NewServerView/ServerInput/Item.js b/app/views/NewServerView/ServerInput/Item.js
index f9496332a..b249c88e0 100644
--- a/app/views/NewServerView/ServerInput/Item.js
+++ b/app/views/NewServerView/ServerInput/Item.js
@@ -34,8 +34,8 @@ const Item = ({
}) => (
onPress(item.url)} theme={theme} testID={`server-history-${ item.url }`}>
- {item.url}
- {item.username}
+ {item.url}
+ {item.username}
onDelete(item)} testID={`server-history-delete-${ item.url }`}>
diff --git a/app/views/NewServerView/ServerInput/index.js b/app/views/NewServerView/ServerInput/index.js
index 4f13f8894..6088e4ef6 100644
--- a/app/views/NewServerView/ServerInput/index.js
+++ b/app/views/NewServerView/ServerInput/index.js
@@ -6,6 +6,7 @@ import TextInput from '../../../containers/TextInput';
import * as List from '../../../containers/List';
import { themes } from '../../../constants/colors';
import Item from './Item';
+import I18n from '../../../i18n';
const styles = StyleSheet.create({
container: {
@@ -42,8 +43,8 @@ const ServerInput = ({
return (
{
- this.backHandler = BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
- });
- this.unsubscribeBlur = navigation.addListener('blur', () => {
- if (this.backHandler && this.backHandler.remove) {
- this.backHandler.remove();
- }
- });
- }
-
shouldComponentUpdate(nextProps) {
const { theme } = this.props;
if (theme !== nextProps.theme) {
@@ -54,21 +39,6 @@ class OnboardingView extends React.Component {
return false;
}
- componentWillUnmount() {
- if (this.unsubscribeFocus) {
- this.unsubscribeFocus();
- }
- if (this.unsubscribeBlur) {
- this.unsubscribeBlur();
- }
- }
-
- handleBackPress = () => {
- const { appStart } = this.props;
- appStart({ root: ROOT_BACKGROUND });
- return false;
- }
-
connectServer = () => {
logEvent(events.ONBOARD_JOIN_A_WORKSPACE);
const { navigation } = this.props;
@@ -89,7 +59,7 @@ class OnboardingView extends React.Component {
return (
-
+
{I18n.t('Onboarding_title')}
{I18n.t('Onboarding_subtitle')}
{I18n.t('Onboarding_description')}
@@ -116,8 +86,4 @@ class OnboardingView extends React.Component {
}
}
-const mapDispatchToProps = dispatch => ({
- appStart: params => dispatch(appStartAction(params))
-});
-
-export default connect(null, mapDispatchToProps)(withTheme(OnboardingView));
+export default withTheme(OnboardingView);
diff --git a/app/views/OnboardingView/styles.js b/app/views/OnboardingView/styles.js
index deca9b1bb..809fcf36e 100644
--- a/app/views/OnboardingView/styles.js
+++ b/app/views/OnboardingView/styles.js
@@ -11,8 +11,8 @@ export default StyleSheet.create({
marginBottom: verticalScale(50),
maxHeight: verticalScale(150),
resizeMode: 'contain',
- width: 80,
- height: 70
+ width: 100,
+ height: 100
},
title: {
...sharedStyles.textBold,
diff --git a/app/views/RoomActionsView/index.js b/app/views/RoomActionsView/index.js
index 9d8278bc5..a3e223336 100644
--- a/app/views/RoomActionsView/index.js
+++ b/app/views/RoomActionsView/index.js
@@ -5,6 +5,7 @@ import {
} from 'react-native';
import { connect } from 'react-redux';
import _ from 'lodash';
+import semver from 'semver';
import Touch from '../../utils/touch';
import { setLoading as setLoadingAction } from '../../actions/selectedUsers';
@@ -46,11 +47,12 @@ class RoomActionsView extends React.Component {
route: PropTypes.object,
leaveRoom: PropTypes.func,
jitsiEnabled: PropTypes.bool,
- e2eEnabled: PropTypes.bool,
+ encryptionEnabled: PropTypes.bool,
setLoadingInvite: PropTypes.func,
closeRoom: PropTypes.func,
theme: PropTypes.string,
- fontScale: PropTypes.number
+ fontScale: PropTypes.number,
+ serverVersion: PropTypes.string
}
constructor(props) {
@@ -71,7 +73,8 @@ class RoomActionsView extends React.Component {
canInviteUser: false,
canForwardGuest: false,
canReturnQueue: false,
- canEdit: false
+ canEdit: false,
+ canToggleEncryption: false
};
if (room && room.observe && room.rid) {
this.roomObservable = room.observe();
@@ -120,6 +123,7 @@ class RoomActionsView extends React.Component {
this.canAddUser();
this.canInviteUser();
this.canEdit();
+ this.canToggleEncryption();
// livechat permissions
if (room.t === 'l') {
@@ -152,7 +156,6 @@ class RoomActionsView extends React.Component {
}
}
- // eslint-disable-next-line react/sort-comp
canAddUser = async() => {
const { room, joined } = this.state;
const { rid, t } = room;
@@ -193,6 +196,15 @@ class RoomActionsView extends React.Component {
this.setState({ canEdit });
}
+ canToggleEncryption = async() => {
+ const { room } = this.state;
+ const { rid } = room;
+ const permissions = await RocketChat.hasPermission(['toggle-room-e2e-encryption'], rid);
+
+ const canToggleEncryption = permissions && permissions['toggle-room-e2e-encryption'];
+ this.setState({ canToggleEncryption });
+ }
+
canViewMembers = async() => {
const { room } = this.state;
const { rid, t, broadcast } = room;
@@ -235,13 +247,21 @@ class RoomActionsView extends React.Component {
}
renderEncryptedSwitch = () => {
- const { room } = this.state;
+ const { room, canToggleEncryption, canEdit } = this.state;
const { encrypted } = room;
+ const { serverVersion } = this.props;
+ let hasPermission = false;
+ if (serverVersion && semver.lt(semver.coerce(serverVersion), '3.11.0')) {
+ hasPermission = canEdit;
+ } else {
+ hasPermission = canToggleEncryption;
+ }
return (
);
}
@@ -480,15 +500,12 @@ class RoomActionsView extends React.Component {
}
renderE2EEncryption = () => {
- const {
- room, canEdit
- } = this.state;
- const { e2eEnabled } = this.props;
+ const { room } = this.state;
+ const { encryptionEnabled } = this.props;
- // If can edit this room
- // If this room type can be Encrypted
- // If e2e is enabled for this server
- if (canEdit && E2E_ROOM_TYPES[room?.t] && e2eEnabled) {
+ // If this room type can be encrypted
+ // If e2e is enabled
+ if (E2E_ROOM_TYPES[room?.t] && encryptionEnabled) {
return (
@@ -847,7 +864,8 @@ class RoomActionsView extends React.Component {
const mapStateToProps = state => ({
jitsiEnabled: state.settings.Jitsi_Enabled || false,
- e2eEnabled: state.settings.E2E_Enable || false
+ encryptionEnabled: state.encryption.enabled,
+ serverVersion: state.server.version
});
const mapDispatchToProps = dispatch => ({
diff --git a/app/views/RoomInfoEditView/index.js b/app/views/RoomInfoEditView/index.js
index b42c83ece..ff56a1399 100644
--- a/app/views/RoomInfoEditView/index.js
+++ b/app/views/RoomInfoEditView/index.js
@@ -60,7 +60,7 @@ class RoomInfoEditView extends React.Component {
route: PropTypes.object,
deleteRoom: PropTypes.func,
serverVersion: PropTypes.string,
- e2eEnabled: PropTypes.bool,
+ encryptionEnabled: PropTypes.bool,
theme: PropTypes.string
};
@@ -414,7 +414,7 @@ class RoomInfoEditView extends React.Component {
const {
name, nameError, description, topic, announcement, t, ro, reactWhenReadOnly, room, joinCode, saving, permissions, archived, enableSysMes, encrypted, avatar
} = this.state;
- const { serverVersion, e2eEnabled, theme } = this.props;
+ const { serverVersion, encryptionEnabled, theme } = this.props;
const { dangerColor } = themes[theme];
return (
@@ -561,7 +561,7 @@ class RoomInfoEditView extends React.Component {
{this.renderSystemMessages()}
) : null}
- {e2eEnabled ? (
+ {encryptionEnabled ? (
({
serverVersion: state.share.server.version || state.server.version,
- e2eEnabled: state.settings.E2E_Enable || false
+ encryptionEnabled: state.encryption.enabled
});
const mapDispatchToProps = dispatch => ({
diff --git a/app/views/RoomsListView/ListHeader/Encryption.js b/app/views/RoomsListView/ListHeader/Encryption.js
deleted file mode 100644
index b666f487f..000000000
--- a/app/views/RoomsListView/ListHeader/Encryption.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import React from 'react';
-import { Text } from 'react-native';
-import PropTypes from 'prop-types';
-import { BorderlessButton } from 'react-native-gesture-handler';
-
-import { withTheme } from '../../../theme';
-import { CustomIcon } from '../../../lib/Icons';
-import { themes } from '../../../constants/colors';
-import I18n from '../../../i18n';
-import styles from '../styles';
-import { E2E_BANNER_TYPE } from '../../../lib/encryption/constants';
-
-const Encryption = React.memo(({
- searching,
- goEncryption,
- encryptionBanner,
- theme
-}) => {
- if (searching > 0 || !encryptionBanner) {
- return null;
- }
-
- let text = I18n.t('Save_Your_Encryption_Password');
- if (encryptionBanner === E2E_BANNER_TYPE.REQUEST_PASSWORD) {
- text = I18n.t('Enter_Your_E2E_Password');
- }
-
- return (
-
-
- {text}
-
- );
-});
-
-Encryption.propTypes = {
- searching: PropTypes.bool,
- goEncryption: PropTypes.func,
- encryptionBanner: PropTypes.string,
- theme: PropTypes.string
-};
-
-export default withTheme(Encryption);
diff --git a/app/views/RoomsListView/ListHeader/Sort.js b/app/views/RoomsListView/ListHeader/Sort.js
deleted file mode 100644
index 972b29801..000000000
--- a/app/views/RoomsListView/ListHeader/Sort.js
+++ /dev/null
@@ -1,45 +0,0 @@
-import React from 'react';
-import { View, Text, StyleSheet } from 'react-native';
-import PropTypes from 'prop-types';
-
-import Touch from '../../../utils/touch';
-import { CustomIcon } from '../../../lib/Icons';
-import I18n from '../../../i18n';
-import styles from '../styles';
-import { themes } from '../../../constants/colors';
-import { withTheme } from '../../../theme';
-
-
-const Sort = React.memo(({
- searching, sortBy, toggleSort, theme
-}) => {
- if (searching > 0) {
- return null;
- }
- return (
-
-
-
- {I18n.t('Sorting_by', { key: I18n.t(sortBy === 'alphabetical' ? 'name' : 'activity') })}
-
-
- );
-});
-
-Sort.propTypes = {
- searching: PropTypes.bool,
- sortBy: PropTypes.string,
- theme: PropTypes.string,
- toggleSort: PropTypes.func
-};
-
-export default withTheme(Sort);
diff --git a/app/views/RoomsListView/ListHeader/index.js b/app/views/RoomsListView/ListHeader/index.js
index aa4372732..85ffcfa9c 100644
--- a/app/views/RoomsListView/ListHeader/index.js
+++ b/app/views/RoomsListView/ListHeader/index.js
@@ -1,8 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';
-import Sort from './Sort';
-import Encryption from './Encryption';
+import { withTheme } from '../../../theme';
+import I18n from '../../../i18n';
+import * as List from '../../../containers/List';
+import { E2E_BANNER_TYPE } from '../../../lib/encryption/constants';
+import { themes } from '../../../constants/colors';
import OmnichannelStatus from '../../../ee/omnichannel/containers/OmnichannelStatus';
@@ -15,14 +18,55 @@ const ListHeader = React.memo(({
queueSize,
inquiryEnabled,
encryptionBanner,
- user
-}) => (
- <>
-
-
-
- >
-));
+ user,
+ theme
+}) => {
+ const sortTitle = I18n.t('Sorting_by', { key: I18n.t(sortBy === 'alphabetical' ? 'name' : 'activity') });
+
+ if (searching) {
+ return null;
+ }
+
+ return (
+ <>
+ {encryptionBanner
+ ? (
+ <>
+ }
+ underlayColor={themes[theme].tintActive}
+ backgroundColor={themes[theme].actionTintColor}
+ color={themes[theme].buttonText}
+ onPress={goEncryption}
+ testID='listheader-encryption'
+ />
+
+ >
+ )
+ : null}
+ }
+ color={themes[theme].auxiliaryText}
+ onPress={toggleSort}
+ translateTitle={false}
+ />
+
+
+ >
+ );
+});
ListHeader.propTypes = {
searching: PropTypes.bool,
@@ -33,7 +77,8 @@ ListHeader.propTypes = {
queueSize: PropTypes.number,
inquiryEnabled: PropTypes.bool,
encryptionBanner: PropTypes.string,
- user: PropTypes.object
+ user: PropTypes.object,
+ theme: PropTypes.string
};
-export default ListHeader;
+export default withTheme(ListHeader);
diff --git a/app/views/RoomsListView/ServerDropdown.js b/app/views/RoomsListView/ServerDropdown.js
index 0f49cef82..5081f4504 100644
--- a/app/views/RoomsListView/ServerDropdown.js
+++ b/app/views/RoomsListView/ServerDropdown.js
@@ -1,6 +1,6 @@
import React, { Component } from 'react';
import {
- View, Text, Animated, Easing, TouchableWithoutFeedback, TouchableOpacity, FlatList, Image, Pressable
+ View, Text, Animated, Easing, TouchableWithoutFeedback, TouchableOpacity, FlatList
} from 'react-native';
import PropTypes from 'prop-types';
import { connect, batch } from 'react-redux';
@@ -14,12 +14,12 @@ import styles from './styles';
import RocketChat from '../../lib/rocketchat';
import I18n from '../../i18n';
import EventEmitter from '../../utils/events';
-import Check from '../../containers/Check';
+import ServerItem from '../../presentation/ServerItem';
import database from '../../lib/database';
import { themes } from '../../constants/colors';
import { withTheme } from '../../theme';
import { KEY_COMMAND, handleCommandSelectServer } from '../../commands';
-import { isTablet, isIOS } from '../../utils/deviceInfo';
+import { isTablet } from '../../utils/deviceInfo';
import { localAuthenticate } from '../../utils/localAuthentication';
import { showConfirmationAlert } from '../../utils/info';
import { logEvent, events } from '../../utils/log';
@@ -143,7 +143,7 @@ class ServerDropdown extends Component {
}, ANIMATION_DURATION);
}
- select = async(server) => {
+ select = async(server, version) => {
const {
server: currentServer, selectServerRequest, isMasterDetail
} = this.props;
@@ -163,7 +163,7 @@ class ServerDropdown extends Component {
}, ANIMATION_DURATION);
} else {
await localAuthenticate(server);
- selectServerRequest(server);
+ selectServerRequest(server, version);
}
}
}
@@ -202,43 +202,13 @@ class ServerDropdown extends Component {
const { server, theme } = this.props;
return (
- this.select(item.id)}
+ this.select(item.id, item.version)}
onLongPress={() => (item.id === server || this.remove(item.id))}
- testID={`rooms-list-header-server-${ item.id }`}
- android_ripple={{
- color: themes[theme].bannerBackground
- }}
- style={({ pressed }) => ({
- backgroundColor: isIOS && pressed
- ? themes[theme].bannerBackground
- : 'transparent'
- })}
- >
-
- {item.iconURL
- ? (
- console.warn('error loading serverIcon')}
- />
- )
- : (
-
- )
- }
-
- {item.name || item.id}
- {item.id}
-
- {item.id === server ? : null}
-
-
+ hasCheck={item.id === server}
+ theme={theme}
+ />
);
}
@@ -313,7 +283,7 @@ const mapStateToProps = state => ({
const mapDispatchToProps = dispatch => ({
toggleServerDropdown: () => dispatch(toggleServerDropdownAction()),
- selectServerRequest: server => dispatch(selectServerRequestAction(server)),
+ selectServerRequest: (server, version) => dispatch(selectServerRequestAction(server, version, true, true)),
appStart: params => dispatch(appStartAction(params)),
initAdd: previousServer => dispatch(serverInitAddAction(previousServer))
});
diff --git a/app/views/RoomsListView/SortDropdown/Item.js b/app/views/RoomsListView/SortDropdown/Item.js
deleted file mode 100644
index b842b3f72..000000000
--- a/app/views/RoomsListView/SortDropdown/Item.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import React from 'react';
-import { View, Text, Image } from 'react-native';
-import PropTypes from 'prop-types';
-
-import styles from '../styles';
-import Touch from '../../../utils/touch';
-import I18n from '../../../i18n';
-import { CustomIcon } from '../../../lib/Icons';
-import Check from '../../../containers/Check';
-import { themes } from '../../../constants/colors';
-
-
-export const SortItemButton = ({ children, onPress, theme }) => (
-
- {children}
-
-);
-
-SortItemButton.propTypes = {
- theme: PropTypes.string,
- children: PropTypes.node,
- onPress: PropTypes.func
-};
-
-export const SortItemContent = ({
- label, icon, imageUri, checked, theme
-}) => (
-
- {icon && }
- {imageUri && }
- {I18n.t(label)}
- {checked ? : null}
-
-);
-
-SortItemContent.propTypes = {
- theme: PropTypes.string,
- label: PropTypes.string,
- icon: PropTypes.string,
- imageUri: PropTypes.string,
- checked: PropTypes.bool
-};
diff --git a/app/views/RoomsListView/SortDropdown/index.js b/app/views/RoomsListView/SortDropdown/index.js
index 536f5738c..cb85edcd5 100644
--- a/app/views/RoomsListView/SortDropdown/index.js
+++ b/app/views/RoomsListView/SortDropdown/index.js
@@ -1,21 +1,19 @@
import React, { PureComponent } from 'react';
import {
- View, Text, Animated, Easing, TouchableWithoutFeedback
+ Animated, Easing, TouchableWithoutFeedback
} from 'react-native';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { withSafeAreaInsets } from 'react-native-safe-area-context';
import styles from '../styles';
-import Touch from '../../../utils/touch';
+import * as List from '../../../containers/List';
import RocketChat from '../../../lib/rocketchat';
import { setPreference } from '../../../actions/sortPreferences';
import log, { logEvent, events } from '../../../utils/log';
import I18n from '../../../i18n';
-import { CustomIcon } from '../../../lib/Icons';
import { withTheme } from '../../../theme';
import { themes } from '../../../constants/colors';
-import { SortItemButton, SortItemContent } from './Item';
import { headerHeight } from '../../../containers/Header';
const ANIMATION_DURATION = 200;
@@ -113,6 +111,11 @@ class Sort extends PureComponent {
).start(() => close());
}
+ renderCheck = () => {
+ const { theme } = this.props;
+ return ;
+ }
+
render() {
const { isMasterDetail, insets } = this.props;
const statusBarHeight = insets?.top ?? 0;
@@ -150,58 +153,50 @@ class Sort extends PureComponent {
}
]}
>
- }
+ color={themes[theme].auxiliaryText}
onPress={this.close}
- theme={theme}
- >
-
-
-
- {I18n.t('Sorting_by', { key: I18n.t(sortBy === 'alphabetical' ? 'name' : 'activity') })}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ translateTitle={false}
+ />
+
+ }
+ color={themes[theme].auxiliaryText}
+ onPress={this.sortByName}
+ right={() => (sortBy === 'alphabetical' ? this.renderCheck() : null)}
+ />
+ }
+ color={themes[theme].auxiliaryText}
+ onPress={this.sortByActivity}
+ right={() => (sortBy === 'activity' ? this.renderCheck() : null)}
+ />
+
+ }
+ color={themes[theme].auxiliaryText}
+ onPress={this.toggleGroupByType}
+ right={() => (groupByType ? this.renderCheck() : null)}
+ />
+ }
+ color={themes[theme].auxiliaryText}
+ onPress={this.toggleGroupByFavorites}
+ right={() => (showFavorites ? this.renderCheck() : null)}
+ />
+ }
+ color={themes[theme].auxiliaryText}
+ onPress={this.toggleUnread}
+ right={() => (showUnread ? this.renderCheck() : null)}
+ />
>
);
diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js
index ed4226872..6ec23f29c 100644
--- a/app/views/RoomsListView/index.js
+++ b/app/views/RoomsListView/index.js
@@ -29,7 +29,6 @@ import {
roomsRequest as roomsRequestAction,
closeServerDropdown as closeServerDropdownAction
} from '../../actions/rooms';
-import { appStart as appStartAction, ROOT_BACKGROUND } from '../../actions/app';
import debounce from '../../utils/debounce';
import { isIOS, isTablet } from '../../utils/deviceInfo';
import RoomsListHeaderView from './Header';
@@ -117,6 +116,7 @@ class RoomsListView extends React.Component {
}),
server: PropTypes.string,
searchText: PropTypes.string,
+ changingServer: PropTypes.bool,
loadingServer: PropTypes.bool,
showServerDropdown: PropTypes.bool,
showSortDropdown: PropTypes.bool,
@@ -150,8 +150,8 @@ class RoomsListView extends React.Component {
console.time(`${ this.constructor.name } init`);
console.time(`${ this.constructor.name } mount`);
- this.gotSubscriptions = false;
this.animated = false;
+ this.mounted = false;
this.count = 0;
this.state = {
searching: false,
@@ -162,24 +162,14 @@ class RoomsListView extends React.Component {
item: {}
};
this.setHeader();
+ this.getSubscriptions();
}
componentDidMount() {
const {
- navigation, closeServerDropdown, appState
+ navigation, closeServerDropdown
} = this.props;
-
- /**
- * - When didMount is triggered and appState is foreground,
- * it means the user is logging in and selectServer has ran, so we can getSubscriptions
- *
- * - When didMount is triggered and appState is background,
- * it means the user has resumed the app, so selectServer needs to be triggered,
- * which is going to change server and getSubscriptions will be triggered by componentWillReceiveProps
- */
- if (appState === 'foreground') {
- this.getSubscriptions();
- }
+ this.mounted = true;
if (isTablet) {
EventEmitter.addEventListener(KEY_COMMAND, this.handleCommands);
@@ -206,17 +196,17 @@ class RoomsListView extends React.Component {
}
UNSAFE_componentWillReceiveProps(nextProps) {
- const { loadingServer, searchText, server } = this.props;
+ const {
+ loadingServer, searchText, server, changingServer
+ } = this.props;
- if (nextProps.server && loadingServer !== nextProps.loadingServer) {
- if (nextProps.loadingServer) {
- this.setState({ loading: true });
- } else {
- this.getSubscriptions();
- }
+ // when the server is changed
+ if (server !== nextProps.server && loadingServer !== nextProps.loadingServer && nextProps.loadingServer) {
+ this.setState({ loading: true });
}
- if (server && server !== nextProps.server) {
- this.gotSubscriptions = false;
+ // when the server is changing and stopped loading
+ if (changingServer && loadingServer !== nextProps.loadingServer && !nextProps.loadingServer) {
+ this.getSubscriptions();
}
if (searchText !== nextProps.searchText) {
this.search(nextProps.searchText);
@@ -508,11 +498,17 @@ class RoomsListView extends React.Component {
tempChats = chats;
}
- this.internalSetState({
- chats: tempChats,
- chatsUpdate,
- loading: false
- });
+ if (this.mounted) {
+ this.internalSetState({
+ chats: tempChats,
+ chatsUpdate,
+ loading: false
+ });
+ } else {
+ this.state.chats = tempChats;
+ this.state.chatsUpdate = chatsUpdate;
+ this.state.loading = false;
+ }
});
}
@@ -552,12 +548,10 @@ class RoomsListView extends React.Component {
handleBackPress = () => {
const { searching } = this.state;
- const { appStart } = this.props;
if (searching) {
this.cancelSearch();
return true;
}
- appStart({ root: ROOT_BACKGROUND });
return false;
};
@@ -1023,6 +1017,7 @@ const mapStateToProps = state => ({
user: getUserSelector(state),
isMasterDetail: state.app.isMasterDetail,
server: state.server.server,
+ changingServer: state.server.changingServer,
connected: state.server.connected,
searchText: state.rooms.searchText,
loadingServer: state.server.loading,
@@ -1046,7 +1041,6 @@ const mapDispatchToProps = dispatch => ({
toggleSortDropdown: () => dispatch(toggleSortDropdownAction()),
openSearchHeader: () => dispatch(openSearchHeaderAction()),
closeSearchHeader: () => dispatch(closeSearchHeaderAction()),
- appStart: params => dispatch(appStartAction(params)),
roomsRequest: params => dispatch(roomsRequestAction(params)),
selectServerRequest: server => dispatch(selectServerRequestAction(server)),
closeServerDropdown: () => dispatch(closeServerDropdownAction())
diff --git a/app/views/RoomsListView/styles.js b/app/views/RoomsListView/styles.js
index a921830a6..beb04c92f 100644
--- a/app/views/RoomsListView/styles.js
+++ b/app/views/RoomsListView/styles.js
@@ -15,56 +15,22 @@ export default StyleSheet.create({
alignItems: 'center',
flexDirection: 'row'
},
- sortToggleContainerClose: {
- position: 'absolute',
- top: 0,
- width: '100%'
- },
- sortToggleText: {
- fontSize: 16,
- flex: 1,
- ...sharedStyles.textRegular
- },
- queueToggleText: {
- fontSize: 16,
- flex: 1,
- ...sharedStyles.textRegular
- },
dropdownContainer: {
width: '100%',
position: 'absolute',
top: 0,
borderBottomWidth: StyleSheet.hairlineWidth
},
- sortItemButton: {
- height: 57,
- justifyContent: 'center'
- },
- sortItemContainer: {
- flexDirection: 'row',
- alignItems: 'center'
- },
- sortItemText: {
- fontSize: 18,
- flex: 1,
- ...sharedStyles.textRegular
- },
backdrop: {
...StyleSheet.absoluteFill
},
- sortSeparator: {
- height: StyleSheet.hairlineWidth,
- marginHorizontal: 12,
- flex: 1
- },
- sortIcon: {
- width: 22,
- height: 22,
- marginHorizontal: 12
- },
queueIcon: {
marginHorizontal: 12
},
+ omnichannelRightContainer: {
+ flexDirection: 'row',
+ alignItems: 'center'
+ },
groupTitleContainer: {
paddingHorizontal: 12,
paddingTop: 17,
@@ -90,56 +56,5 @@ export default StyleSheet.create({
marginRight: 12,
paddingVertical: 10,
...sharedStyles.textRegular
- },
- serverItem: {
- height: 68
- },
- serverItemContainer: {
- flexDirection: 'row',
- alignItems: 'center',
- height: 68
- },
- serverIcon: {
- width: 42,
- height: 42,
- marginHorizontal: 12,
- marginVertical: 13,
- borderRadius: 4,
- resizeMode: 'contain'
- },
- serverTextContainer: {
- flex: 1,
- flexDirection: 'column',
- justifyContent: 'center'
- },
- serverName: {
- fontSize: 18,
- ...sharedStyles.textSemibold
- },
- serverUrl: {
- fontSize: 16,
- ...sharedStyles.textRegular
- },
- serverSeparator: {
- height: StyleSheet.hairlineWidth,
- marginLeft: 72
- },
- encryptionButton: {
- width: '100%',
- flexDirection: 'row',
- alignItems: 'center',
- padding: 12
- },
- encryptionIcon: {
- ...sharedStyles.textMedium
- },
- encryptionText: {
- flex: 1,
- fontSize: 16,
- marginHorizontal: 16,
- ...sharedStyles.textMedium
- },
- omnichannelToggle: {
- marginRight: 12
}
});
diff --git a/app/views/SelectServerView.js b/app/views/SelectServerView.js
index f956feba5..72a6b939f 100644
--- a/app/views/SelectServerView.js
+++ b/app/views/SelectServerView.js
@@ -65,10 +65,9 @@ class SelectServerView extends React.Component {
const { server, theme } = this.props;
return (
this.select(item.id)}
item={item}
- hasCheck
+ hasCheck={item.id === server}
theme={theme}
/>
);
diff --git a/app/views/ShareListView/index.js b/app/views/ShareListView/index.js
index f2b1d25ca..8d5f7b0eb 100644
--- a/app/views/ShareListView/index.js
+++ b/app/views/ShareListView/index.js
@@ -195,7 +195,7 @@ class ShareListView extends React.Component {
Q.where('archived', false),
Q.where('open', true),
Q.experimentalSkip(0),
- Q.experimentalTake(50),
+ Q.experimentalTake(20),
Q.experimentalSortBy('room_updated_at', Q.desc)
];
if (text) {
@@ -451,11 +451,9 @@ class ShareListView extends React.Component {
ListFooterComponent={!searching && this.renderBorderBottom}
ListHeaderComponentStyle={!searching ? { ...styles.borderBottom, borderColor: themes[theme].separatorColor } : {}}
ListEmptyComponent={searching && searchText ? this.renderEmptyComponent : null}
- enableEmptySections
removeClippedSubviews
keyboardShouldPersistTaps='always'
initialNumToRender={12}
- windowSize={20}
/>
);
}
diff --git a/app/views/ThreadMessagesView/Item.js b/app/views/ThreadMessagesView/Item.js
index 168ff93c2..89ae95cc6 100644
--- a/app/views/ThreadMessagesView/Item.js
+++ b/app/views/ThreadMessagesView/Item.js
@@ -1,15 +1,15 @@
import React from 'react';
import PropTypes from 'prop-types';
import { View, Text, StyleSheet } from 'react-native';
+import Touchable from 'react-native-platform-touchable';
import { withTheme } from '../../theme';
import Avatar from '../../containers/Avatar';
-import Touch from '../../utils/touch';
import sharedStyles from '../Styles';
import { themes } from '../../constants/colors';
import Markdown from '../../containers/markdown';
-import { CustomIcon } from '../../lib/Icons';
import { formatDateThreads, makeThreadName } from '../../utils/room';
+import ThreadDetails from '../../containers/ThreadDetails';
const styles = StyleSheet.create({
container: {
@@ -38,34 +38,26 @@ const styles = StyleSheet.create({
avatar: {
marginRight: 8
},
- detailsContainer: {
- marginTop: 8,
- flexDirection: 'row'
- },
- detailContainer: {
- marginRight: 8,
- flexDirection: 'row',
- alignItems: 'center',
- justifyContent: 'center'
- },
- detailText: {
- fontSize: 10,
- marginLeft: 2,
- ...sharedStyles.textSemibold
- },
- badgeContainer: {
- marginLeft: 8,
- justifyContent: 'center'
+ threadDetails: {
+ marginTop: 8
},
badge: {
- width: 12,
- height: 12,
- borderRadius: 6
+ width: 8,
+ height: 8,
+ borderRadius: 4,
+ marginHorizontal: 8,
+ alignSelf: 'center'
+ },
+ messageContainer: {
+ flexDirection: 'row'
+ },
+ markdown: {
+ flex: 1
}
});
const Item = ({
- item, baseUrl, theme, useRealName, user, badgeColor, onPress
+ item, baseUrl, theme, useRealName, user, badgeColor, onPress, toggleFollowThread
}) => {
const username = (useRealName && item?.u?.name) || item?.u?.username;
let time;
@@ -73,13 +65,8 @@ const Item = ({
time = formatDateThreads(item.ts);
}
- let tlm;
- if (item?.tlm) {
- tlm = formatDateThreads(item.tlm);
- }
-
return (
- onPress(item)} testID={`thread-messages-view-${ item.msg }`} style={{ backgroundColor: themes[theme].backgroundColor }}>
+ onPress(item)} testID={`thread-messages-view-${ item.msg }`} style={{ backgroundColor: themes[theme].backgroundColor }}>
{username}
{time}
-
-
-
-
- {item?.tcount}
-
-
-
-
- {item?.replies?.length}
-
-
-
-
- {tlm}
-
+
+
+ {badgeColor ? : null }
+
- {badgeColor
- ? (
-
-
-
- )
- : null}
-
+
);
};
@@ -133,7 +106,8 @@ Item.propTypes = {
useRealName: PropTypes.bool,
user: PropTypes.object,
badgeColor: PropTypes.string,
- onPress: PropTypes.func
+ onPress: PropTypes.func,
+ toggleFollowThread: PropTypes.func
};
export default withTheme(Item);
diff --git a/app/views/ThreadMessagesView/index.js b/app/views/ThreadMessagesView/index.js
index ba824605c..d75328a99 100644
--- a/app/views/ThreadMessagesView/index.js
+++ b/app/views/ThreadMessagesView/index.js
@@ -33,6 +33,8 @@ import { isIOS } from '../../utils/deviceInfo';
import { getBadgeColor, makeThreadName } from '../../utils/room';
import { getHeaderTitlePosition } from '../../containers/Header';
import SearchHeader from './SearchHeader';
+import EventEmitter from '../../utils/events';
+import { LISTENER } from '../../containers/Toast';
const API_FETCH_COUNT = 50;
@@ -410,6 +412,15 @@ class ThreadMessagesView extends React.Component {
this.setState({ currentFilter: filter, displayingThreads });
}
+ toggleFollowThread = async(isFollowingThread, tmid) => {
+ try {
+ await RocketChat.toggleFollowMessage(tmid, !isFollowingThread);
+ EventEmitter.emit(LISTENER, { message: isFollowingThread ? I18n.t('Unfollowed_thread') : I18n.t('Following_thread') });
+ } catch (e) {
+ log(e);
+ }
+ }
+
renderItem = ({ item }) => {
const {
user, navigation, baseUrl, useRealName
@@ -426,6 +437,7 @@ class ThreadMessagesView extends React.Component {
badgeColor
}}
onPress={this.onThreadPress}
+ toggleFollowThread={this.toggleFollowThread}
/>
);
}
diff --git a/e2e/README.md b/e2e/README.md
index 995bd3f90..f2ae5651b 100644
--- a/e2e/README.md
+++ b/e2e/README.md
@@ -13,7 +13,7 @@
Either
-* Install Rocket.Chat meteor app by following this [guide](https://rocket.chat/docs/developer-guides/quick-start).
+* Install Rocket.Chat meteor app by following this [guide](https://docs.rocket.chat/guides/developer/quick-start).
Or
diff --git a/e2e/tests/assorted/05-joinpublicroom.spec.js b/e2e/tests/assorted/05-joinpublicroom.spec.js
index 1c946f0c2..90707f9f9 100644
--- a/e2e/tests/assorted/05-joinpublicroom.spec.js
+++ b/e2e/tests/assorted/05-joinpublicroom.spec.js
@@ -9,8 +9,8 @@ const room = data.channels.detoxpublic.name;
async function navigateToRoom() {
await searchRoom(room);
- await waitFor(element(by.id(`rooms-list-view-item-${ room }`)).atIndex(0)).toBeVisible().withTimeout(60000);
- await element(by.id(`rooms-list-view-item-${ room }`)).atIndex(0).tap();
+ await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toBeVisible().withTimeout(60000);
+ await element(by.id(`rooms-list-view-item-${ room }`)).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
}
diff --git a/e2e/tests/assorted/08-joinprotectedroom.spec.js b/e2e/tests/assorted/08-joinprotectedroom.spec.js
index d6e5605f7..32b23e9b3 100644
--- a/e2e/tests/assorted/08-joinprotectedroom.spec.js
+++ b/e2e/tests/assorted/08-joinprotectedroom.spec.js
@@ -10,8 +10,8 @@ const joinCode = data.channels.detoxpublicprotected.joinCode
async function navigateToRoom() {
await searchRoom(room);
- await waitFor(element(by.id(`rooms-list-view-item-${ room }`)).atIndex(0)).toBeVisible().withTimeout(60000);
- await element(by.id(`rooms-list-view-item-${ room }`)).atIndex(0).tap();
+ await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toBeVisible().withTimeout(60000);
+ await element(by.id(`rooms-list-view-item-${ room }`)).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
}
diff --git a/e2e/tests/onboarding/06-roomslist.spec.js b/e2e/tests/onboarding/06-roomslist.spec.js
index bbe0b886b..e213b7f2c 100644
--- a/e2e/tests/onboarding/06-roomslist.spec.js
+++ b/e2e/tests/onboarding/06-roomslist.spec.js
@@ -18,7 +18,7 @@ describe('Rooms list screen', () => {
});
it('should have room item', async() => {
- await expect(element(by.id('rooms-list-view-item-general')).atIndex(0)).toExist();
+ await expect(element(by.id('rooms-list-view-item-general'))).toExist();
});
// Render - Header
diff --git a/e2e/tests/room/01-createroom.spec.js b/e2e/tests/room/01-createroom.spec.js
index 5a726a967..07ecd8030 100644
--- a/e2e/tests/room/01-createroom.spec.js
+++ b/e2e/tests/room/01-createroom.spec.js
@@ -67,12 +67,12 @@ describe('Create room screen', () => {
it('should select/unselect user', async() => {
// Spotlight issues
- await element(by.id('select-users-view-item-rocket.cat')).atIndex(0).tap();
+ await element(by.id('select-users-view-item-rocket.cat')).tap();
await waitFor(element(by.id('selected-user-rocket.cat'))).toBeVisible().withTimeout(10000);
await element(by.id('selected-user-rocket.cat')).tap();
await waitFor(element(by.id('selected-user-rocket.cat'))).toBeNotVisible().withTimeout(10000);
// Spotlight issues
- await element(by.id('select-users-view-item-rocket.cat')).atIndex(0).tap();
+ await element(by.id('select-users-view-item-rocket.cat')).tap();
await waitFor(element(by.id('selected-user-rocket.cat'))).toBeVisible().withTimeout(10000);
});
diff --git a/e2e/tests/room/03-roomactions.spec.js b/e2e/tests/room/03-roomactions.spec.js
index 4cdbbff71..74b3114b2 100644
--- a/e2e/tests/room/03-roomactions.spec.js
+++ b/e2e/tests/room/03-roomactions.spec.js
@@ -13,8 +13,8 @@ async function navigateToRoomActions(type) {
room = data.groups.private.name;
}
await searchRoom(room);
- await waitFor(element(by.id(`rooms-list-view-item-${ room }`)).atIndex(0)).toExist().withTimeout(60000);
- await element(by.id(`rooms-list-view-item-${ room }`)).atIndex(0).tap();
+ await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toExist().withTimeout(60000);
+ await element(by.id(`rooms-list-view-item-${ room }`)).tap();
await waitFor(element(by.id('room-view'))).toExist().withTimeout(2000);
await element(by.id('room-view-header-actions')).tap();
await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(5000);
diff --git a/e2e/tests/room/04-roominfo.spec.js b/e2e/tests/room/04-roominfo.spec.js
index d8f079b50..9fd9aebbf 100644
--- a/e2e/tests/room/04-roominfo.spec.js
+++ b/e2e/tests/room/04-roominfo.spec.js
@@ -14,8 +14,8 @@ async function navigateToRoomInfo(type) {
room = privateRoomName;
}
await searchRoom(room);
- await waitFor(element(by.id(`rooms-list-view-item-${ room }`)).atIndex(0)).toExist().withTimeout(60000);
- await element(by.id(`rooms-list-view-item-${ room }`)).atIndex(0).tap();
+ await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toExist().withTimeout(60000);
+ await element(by.id(`rooms-list-view-item-${ room }`)).tap();
await waitFor(element(by.id('room-view'))).toExist().withTimeout(2000);
await element(by.id('room-view-header-actions')).tap();
await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(5000);
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 4d25556b4..709857fb5 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -461,7 +461,7 @@ PODS:
- React
- ReactNativeUiLib (3.0.4):
- React
- - rn-extensions-share (2.4.0):
+ - rn-extensions-share (2.4.1):
- React
- rn-fetch-blob (0.12.0):
- React-Core
@@ -964,7 +964,7 @@ SPEC CHECKSUMS:
ReactCommon: 73d79c7039f473b76db6ff7c6b159c478acbbb3b
ReactNativeART: 78edc68dd4a1e675338cd0cd113319cf3a65f2ab
ReactNativeUiLib: cde7263a7d308b60161cd286f95c9433e79f2f7d
- rn-extensions-share: 8db79372089567cbc5aefe8444869bbc808578d3
+ rn-extensions-share: 5fd84a80e6594706f0dfa1884f2d6d591b382cf5
rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba
RNBootSplash: b3836aa90c5bec690c6cd3c9ab355fcf98d0fe1d
RNCAsyncStorage: d059c3ee71738c39834a627476322a5a8cd5bf36
diff --git a/ios/Pods/Local Podspecs/rn-extensions-share.podspec.json b/ios/Pods/Local Podspecs/rn-extensions-share.podspec.json
index 9d084a5ea..dc0c56a99 100644
--- a/ios/Pods/Local Podspecs/rn-extensions-share.podspec.json
+++ b/ios/Pods/Local Podspecs/rn-extensions-share.podspec.json
@@ -1,6 +1,6 @@
{
"name": "rn-extensions-share",
- "version": "2.4.0",
+ "version": "2.4.1",
"summary": "Share-Extension using react-native for both ios and android",
"license": "MIT",
"authors": {
diff --git a/ios/Pods/Manifest.lock b/ios/Pods/Manifest.lock
index 4d25556b4..709857fb5 100644
--- a/ios/Pods/Manifest.lock
+++ b/ios/Pods/Manifest.lock
@@ -461,7 +461,7 @@ PODS:
- React
- ReactNativeUiLib (3.0.4):
- React
- - rn-extensions-share (2.4.0):
+ - rn-extensions-share (2.4.1):
- React
- rn-fetch-blob (0.12.0):
- React-Core
@@ -964,7 +964,7 @@ SPEC CHECKSUMS:
ReactCommon: 73d79c7039f473b76db6ff7c6b159c478acbbb3b
ReactNativeART: 78edc68dd4a1e675338cd0cd113319cf3a65f2ab
ReactNativeUiLib: cde7263a7d308b60161cd286f95c9433e79f2f7d
- rn-extensions-share: 8db79372089567cbc5aefe8444869bbc808578d3
+ rn-extensions-share: 5fd84a80e6594706f0dfa1884f2d6d591b382cf5
rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba
RNBootSplash: b3836aa90c5bec690c6cd3c9ab355fcf98d0fe1d
RNCAsyncStorage: d059c3ee71738c39834a627476322a5a8cd5bf36
diff --git a/ios/RocketChatRN.xcodeproj/project.pbxproj b/ios/RocketChatRN.xcodeproj/project.pbxproj
index b7176d5ee..15b7d343a 100644
--- a/ios/RocketChatRN.xcodeproj/project.pbxproj
+++ b/ios/RocketChatRN.xcodeproj/project.pbxproj
@@ -1717,7 +1717,7 @@
INFOPLIST_FILE = NotificationService/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
- MARKETING_VERSION = 4.13.1;
+ MARKETING_VERSION = 4.14.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.NotificationService;
@@ -1754,7 +1754,7 @@
INFOPLIST_FILE = NotificationService/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
- MARKETING_VERSION = 4.13.1;
+ MARKETING_VERSION = 4.14.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.NotificationService;
PRODUCT_NAME = "$(TARGET_NAME)";
diff --git a/ios/RocketChatRN/AppDelegate.m b/ios/RocketChatRN/AppDelegate.m
index a7e8be777..28fde17f0 100644
--- a/ios/RocketChatRN/AppDelegate.m
+++ b/ios/RocketChatRN/AppDelegate.m
@@ -69,27 +69,6 @@ static void InitializeFlipper(UIApplication *application) {
// AppGroup MMKV
NSString *groupDir = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"AppGroup"]].path;
[MMKV initializeMMKV:nil groupDir:groupDir logLevel:MMKVLogNone];
-
- // Start the MMKV container
- MMKV *defaultMMKV = [MMKV mmkvWithID:@"migration" mode:MMKVMultiProcess];
- BOOL alreadyMigrated = [defaultMMKV getBoolForKey:@"alreadyMigrated"];
-
- if (!alreadyMigrated) {
- // MMKV Instance that will be used by JS
- MMKV *mmkv = [MMKV mmkvWithID:@"default" mode:MMKVMultiProcess];
-
- // NSUserDefaults -> MMKV (Migration)
- NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"AppGroup"]];
- [mmkv migrateFromUserDefaults:userDefaults];
-
- // Remove our own keys of NSUserDefaults
- for (NSString *key in [userDefaults dictionaryRepresentation].keyEnumerator) {
- [userDefaults removeObjectForKey:key];
- }
-
- // Mark migration complete
- [defaultMMKV setBool:YES forKey:@"alreadyMigrated"];
- }
return YES;
}
diff --git a/ios/RocketChatRN/Images.xcassets/logo.imageset/Contents.json b/ios/RocketChatRN/Images.xcassets/logo.imageset/Contents.json
deleted file mode 100644
index add923b2a..000000000
--- a/ios/RocketChatRN/Images.xcassets/logo.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "images" : [
- {
- "filename" : "icon.png",
- "idiom" : "universal",
- "scale" : "1x"
- },
- {
- "filename" : "icon@2x.png",
- "idiom" : "universal",
- "scale" : "2x"
- },
- {
- "filename" : "icon@3x.png",
- "idiom" : "universal",
- "scale" : "3x"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- }
-}
diff --git a/ios/RocketChatRN/Images.xcassets/logo.imageset/icon.png b/ios/RocketChatRN/Images.xcassets/logo.imageset/icon.png
deleted file mode 100644
index b8b1ab226..000000000
Binary files a/ios/RocketChatRN/Images.xcassets/logo.imageset/icon.png and /dev/null differ
diff --git a/ios/RocketChatRN/Images.xcassets/logo.imageset/icon@2x.png b/ios/RocketChatRN/Images.xcassets/logo.imageset/icon@2x.png
deleted file mode 100644
index dd29b4d50..000000000
Binary files a/ios/RocketChatRN/Images.xcassets/logo.imageset/icon@2x.png and /dev/null differ
diff --git a/ios/RocketChatRN/Images.xcassets/logo.imageset/icon@3x.png b/ios/RocketChatRN/Images.xcassets/logo.imageset/icon@3x.png
deleted file mode 100644
index 42281021c..000000000
Binary files a/ios/RocketChatRN/Images.xcassets/logo.imageset/icon@3x.png and /dev/null differ
diff --git a/ios/RocketChatRN/Info.plist b/ios/RocketChatRN/Info.plist
index c4bce792e..59c55defe 100644
--- a/ios/RocketChatRN/Info.plist
+++ b/ios/RocketChatRN/Info.plist
@@ -23,7 +23,7 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 4.13.1
+ 4.14.0
CFBundleSignature
????
CFBundleURLTypes
diff --git a/ios/ShareRocketChatRN/Info.plist b/ios/ShareRocketChatRN/Info.plist
index 8bb70ebe4..50ee5163d 100644
--- a/ios/ShareRocketChatRN/Info.plist
+++ b/ios/ShareRocketChatRN/Info.plist
@@ -19,7 +19,7 @@
CFBundlePackageType
XPC!
CFBundleShortVersionString
- 4.13.1
+ 4.14.0
CFBundleVersion
1
KeychainGroup
diff --git a/package.json b/package.json
index 6b941d5a3..a8b85b5e7 100644
--- a/package.json
+++ b/package.json
@@ -108,7 +108,7 @@
"react-native-scrollable-tab-view": "^1.0.0",
"react-native-simple-crypto": "RocketChat/react-native-simple-crypto",
"react-native-slowlog": "^1.0.2",
- "react-native-ui-lib": "RocketChat/react-native-ui-lib",
+ "react-native-ui-lib": "RocketChat/react-native-ui-lib#minor-improvements",
"react-native-unimodules": "0.10.1",
"react-native-vector-icons": "7.0.0",
"react-native-webview": "10.3.2",
@@ -119,7 +119,7 @@
"redux-saga": "1.1.3",
"remove-markdown": "^0.3.0",
"reselect": "4.0.0",
- "rn-extensions-share": "^2.4.0",
+ "rn-extensions-share": "RocketChat/rn-extensions-share",
"rn-fetch-blob": "0.12.0",
"rn-root-view": "^1.0.3",
"semver": "7.3.2",
@@ -193,7 +193,8 @@
"build": "xcodebuild -workspace ios/RocketChatRN.xcworkspace -scheme RocketChatRN -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build",
"type": "ios.simulator",
"device": {
- "type": "iPhone 11 Pro"
+ "type": "iPhone 11 Pro",
+ "os": "13.7"
}
},
"ios.sim.release": {
@@ -201,7 +202,8 @@
"build": "xcodebuild -workspace ios/RocketChatRN.xcworkspace -scheme RocketChatRN -configuration Release -sdk iphonesimulator -derivedDataPath ios/build",
"type": "ios.simulator",
"device": {
- "type": "iPhone 11 Pro"
+ "type": "iPhone 11 Pro",
+ "os": "13.7"
},
"artifacts": {
"plugins": {
diff --git a/storybook/stories/List.js b/storybook/stories/List.js
index 568fbc499..632018054 100644
--- a/storybook/stories/List.js
+++ b/storybook/stories/List.js
@@ -111,11 +111,20 @@ stories.add('with icon', () => (
));
-stories.add('with custom color', () => (
+stories.add('with custom colors', () => (
+ alert('Press')}
+ backgroundColor='red'
+ underlayColor='green'
+ translateTitle={false}
+ />
+
));
diff --git a/storybook/stories/Message.js b/storybook/stories/Message.js
index a5e114408..e2f67a469 100644
--- a/storybook/stories/Message.js
+++ b/storybook/stories/Message.js
@@ -469,11 +469,6 @@ export default ({ theme }) => {
tcount={1}
tlm={date}
/>
-
(
+
+);
+
+stories.add('content', () => (
+ <>
+
+
+
+ >
+));
+
+stories.add('touchable', () => (
+ <>
+ alert('Long Press')} onPress={() => alert('Press')} />
+ alert('Press')} />
+ alert('Long Press')} />
+ >
+));
+
+const ThemeStory = ({ theme }) => (
+
+
+
+);
+
+stories.add('themes', () => (
+ <>
+
+
+
+ >
+));
diff --git a/storybook/stories/index.js b/storybook/stories/index.js
index 693e921b1..8edb159ba 100644
--- a/storybook/stories/index.js
+++ b/storybook/stories/index.js
@@ -6,6 +6,7 @@ import { storiesOf } from '@storybook/react-native';
import RoomItem from './RoomItem';
import './List';
+import './ServerItem';
import Message from './Message';
import UiKitMessage from './UiKitMessage';
import UiKitModal from './UiKitModal';
diff --git a/yarn.lock b/yarn.lock
index 12386a08b..36c5c405c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -13109,9 +13109,9 @@ react-native-text-size@4.0.0-rc.1:
resolved "https://registry.yarnpkg.com/react-native-text-size/-/react-native-text-size-4.0.0-rc.1.tgz#1e048d345dd6a5a8e1269e0585c1a5948c478da5"
integrity sha512-CysqjU2jK6Yc+a+kEI222pUyTY2ywcU2HqbFqf1KHymW6OPTdvBBHqbEJKL0QiLhQaFYDbqicM+h990s9TP00g==
-react-native-ui-lib@RocketChat/react-native-ui-lib:
- version "4.2.0"
- resolved "https://codeload.github.com/RocketChat/react-native-ui-lib/tar.gz/48478a9567c1d5d6ade8def7297578efb04554ca"
+react-native-ui-lib@RocketChat/react-native-ui-lib#minor-improvements:
+ version "4.2.1"
+ resolved "https://codeload.github.com/RocketChat/react-native-ui-lib/tar.gz/a80f38aaa947849736ce8643253991cdcb639414"
dependencies:
babel-plugin-transform-inline-environment-variables "^0.0.2"
color "^3.1.0"
@@ -13829,10 +13829,9 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
hash-base "^3.0.0"
inherits "^2.0.1"
-rn-extensions-share@^2.4.0:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/rn-extensions-share/-/rn-extensions-share-2.4.0.tgz#a614f6bf6cdd3948fbd7e0f2519592d4bb5f551f"
- integrity sha512-zX3HcOhib805fVHR7TMYfFXrVBJWYgcrLYNB89RxbKqmSjaqUyWlHYuF61SKOs/dXXeic91e/L8d1YJa6TdzGA==
+rn-extensions-share@RocketChat/rn-extensions-share:
+ version "2.4.1"
+ resolved "https://codeload.github.com/RocketChat/rn-extensions-share/tar.gz/4d7c0e4c2f300e4fb116af7b7cc0dbbc8169150c"
rn-fetch-blob@0.12.0:
version "0.12.0"