Merge branch 'develop' into migrate-auto-translate-view-hooks

This commit is contained in:
Reinaldo Neto 2023-02-02 17:54:46 -03:00 committed by GitHub
commit 0da22bf336
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
184 changed files with 6160 additions and 2775 deletions

View File

@ -3,7 +3,7 @@ defaults: &defaults
macos: &macos
macos:
xcode: "13.3.0"
xcode: "14.2.0"
resource_class: large
bash-env: &bash-env
@ -51,14 +51,14 @@ save-gems-cache: &save-gems-cache
update-fastlane-ios: &update-fastlane-ios
name: Update Fastlane
command: |
echo "ruby-2.6.4" > ~/.ruby-version
echo "ruby-2.7.7" > ~/.ruby-version
bundle install
working_directory: ios
update-fastlane-android: &update-fastlane-android
name: Update Fastlane
command: |
echo "ruby-2.6.4" > ~/.ruby-version
echo "ruby-2.7.7" > ~/.ruby-version
bundle install
working_directory: android
@ -118,26 +118,26 @@ commands:
if [[ $CIRCLE_JOB == "android-build-official" ]]; then
echo -e "APPLICATION_ID=chat.rocket.android" >> ./gradle.properties
echo -e "BugsnagAPIKey=$BUGSNAG_KEY_OFFICIAL" >> ./gradle.properties
echo $CHAT_ROCKET_ANDROID_STORE_FILE_BASE64_JKS | base64 --decode > ./app/$KEYSTORE_OFFICIAL
echo $KEYSTORE_OFFICIAL_BASE64 | base64 --decode > ./app/$KEYSTORE_OFFICIAL
echo -e "KEYSTORE=$KEYSTORE_OFFICIAL" >> ./gradle.properties
echo -e "KEYSTORE_PASSWORD=$CHAT_ROCKET_ANDROID_STORE_PASSWORD" >> ./gradle.properties
echo -e "KEY_ALIAS=$CHAT_ROCKET_ANDROID_KEY_ALIAS" >> ./gradle.properties
echo -e "KEY_PASSWORD=$CHAT_ROCKET_ANDROID_KEY_PASSWORD" >> ./gradle.properties
echo -e "KEYSTORE_PASSWORD=$KEYSTORE_OFFICIAL_PASSWORD" >> ./gradle.properties
echo -e "KEY_ALIAS=$KEYSTORE_OFFICIAL_ALIAS" >> ./gradle.properties
echo -e "KEY_PASSWORD=$KEYSTORE_OFFICIAL_PASSWORD" >> ./gradle.properties
else
echo -e "APPLICATION_ID=chat.rocket.reactnative" >> ./gradle.properties
echo -e "BugsnagAPIKey=$BUGSNAG_KEY" >> ./gradle.properties
echo $KEYSTORE_BASE64 | base64 --decode > ./app/$KEYSTORE
echo -e "KEYSTORE=$KEYSTORE" >> ./gradle.properties
echo -e "KEYSTORE_PASSWORD=$KEYSTORE_PASSWORD" >> ./gradle.properties
echo -e "KEY_ALIAS=$KEY_ALIAS" >> ./gradle.properties
echo -e "KEY_PASSWORD=$KEYSTORE_PASSWORD" >> ./gradle.properties
echo $KEYSTORE_EXPERIMENTAL_BASE64 | base64 --decode > ./app/$KEYSTORE_EXPERIMENTAL
echo -e "KEYSTORE=$KEYSTORE_EXPERIMENTAL" >> ./gradle.properties
echo -e "KEYSTORE_PASSWORD=$KEYSTORE_EXPERIMENTAL_PASSWORD" >> ./gradle.properties
echo -e "KEY_ALIAS=$KEYSTORE_EXPERIMENTAL_ALIAS" >> ./gradle.properties
echo -e "KEY_PASSWORD=$KEYSTORE_EXPERIMENTAL_PASSWORD" >> ./gradle.properties
fi
working_directory: android
- run:
name: Set Google Services
command: |
if [[ $KEYSTORE ]]; then
if [[ $GOOGLE_SERVICES_ANDROID ]]; then
echo $GOOGLE_SERVICES_ANDROID | base64 --decode > google-services.json
fi
working_directory: android/app
@ -151,7 +151,7 @@ commands:
if [[ $CIRCLE_JOB == "android-build-experimental" || "android-automatic-build-experimental" ]]; then
./gradlew bundleExperimentalPlayRelease
fi
if [[ ! $KEYSTORE ]]; then
if [[ ! $GOOGLE_SERVICES_ANDROID ]]; then
./gradlew assembleExperimentalPlayDebug
fi
working_directory: android
@ -200,8 +200,12 @@ commands:
- run:
name: Set Google Services
command: |
if [[ $KEYSTORE ]]; then
if [[ $APP_STORE_CONNECT_API_KEY_BASE64 ]]; then
if [[ $CIRCLE_JOB == "ios-build-official" ]]; then
echo $GOOGLE_SERVICES_IOS | base64 --decode > GoogleService-Info.plist
else
echo $GOOGLE_SERVICES_IOS_EXPERIMENTAL | base64 --decode > GoogleService-Info.plist
fi
fi
working_directory: ios
- run:
@ -223,12 +227,12 @@ commands:
/usr/libexec/PlistBuddy -c "Set IS_OFFICIAL NO" ./NotificationService/Info.plist
fi
if [[ $APP_STORE_CONNECT_API_BASE64 ]]; then
echo $APP_STORE_CONNECT_API_BASE64 | base64 --decode > ./fastlane/app_store_connect_api_key.p8
if [[ $APP_STORE_CONNECT_API_KEY_BASE64 ]]; then
echo $APP_STORE_CONNECT_API_KEY_BASE64 | base64 --decode > ./fastlane/app_store_connect_api_key.p8
if [[ $CIRCLE_JOB == "ios-build-official" ]]; then
bundle exec fastlane ios build_official
else
if [[ $KEYSTORE ]]; then
if [[ $APP_STORE_CONNECT_API_KEY_BASE64 ]]; then
bundle exec fastlane ios build_experimental
else
bundle exec fastlane ios build_fork
@ -318,7 +322,7 @@ commands:
- run:
name: Fastlane Tesflight Upload
command: |
echo $APP_STORE_CONNECT_API_BASE64 | base64 --decode > ./fastlane/app_store_connect_api_key.p8
echo $APP_STORE_CONNECT_API_KEY_BASE64 | base64 --decode > ./fastlane/app_store_connect_api_key.p8
bundle exec fastlane ios beta official:<< parameters.official >>
working_directory: ios
- save_cache: *save-gems-cache

View File

@ -2,7 +2,7 @@ module.exports = {
settings: {
'import/resolver': {
node: {
extensions: ['.ts', '.tsx', '.js', '.ios.js', '.android.js', '.native.js']
extensions: ['.ts', '.tsx', '.js', '.ios.js', '.android.js', '.native.js', '.ios.tsx', '.android.tsx']
}
}
},

View File

@ -1 +1 @@
2.7.4
2.7.7

View File

@ -1,4 +1,4 @@
source 'https://rubygems.org'
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
ruby '2.7.4'
ruby '2.7.7'
gem 'cocoapods', '~> 1.11', '>= 1.11.2'

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -147,7 +147,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode VERSIONCODE as Integer
versionName "4.33.0"
versionName "4.36.0"
vectorDrawables.useSupportLibrary = true
if (!isFoss) {
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
@ -357,9 +357,6 @@ dependencies {
playImplementation project(':@react-native-firebase_app')
playImplementation project(':@react-native-firebase_analytics')
playImplementation project(':@react-native-firebase_crashlytics')
implementation(project(':react-native-jitsi-meet')) { // https://github.com/skrafft/react-native-jitsi-meet#side-note
exclude group: 'com.facebook.react',module:'react-native-svg'
}
implementation fileTree(dir: "libs", include: ["*.jar"])
//noinspection GradleDynamicVersion

View File

@ -1,25 +0,0 @@
package chat.rocket.reactnative;
import android.content.Context;
import com.facebook.react.ReactInstanceManager;
public class MainDebugApplication extends MainApplication {
@Override
public void onCreate() {
super.onCreate();
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
}
/**
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
*
* @param context
* @param reactInstanceManager
*/
private static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
ReactNativeFlipper.initializeFlipper(context, reactInstanceManager);
}
}

View File

@ -5,6 +5,21 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.VIDEO_CAPTURE" />
<uses-permission android:name="android.permission.AUDIO_CAPTURE" />
<!-- permissions related to jitsi call -->
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" android:usesPermissionFlags="neverForLocation" tools:targetApi="s" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="28"/>
<uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION" tools:targetApi="Q"/>
<application
android:name="chat.rocket.reactnative.MainApplication"
android:allowBackup="false"
@ -14,6 +29,7 @@
android:requestLegacyExternalStorage="true"
android:supportsRtl="true"
android:theme="@style/BootTheme"
android:hardwareAccelerated="true"
tools:replace="android:allowBackup">
<activity
android:name="chat.rocket.reactnative.MainActivity"
@ -69,5 +85,10 @@
</intent-filter>
</activity>
</application>
<queries>
<package android:name="org.jitsi.meet" />
<intent>
<action android:name="android.intent.action.SEND" />
</intent>
</queries>
</manifest>

View File

@ -9,13 +9,16 @@ import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.config.ReactFeatureFlags;
import com.facebook.react.ReactInstanceManager;
import com.facebook.soloader.SoLoader;
import com.reactnativecommunity.viewpager.RNCViewPagerPackage;
import com.facebook.react.bridge.JSIModulePackage;
import com.swmansion.reanimated.ReanimatedJSIModulePackage;
import android.content.Context;
import android.content.res.Configuration;
import expo.modules.ApplicationLifecycleDispatcher;
import expo.modules.ReactNativeHostWrapper;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;
@ -76,6 +79,7 @@ public class MainApplication extends Application implements ReactApplication {
// If you opted-in for the New Architecture, we enable the TurboModule system
ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
SoLoader.init(this, /* native exopackage */ false);
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
ApplicationLifecycleDispatcher.onApplicationCreate(this);
}
@ -84,4 +88,35 @@ public class MainApplication extends Application implements ReactApplication {
super.onConfigurationChanged(newConfig);
ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig);
}
/**
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
*
* @param context
* @param reactInstanceManager
*/
private static void initializeFlipper(
Context context, ReactInstanceManager reactInstanceManager) {
if (BuildConfig.DEBUG) {
try {
/*
We use reflection here to pick up the class that initializes Flipper,
since Flipper library is not available in release mode
*/
Class<?> aClass = Class.forName("chat.rocket.reactnative.ReactNativeFlipper");
aClass
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
.invoke(null, context, reactInstanceManager);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}

View File

@ -26,8 +26,6 @@ buildscript {
kotlinVersion = '1.6.10'
supportLibVersion = "28.0.0"
libre_build = !(isPlay.toBoolean())
jitsi_url = "https://github.com/RocketChat/jitsi-maven-repository/raw/master/releases"
jitsi_version = "3.7.0"
}
repositories {
@ -68,9 +66,6 @@ allprojects {
url "$rootDir/../node_modules/detox/Detox-android"
}
maven {
url jitsi_url
}
mavenCentral {
content {
excludeGroup "com.facebook.react"

View File

@ -23,7 +23,7 @@ android.useAndroidX=true
android.enableJetifier=true
# Version of flipper SDK to use with React Native
FLIPPER_VERSION=0.125.0
FLIPPER_VERSION=0.175.0
# Use this property to specify which architecture you want to build.
# You can also override it from the CLI using

View File

@ -28,7 +28,9 @@ export const ROOM = createRequestTypes('ROOM', [
'DELETE',
'REMOVED',
'FORWARD',
'USER_TYPING'
'USER_TYPING',
'HISTORY_REQUEST',
'HISTORY_FINISHED'
]);
export const INQUIRY = createRequestTypes('INQUIRY', [
...defaultTypes,

View File

@ -1,6 +1,6 @@
import { Action } from 'redux';
import { ERoomType } from '../definitions/ERoomType';
import { ERoomType, RoomType } from '../definitions';
import { ROOM } from './actionsTypes';
// TYPE RETURN RELATED
@ -44,7 +44,24 @@ interface IUserTyping extends Action {
status: boolean;
}
export type TActionsRoom = TSubscribeRoom & TUnsubscribeRoom & ILeaveRoom & IDeleteRoom & IForwardRoom & IUserTyping;
export interface IRoomHistoryRequest extends Action {
rid: string;
t: RoomType;
loaderId: string;
}
export interface IRoomHistoryFinished extends Action {
loaderId: string;
}
export type TActionsRoom = TSubscribeRoom &
TUnsubscribeRoom &
ILeaveRoom &
IDeleteRoom &
IForwardRoom &
IUserTyping &
IRoomHistoryRequest &
IRoomHistoryFinished;
export function subscribeRoom(rid: string): TSubscribeRoom {
return {
@ -99,3 +116,19 @@ export function userTyping(rid: string, status = true): IUserTyping {
status
};
}
export function roomHistoryRequest({ rid, t, loaderId }: { rid: string; t: RoomType; loaderId: string }): IRoomHistoryRequest {
return {
type: ROOM.HISTORY_REQUEST,
rid,
t,
loaderId
};
}
export function roomHistoryFinished({ loaderId }: { loaderId: string }): IRoomHistoryFinished {
return {
type: ROOM.HISTORY_FINISHED,
loaderId
};
}

View File

@ -122,7 +122,6 @@ const ActionSheetContentWithInputAndSubmit = ({
}}
testID={testID}
secureTextEntry={secureTextEntry}
inputStyle={{ borderWidth: 2 }}
bottomSheet={isIOS}
/>
) : null}

View File

@ -1,10 +1,11 @@
import React from 'react';
import { ViewStyle } from 'react-native';
import { TGetCustomEmoji } from '../../definitions/IEmoji';
export interface IAvatar {
server?: string;
style?: any;
style?: ViewStyle;
text?: string;
avatar?: string;
emoji?: string;

View File

@ -1,7 +1,9 @@
export const mappedIcons = {
'lamp-bulb': 59812,
'lamp-bulb': 59836,
'phone-in': 59835,
'basketball': 59776,
'percentage': 59777,
'glasses': 59812,
'burger': 59813,
'leaf': 59814,
'airplane': 59815,

File diff suppressed because one or more lines are too long

View File

@ -12,7 +12,6 @@ import { themes } from '../../lib/constants';
import { useTheme } from '../../theme';
import { ROW_HEIGHT } from '../RoomItem';
import { goRoom } from '../../lib/methods/helpers/goRoom';
import Navigation from '../../lib/navigation/appNavigation';
import { useOrientation } from '../../dimensions';
import { IApplicationState, ISubscription, SubscriptionType } from '../../definitions';
@ -98,12 +97,7 @@ const NotifierComponent = React.memo(({ notification, isMasterDetail }: INotifie
prid
};
if (isMasterDetail) {
Navigation.navigate('DrawerNavigator');
} else {
Navigation.navigate('RoomsListView');
}
goRoom({ item, isMasterDetail, jumpToMessageId: _id });
goRoom({ item, isMasterDetail, jumpToMessageId: _id, popToRoot: true });
hideNotification();
};
@ -124,6 +118,7 @@ const NotifierComponent = React.memo(({ notification, isMasterDetail }: INotifie
onPress={onPress}
hitSlop={BUTTON_HIT_SLOP}
background={Touchable.SelectableBackgroundBorderless()}
testID={`in-app-notification-${text}`}
>
<>
<Avatar text={avatar} size={AVATAR_SIZE} type={type} rid={rid} style={styles.avatar} />

View File

@ -1,19 +1,20 @@
import React, { memo, useEffect } from 'react';
import { Easing, Notifier, NotifierRoot } from 'react-native-notifier';
import { connect } from 'react-redux';
import { dequal } from 'dequal';
import NotifierComponent, { INotifierComponent } from './NotifierComponent';
import EventEmitter from '../../lib/methods/helpers/events';
import Navigation from '../../lib/navigation/appNavigation';
import { getActiveRoute } from '../../lib/methods/helpers/navigation';
import { IApplicationState } from '../../definitions';
import { IRoom } from '../../reducers/room';
import { useAppSelector } from '../../lib/hooks';
export const INAPP_NOTIFICATION_EMITTER = 'NotificationInApp';
const InAppNotification = memo(
({ rooms, appState }: { rooms: IRoom['rooms']; appState: string }) => {
const InAppNotification = memo(() => {
const { appState, subscribedRoom } = useAppSelector(state => ({
subscribedRoom: state.room.subscribedRoom,
appState: state.app.ready && state.app.foreground ? 'foreground' : 'background'
}));
const show = (notification: INotifierComponent['notification']) => {
if (appState !== 'foreground') {
return;
@ -23,7 +24,7 @@ const InAppNotification = memo(
const state = Navigation.navigationRef.current?.getRootState();
const route = getActiveRoute(state);
if (payload.rid) {
if (rooms.includes(payload.rid) || route?.name === 'JitsiMeetView') {
if (payload.rid === subscribedRoom || route?.name === 'JitsiMeetView') {
return;
}
Notifier.showNotification({
@ -41,16 +42,9 @@ const InAppNotification = memo(
return () => {
EventEmitter.removeListener(INAPP_NOTIFICATION_EMITTER, listener);
};
}, [rooms]);
}, [subscribedRoom, appState]);
return <NotifierRoot />;
},
(prevProps, nextProps) => dequal(prevProps.rooms, nextProps.rooms)
);
const mapStateToProps = (state: IApplicationState) => ({
rooms: state.room.rooms,
appState: state.app.ready && state.app.foreground ? 'foreground' : 'background'
});
export default connect(mapStateToProps)(InAppNotification);
export default InAppNotification;

View File

@ -14,12 +14,12 @@ import { IEmoji, TAnyMessageModel } from '../../definitions';
import Touch from '../Touch';
export interface IHeader {
handleReaction: (emoji: IEmoji, message: TAnyMessageModel) => void;
handleReaction: (emoji: IEmoji | null, message: TAnyMessageModel) => void;
message: TAnyMessageModel;
isMasterDetail: boolean;
}
type TOnReaction = ({ emoji }: { emoji: IEmoji }) => void;
type TOnReaction = ({ emoji }: { emoji?: IEmoji }) => void;
interface THeaderItem {
item: IEmoji;
@ -94,8 +94,10 @@ const Header = React.memo(({ handleReaction, message, isMasterDetail }: IHeader)
const quantity = Math.trunc(size / (ITEM_SIZE + ITEM_MARGIN * 2) - 1);
const onReaction: TOnReaction = ({ emoji }) => {
handleReaction(emoji, message);
handleReaction(emoji || null, message);
if (emoji) {
addFrequentlyUsed(emoji);
}
};
const renderItem = ({ item }: { item: IEmoji }) => <HeaderItem item={item} onReaction={onReaction} theme={theme} />;

View File

@ -40,6 +40,7 @@ export interface IMessageActionsProps {
editMessagePermission?: string[];
deleteMessagePermission?: string[];
forceDeleteMessagePermission?: string[];
deleteOwnMessagePermission?: string[];
pinMessagePermission?: string[];
createDirectMessagePermission?: string[];
}
@ -71,6 +72,7 @@ const MessageActions = React.memo(
editMessagePermission,
deleteMessagePermission,
forceDeleteMessagePermission,
deleteOwnMessagePermission,
pinMessagePermission,
createDirectMessagePermission
},
@ -80,19 +82,27 @@ const MessageActions = React.memo(
hasEditPermission: false,
hasDeletePermission: false,
hasForceDeletePermission: false,
hasPinPermission: false
hasPinPermission: false,
hasDeleteOwnPermission: false
};
const { showActionSheet, hideActionSheet } = useActionSheet();
const getPermissions = async () => {
try {
const permission = [editMessagePermission, deleteMessagePermission, forceDeleteMessagePermission, pinMessagePermission];
const permission = [
editMessagePermission,
deleteMessagePermission,
forceDeleteMessagePermission,
pinMessagePermission,
deleteOwnMessagePermission
];
const result = await hasPermission(permission, room.rid);
permissions = {
hasEditPermission: result[0],
hasDeletePermission: result[1],
hasForceDeletePermission: result[2],
hasPinPermission: result[3]
hasPinPermission: result[3],
hasDeleteOwnPermission: result[4]
};
} catch {
// Do nothing
@ -134,7 +144,7 @@ const MessageActions = React.memo(
if (tmid === message.id) {
return false;
}
const deleteOwn = isOwn(message);
const deleteOwn = isOwn(message) && permissions.hasDeleteOwnPermission;
if (!(permissions.hasDeletePermission || (Message_AllowDeleting && deleteOwn) || permissions.hasForceDeletePermission)) {
return false;
}
@ -275,10 +285,10 @@ const MessageActions = React.memo(
}
};
const handleReaction: IHeader['handleReaction'] = (shortname, message) => {
const handleReaction: IHeader['handleReaction'] = (emoji, message) => {
logEvent(events.ROOM_MSG_ACTION_REACTION);
if (shortname) {
onReactionPress(shortname, message.id);
if (emoji) {
onReactionPress(emoji, message.id);
} else {
setTimeout(() => reactionInit(message), ACTION_SHEET_ANIMATION_DURATION);
}
@ -343,9 +353,10 @@ const MessageActions = React.memo(
const getOptions = (message: TAnyMessageModel) => {
const options: TActionSheetOptionsItem[] = [];
const videoConfBlock = message.t === 'videoconf';
// Quote
if (!isReadOnly) {
if (!isReadOnly && !videoConfBlock) {
options.push({
title: I18n.t('Quote'),
icon: 'quote',
@ -363,7 +374,7 @@ const MessageActions = React.memo(
}
// Reply in DM
if (room.t !== 'd' && room.t !== 'l' && createDirectMessagePermission) {
if (room.t !== 'd' && room.t !== 'l' && createDirectMessagePermission && !videoConfBlock) {
options.push({
title: I18n.t('Reply_in_direct_message'),
icon: 'arrow-back',
@ -386,11 +397,13 @@ const MessageActions = React.memo(
});
// Copy
if (!videoConfBlock) {
options.push({
title: I18n.t('Copy'),
icon: 'copy',
onPress: () => handleCopy(message)
});
}
// Share
options.push({
@ -400,7 +413,7 @@ const MessageActions = React.memo(
});
// Edit
if (allowEdit(message)) {
if (allowEdit(message) && !videoConfBlock) {
options.push({
title: I18n.t('Edit'),
icon: 'edit',
@ -409,7 +422,7 @@ const MessageActions = React.memo(
}
// Pin
if (Message_AllowPinning && permissions?.hasPinPermission) {
if (Message_AllowPinning && permissions?.hasPinPermission && !videoConfBlock) {
options.push({
title: I18n.t(message.pinned ? 'Unpin' : 'Pin'),
icon: 'pin',
@ -418,7 +431,7 @@ const MessageActions = React.memo(
}
// Star
if (Message_AllowStarring) {
if (Message_AllowStarring && !videoConfBlock) {
options.push({
title: I18n.t(message.starred ? 'Unstar' : 'Star'),
icon: message.starred ? 'star-filled' : 'star',
@ -505,6 +518,7 @@ const mapStateToProps = (state: IApplicationState) => ({
isMasterDetail: state.app.isMasterDetail,
editMessagePermission: state.permissions['edit-message'],
deleteMessagePermission: state.permissions['delete-message'],
deleteOwnMessagePermission: state.permissions['delete-own-message'],
forceDeleteMessagePermission: state.permissions['force-delete-message'],
pinMessagePermission: state.permissions['pin-message'],
createDirectMessagePermission: state.permissions['create-d']

View File

@ -237,7 +237,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
async componentDidMount() {
const db = database.active;
const { rid, tmid, navigation, sharing, usedCannedResponse, isMasterDetail } = this.props;
const { rid, tmid, navigation, sharing, usedCannedResponse } = this.props;
let msg;
try {
const threadsCollection = db.get('threads');
@ -272,7 +272,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
EventEmiter.addEventListener(KEY_COMMAND, this.handleCommands);
}
if (isMasterDetail && usedCannedResponse) {
if (usedCannedResponse) {
this.onChangeText(usedCannedResponse);
}
@ -302,7 +302,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
if (usedCannedResponse !== nextProps.usedCannedResponse) {
this.onChangeText(nextProps.usedCannedResponse ?? '');
}
if (sharing) {
if (sharing && !replying) {
this.setInput(nextProps.message.msg ?? '');
return;
}
@ -857,14 +857,21 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
};
openShareView = (attachments: any) => {
const { message, replyCancel, replyWithMention } = this.props;
const { message, replyCancel, replyWithMention, replying } = this.props;
// Start a thread with an attachment
let value: TThreadModel | IMessage = this.thread;
if (replyWithMention) {
value = message;
replyCancel();
}
Navigation.navigate('ShareView', { room: this.room, thread: value, attachments });
Navigation.navigate('ShareView', {
room: this.room,
thread: value,
attachments,
replying,
replyingMessage: message,
closeReply: replyCancel
});
};
createDiscussion = () => {
@ -1042,16 +1049,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
// Legacy reply or quote (quote is a reply without mention)
} else {
const { user, roomType } = this.props;
const permalink = await this.getPermalink(replyingMessage);
let msg = `[ ](${permalink}) `;
// if original message wasn't sent by current user and neither from a direct room
if (user.username !== replyingMessage?.u?.username && roomType !== 'd' && replyWithMention) {
msg += `@${replyingMessage?.u?.username} `;
}
msg = `${msg} ${message}`;
const msg = await this.formatReplyMessage(replyingMessage, message);
onSubmit(msg);
}
replyCancel();
@ -1063,6 +1061,20 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
}
};
formatReplyMessage = async (replyingMessage: IMessage, message = '') => {
const { user, roomType, replyWithMention, serverVersion } = this.props;
const permalink = await this.getPermalink(replyingMessage);
let msg = `[ ](${permalink}) `;
// if original message wasn't sent by current user and neither from a direct room
if (user.username !== replyingMessage?.u?.username && roomType !== 'd' && replyWithMention) {
msg += `@${replyingMessage?.u?.username} `;
}
const connectionString = compareServerVersion(serverVersion, 'lowerThan', '5.0.0') ? ' ' : '\n';
return `${msg}${connectionString}${message}`;
};
updateMentions = (keyword: any, type: string) => {
if (type === MENTIONS_TRACKING_TYPE_USERS) {
this.getUsers(keyword);

View File

@ -2,13 +2,13 @@ import React, { useEffect, useReducer, useRef } from 'react';
import { Subscription } from 'rxjs';
import I18n from '../../i18n';
import { useAppSelector } from '../../lib/hooks';
import { getUserPresence } from '../../lib/methods';
import { isGroupChat } from '../../lib/methods/helpers';
import { formatDate } from '../../lib/methods/helpers/room';
import { IRoomItemContainerProps } from './interfaces';
import RoomItem from './RoomItem';
import { ROW_HEIGHT, ROW_HEIGHT_CONDENSED } from './styles';
import { useUserStatus } from './useUserStatus';
export { ROW_HEIGHT, ROW_HEIGHT_CONDENSED };
@ -42,11 +42,11 @@ const RoomItemContainer = React.memo(
const isRead = getIsRead(item);
const date = item.roomUpdatedAt && formatDate(item.roomUpdatedAt);
const alert = item.alert || item.tunread?.length;
const connected = useAppSelector(state => state.meteor.connected);
const userStatus = useAppSelector(state => state.activeUsers[id || '']?.status);
const [_, forceUpdate] = useReducer(x => x + 1, 1);
const roomSubscription = useRef<Subscription | null>(null);
const { connected, status } = useUserStatus(item.t, item?.visitor?.status, id);
useEffect(() => {
const init = () => {
if (item?.observe) {
@ -85,8 +85,6 @@ const RoomItemContainer = React.memo(
accessibilityLabel = `, ${I18n.t('last_message')} ${date}`;
}
const status = item.t === 'l' ? item.visitor?.status || item.v?.status : userStatus;
return (
<RoomItem
name={name}

View File

@ -0,0 +1,24 @@
import { TUserStatus } from '../../definitions';
import { useAppSelector } from '../../lib/hooks';
import { RoomTypes } from '../../lib/methods';
export const useUserStatus = (
type: RoomTypes,
liveChatStatus?: TUserStatus,
id?: string
): { connected: boolean; status: TUserStatus } => {
const connected = useAppSelector(state => state.meteor.connected);
const userStatus = useAppSelector(state => state.activeUsers[id || '']?.status);
let status = 'loading';
if (connected) {
if (type === 'd') {
status = userStatus || 'loading';
} else if (type === 'l' && liveChatStatus) {
status = liveChatStatus;
}
}
return {
connected,
status: status as TUserStatus
};
};

View File

@ -19,8 +19,8 @@ const styles = StyleSheet.create({
input: {
height: 48,
paddingLeft: 16,
borderWidth: 2,
borderRadius: 2,
borderWidth: 1,
borderRadius: 4,
alignItems: 'center',
flexDirection: 'row'
},

View File

@ -19,8 +19,8 @@ const styles = StyleSheet.create({
viewContainer: {
marginBottom: 16,
paddingHorizontal: 16,
borderWidth: 2,
borderRadius: 2,
borderWidth: 1,
borderRadius: 4,
justifyContent: 'center'
},
pickerText: {

View File

@ -0,0 +1,80 @@
import React, { useEffect, useState } from 'react';
import { Text, View } from 'react-native';
import Touchable from 'react-native-platform-touchable';
import i18n from '../../../../i18n';
import { getSubscriptionByRoomId } from '../../../../lib/database/services/Subscription';
import { useAppSelector } from '../../../../lib/hooks';
import { getRoomAvatar, getUidDirectMessage } from '../../../../lib/methods/helpers';
import { videoConfStartAndJoin } from '../../../../lib/methods/videoConf';
import { useTheme } from '../../../../theme';
import { useActionSheet } from '../../../ActionSheet';
import AvatarContainer from '../../../Avatar';
import Button from '../../../Button';
import { CustomIcon } from '../../../CustomIcon';
import { BUTTON_HIT_SLOP } from '../../../message/utils';
import StatusContainer from '../../../Status';
import useStyle from './styles';
export default function CallAgainActionSheet({ rid }: { rid: string }): React.ReactElement {
const style = useStyle();
const { colors } = useTheme();
const [user, setUser] = useState({ username: '', avatar: '', uid: '', rid: '' });
const [phone, setPhone] = useState(true);
const [camera, setCamera] = useState(false);
const username = useAppSelector(state => state.login.user.username);
const { hideActionSheet } = useActionSheet();
useEffect(() => {
(async () => {
const room = await getSubscriptionByRoomId(rid);
const uid = (await getUidDirectMessage(room)) as string;
const avt = getRoomAvatar(room);
setUser({ uid, username: room?.name || '', avatar: avt, rid: room?.id || '' });
})();
}, [rid]);
const handleColor = (enabled: boolean) => (enabled ? colors.conferenceCallEnabledIcon : colors.conferenceCallDisabledIcon);
return (
<View style={style.actionSheetContainer}>
<View style={style.actionSheetHeader}>
<Text style={style.actionSheetHeaderTitle}>{i18n.t('Start_a_call')}</Text>
<View style={style.actionSheetHeaderButtons}>
<Touchable
onPress={() => setCamera(!camera)}
style={[style.iconCallContainer, camera && style.enabledBackground, { marginRight: 6 }]}
hitSlop={BUTTON_HIT_SLOP}
>
<CustomIcon name={camera ? 'camera' : 'camera-disabled'} size={16} color={handleColor(camera)} />
</Touchable>
<Touchable
onPress={() => setPhone(!phone)}
style={[style.iconCallContainer, phone && style.enabledBackground]}
hitSlop={BUTTON_HIT_SLOP}
>
<CustomIcon name={phone ? 'microphone' : 'microphone-disabled'} size={16} color={handleColor(phone)} />
</Touchable>
</View>
</View>
<View style={style.actionSheetUsernameContainer}>
<AvatarContainer text={user.avatar} size={36} />
<StatusContainer size={16} id={user.uid} style={{ marginLeft: 8, marginRight: 6 }} />
<Text style={style.actionSheetUsername}>{user.username}</Text>
</View>
<View style={style.actionSheetPhotoContainer}>
<AvatarContainer size={62} text={username} />
</View>
<Button
onPress={() => {
hideActionSheet();
setTimeout(() => {
videoConfStartAndJoin(user.rid, camera);
}, 100);
}}
title={i18n.t('Call')}
/>
</View>
);
}

View File

@ -0,0 +1,27 @@
import React from 'react';
import { Text, View } from 'react-native';
import i18n from '../../../../i18n';
import useStyle from './styles';
import AvatarContainer from '../../../Avatar';
const MAX_USERS = 3;
export type TCallUsers = { _id: string; username: string; name: string; avatarETag: string }[];
export const CallParticipants = ({ users }: { users: TCallUsers }): React.ReactElement => {
const style = useStyle();
return (
<>
{users.map(({ username }, index) =>
index < MAX_USERS ? <AvatarContainer style={{ marginRight: 4 }} key={index} size={28} text={username} /> : null
)}
{users.length > MAX_USERS ? (
<View style={style.plusUsers}>
<Text style={style.plusUsersText}>{users.length > 9 ? '+9' : `+${users.length}`}</Text>
</View>
) : null}
<Text style={style.joined}>{i18n.t('Joined')}</Text>
</>
);
};

View File

@ -0,0 +1,55 @@
import React from 'react';
import { View, Text } from 'react-native';
import i18n from '../../../../i18n';
import { useTheme } from '../../../../theme';
import { CustomIcon, TIconsName } from '../../../CustomIcon';
import useStyle from './styles';
type VideoConfMessageIconProps = {
variant: 'ended' | 'incoming' | 'outgoing';
children: React.ReactElement | React.ReactElement[];
};
export const VideoConferenceBaseContainer = ({ variant, children }: VideoConfMessageIconProps): React.ReactElement => {
const { colors } = useTheme();
const style = useStyle();
const iconStyle: { [key: string]: { icon: TIconsName; color: string; backgroundColor: string; label: string } } = {
ended: {
icon: 'phone-end',
color: colors.conferenceCallEndedPhoneIcon,
backgroundColor: colors.conferenceCallEndedPhoneBackground,
label: i18n.t('Call_ended')
},
incoming: {
icon: 'phone-in',
color: colors.conferenceCallIncomingPhoneIcon,
backgroundColor: colors.conferenceCallIncomingPhoneBackground,
label: i18n.t('Calling')
},
outgoing: {
icon: 'phone',
color: colors.conferenceCallOngoingPhoneIcon,
backgroundColor: colors.conferenceCallOngoingPhoneBackground,
label: i18n.t('Call_ongoing')
}
};
return (
<View style={style.container}>
<View style={style.callInfoContainer}>
<View
style={{
...style.iconContainer,
backgroundColor: iconStyle[variant].backgroundColor
}}
>
<CustomIcon name={iconStyle[variant].icon} size={variant === 'incoming' ? 16 : 24} color={iconStyle[variant].color} />
</View>
<Text style={style.infoContainerText}>{iconStyle[variant].label}</Text>
</View>
<View style={style.callToActionContainer}>{children}</View>
</View>
);
};

View File

@ -0,0 +1,24 @@
import React from 'react';
import { Text } from 'react-native';
import Touchable from 'react-native-platform-touchable';
import i18n from '../../../../i18n';
import { useVideoConf } from '../../../../lib/hooks/useVideoConf';
import useStyle from './styles';
import { VideoConferenceBaseContainer } from './VideoConferenceBaseContainer';
const VideoConferenceDirect = React.memo(({ blockId }: { blockId: string }) => {
const style = useStyle();
const { joinCall } = useVideoConf();
return (
<VideoConferenceBaseContainer variant='incoming'>
<Touchable style={style.callToActionButton} onPress={() => joinCall(blockId)}>
<Text style={style.callToActionButtonText}>{i18n.t('Join')}</Text>
</Touchable>
<Text style={style.callBack}>{i18n.t('Waiting_for_answer')}</Text>
</VideoConferenceBaseContainer>
);
});
export default VideoConferenceDirect;

View File

@ -0,0 +1,64 @@
import React from 'react';
import { Text } from 'react-native';
import Touchable from 'react-native-platform-touchable';
import { IUser } from '../../../../definitions';
import { VideoConferenceType } from '../../../../definitions/IVideoConference';
import i18n from '../../../../i18n';
import { useAppSelector } from '../../../../lib/hooks';
import { useSnaps } from '../../../../lib/hooks/useSnaps';
import { useActionSheet } from '../../../ActionSheet';
import CallAgainActionSheet from './CallAgainActionSheet';
import { CallParticipants, TCallUsers } from './CallParticipants';
import useStyle from './styles';
import { VideoConferenceBaseContainer } from './VideoConferenceBaseContainer';
export default function VideoConferenceEnded({
users,
type,
createdBy,
rid
}: {
users: TCallUsers;
type: VideoConferenceType;
createdBy: Pick<IUser, '_id' | 'username' | 'name'>;
rid: string;
}): React.ReactElement {
const style = useStyle();
const username = useAppSelector(state => state.login.user.username);
const { showActionSheet } = useActionSheet();
const snaps = useSnaps([1250]);
const onlyAuthorOnCall = users.length === 1 && users.some(user => user.username === createdBy.username);
return (
<VideoConferenceBaseContainer variant='ended'>
{type === 'direct' ? (
<>
<Touchable
style={style.callToActionCallBack}
onPress={() =>
showActionSheet({
children: <CallAgainActionSheet rid={rid} />,
snaps
})
}
>
<Text style={style.callToActionCallBackText}>
{createdBy.username === username ? i18n.t('Call_back') : i18n.t('Call_again')}
</Text>
</Touchable>
<Text style={style.callBack}>{i18n.t('Call_was_not_answered')}</Text>
</>
) : (
<>
{users.length && !onlyAuthorOnCall ? (
<CallParticipants users={users} />
) : (
<Text style={style.notAnswered}>{i18n.t('Call_was_not_answered')}</Text>
)}
</>
)}
</VideoConferenceBaseContainer>
);
}

View File

@ -0,0 +1,23 @@
import React from 'react';
import { Text } from 'react-native';
import Touchable from 'react-native-platform-touchable';
import i18n from '../../../../i18n';
import { useVideoConf } from '../../../../lib/hooks/useVideoConf';
import { CallParticipants, TCallUsers } from './CallParticipants';
import useStyle from './styles';
import { VideoConferenceBaseContainer } from './VideoConferenceBaseContainer';
export default function VideoConferenceOutgoing({ users, blockId }: { users: TCallUsers; blockId: string }): React.ReactElement {
const style = useStyle();
const { joinCall } = useVideoConf();
return (
<VideoConferenceBaseContainer variant='outgoing'>
<Touchable style={style.callToActionButton} onPress={() => joinCall(blockId)}>
<Text style={style.callToActionButtonText}>{i18n.t('Join')}</Text>
</Touchable>
<CallParticipants users={users} />
</VideoConferenceBaseContainer>
);
}

View File

@ -0,0 +1,28 @@
import React from 'react';
import SkeletonPlaceholder from 'react-native-skeleton-placeholder';
import { useTheme } from '../../../../theme';
export default function VideoConferenceSkeletonLoading(): React.ReactElement {
const { colors } = useTheme();
return (
<SkeletonPlaceholder backgroundColor={colors.conferenceCallBackground}>
<SkeletonPlaceholder.Item borderWidth={1} borderColor={colors.conferenceCallBorder} borderRadius={4} marginTop={8}>
<SkeletonPlaceholder.Item alignItems={'center'} flexDirection='row' marginTop={16} marginLeft={16}>
<SkeletonPlaceholder.Item width={28} height={26} />
<SkeletonPlaceholder.Item width={75} height={16} marginLeft={8} borderRadius={0} />
</SkeletonPlaceholder.Item>
<SkeletonPlaceholder.Item
width={'100%'}
height={48}
marginTop={16}
borderBottomLeftRadius={4}
borderBottomRightRadius={4}
borderTopLeftRadius={0}
borderTopRightRadius={0}
/>
</SkeletonPlaceholder.Item>
</SkeletonPlaceholder>
);
}

View File

@ -0,0 +1,126 @@
import { StyleSheet } from 'react-native';
import { useTheme } from '../../../../theme';
import sharedStyles from '../../../../views/Styles';
export default function useStyle() {
const { colors } = useTheme();
return StyleSheet.create({
container: { height: 108, flex: 1, borderWidth: 1, borderRadius: 4, marginTop: 8, borderColor: colors.conferenceCallBorder },
callInfoContainer: { flex: 1, alignItems: 'center', paddingLeft: 16, flexDirection: 'row' },
infoContainerText: {
fontSize: 12,
marginLeft: 8,
...sharedStyles.textBold,
color: colors.auxiliaryTintColor
},
iconContainer: {
width: 28,
height: 28,
alignItems: 'center',
justifyContent: 'center',
borderRadius: 4
},
callToActionContainer: {
height: 48,
backgroundColor: colors.conferenceCallBackground,
flexDirection: 'row',
alignItems: 'center',
paddingLeft: 16
},
callToActionButtonText: {
fontSize: 12,
...sharedStyles.textSemibold,
color: colors.buttonText
},
callToActionCallBackText: {
fontSize: 12,
...sharedStyles.textSemibold,
color: colors.conferenceCallCallBackText
},
callToActionButton: {
backgroundColor: colors.tintColor,
minWidth: 50,
alignItems: 'center',
justifyContent: 'center',
height: 32,
borderRadius: 4,
marginRight: 8,
paddingHorizontal: 8
},
joined: {
fontSize: 12,
...sharedStyles.textRegular,
color: colors.passcodeSecondary,
marginLeft: 8
},
plusUsers: {
width: 28,
height: 28,
backgroundColor: colors.conferenceCallPlusUsersButton,
borderRadius: 4,
alignItems: 'center',
justifyContent: 'center'
},
plusUsersText: {
fontSize: 14,
...sharedStyles.textSemibold,
color: colors.conferenceCallPlusUsersText,
alignSelf: 'center'
},
callBack: {
fontSize: 12,
...sharedStyles.textRegular,
color: colors.passcodeSecondary
},
callToActionCallBack: {
backgroundColor: colors.conferenceCallPlusUsersButton,
minWidth: 50,
alignItems: 'center',
justifyContent: 'center',
height: 32,
borderRadius: 4,
marginRight: 8,
paddingHorizontal: 8
},
notAnswered: {
fontSize: 12,
...sharedStyles.textRegular,
color: colors.passcodeSecondary
},
actionSheetContainer: {
paddingHorizontal: 24,
flex: 1
},
actionSheetHeaderTitle: {
fontSize: 14,
...sharedStyles.textBold,
color: colors.passcodePrimary
},
actionSheetUsername: {
fontSize: 16,
...sharedStyles.textBold,
color: colors.passcodePrimary
},
enabledBackground: {
backgroundColor: colors.conferenceCallEnabledIconBackground
},
iconCallContainer: {
padding: 6,
borderRadius: 4
},
actionSheetHeader: { flexDirection: 'row', alignItems: 'center' },
actionSheetHeaderButtons: { flex: 1, alignItems: 'center', flexDirection: 'row', justifyContent: 'flex-end' },
actionSheetUsernameContainer: { flexDirection: 'row', paddingTop: 8, alignItems: 'center' },
actionSheetPhotoContainer: {
height: 220,
width: 148,
backgroundColor: colors.conferenceCallPhotoBackground,
borderRadius: 8,
margin: 24,
alignSelf: 'center',
justifyContent: 'center',
alignItems: 'center'
}
});
}

View File

@ -0,0 +1,23 @@
import React from 'react';
import { useEndpointData } from '../../../lib/hooks/useEndpointData';
import VideoConferenceDirect from './components/VideoConferenceDirect';
import VideoConferenceEnded from './components/VideoConferenceEnded';
import VideoConferenceOutgoing from './components/VideoConferenceOutgoing';
import VideoConferenceSkeletonLoading from './components/VideoConferenceSkeletonLoading';
export default function VideoConferenceBlock({ callId, blockId }: { callId: string; blockId: string }): React.ReactElement {
const { result } = useEndpointData('video-conference.info', { callId });
if (result?.success) {
const { users, type, status, createdBy, rid } = result;
if ('endedAt' in result) return <VideoConferenceEnded createdBy={createdBy} rid={rid} type={type} users={users} />;
if (type === 'direct' && status === 0) return <VideoConferenceDirect blockId={blockId} />;
return <VideoConferenceOutgoing blockId={blockId} users={users} />;
}
return <VideoConferenceSkeletonLoading />;
}

View File

@ -29,6 +29,7 @@ import { DatePicker } from './DatePicker';
import { Overflow } from './Overflow';
import { ThemeContext } from '../../theme';
import { IActions, IButton, IElement, IInputIndex, IParser, ISection } from './interfaces';
import VideoConferenceBlock from './VideoConferenceBlock';
const styles = StyleSheet.create({
input: {
@ -149,6 +150,10 @@ class MessageParser extends UiKitParserMessage<React.ReactElement> {
const [{ loading, value }, action] = useBlockContext(element, context);
return <MultiSelect {...element} value={value} onChange={action} context={context} loading={loading} />;
}
video_conf(element: IElement & { callId: string }) {
return <VideoConferenceBlock callId={element.callId} blockId={element.blockId!} />;
}
}
// plain_text and mrkdwn functions are created in MessageParser and the ModalParser's constructor use the same functions

View File

@ -1,11 +1,9 @@
/* eslint-disable no-shadow */
import React, { useContext, useState } from 'react';
import { BlockContext } from '@rocket.chat/ui-kit';
import React, { useContext, useState } from 'react';
import { useVideoConf } from '../../lib/hooks/useVideoConf';
import { IText } from './interfaces';
import { videoConfJoin } from '../../lib/methods/videoConf';
import { TActionSheetOptionsItem, useActionSheet } from '../ActionSheet';
import i18n from '../../i18n';
export const textParser = ([{ text }]: IText[]) => text;
@ -42,7 +40,7 @@ export const useBlockContext = ({ blockId, actionId, appId, initialValue }: IUse
const { action, appId: appIdFromContext, viewId, state, language, errors, values = {} } = useContext(KitContext);
const { value = initialValue } = values[actionId] || {};
const [loading, setLoading] = useState(false);
const { showActionSheet } = useActionSheet();
const { joinCall } = useVideoConf();
const error = errors && actionId && errors[actionId];
@ -60,20 +58,7 @@ export const useBlockContext = ({ blockId, actionId, appId, initialValue }: IUse
try {
if (appId === 'videoconf-core' && blockId) {
setLoading(false);
const options: TActionSheetOptionsItem[] = [
{
title: i18n.t('Video_call'),
icon: 'camera',
onPress: () => videoConfJoin(blockId, true)
},
{
title: i18n.t('Voice_call'),
icon: 'microphone',
onPress: () => videoConfJoin(blockId, false)
}
];
showActionSheet({ options });
return;
return joinCall(blockId);
}
await action({
blockId,

View File

@ -0,0 +1,62 @@
import React from 'react';
import { View, StyleSheet, Text, ViewStyle } from 'react-native';
import sharedStyles from '../views/Styles';
import { useTheme } from '../theme';
import openLink from '../lib/methods/helpers/openLink';
import { useAppSelector } from '../lib/hooks';
import I18n from '../i18n';
const styles = StyleSheet.create({
bottomContainer: {
flexDirection: 'column',
alignItems: 'center',
marginBottom: 32,
marginHorizontal: 30
},
bottomContainerText: {
...sharedStyles.textRegular,
fontSize: 13
},
bottomContainerTextBold: {
...sharedStyles.textSemibold,
fontSize: 13
}
});
const UGCRules = ({ styleContainer }: { styleContainer?: ViewStyle }) => {
const { colors, theme } = useTheme();
const { server } = useAppSelector(state => ({
server: state.server.server
}));
const openContract = (route: string) => {
if (!server) {
return;
}
openLink(`${server}/${route}`, theme);
};
return (
<View style={[styles.bottomContainer, styleContainer]}>
<Text style={[styles.bottomContainerText, { color: colors.auxiliaryText }]}>
{`${I18n.t('Onboarding_agree_terms')}\n`}
<Text
style={[styles.bottomContainerTextBold, { color: colors.actionTintColor }]}
onPress={() => openContract('terms-of-service')}
>
{I18n.t('Terms_of_Service')}
</Text>{' '}
{I18n.t('and')}
<Text
style={[styles.bottomContainerTextBold, { color: colors.actionTintColor }]}
onPress={() => openContract('privacy-policy')}
>
{' '}
{I18n.t('Privacy_Policy')}
</Text>
</Text>
</View>
);
};
export default UGCRules;

View File

@ -1,14 +1,11 @@
import React from 'react';
import { StyleProp, Text, TextStyle } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { StackNavigationProp } from '@react-navigation/stack';
import { themes } from '../../lib/constants';
import { useTheme } from '../../theme';
import { IUserChannel } from './interfaces';
import styles from './styles';
import { getSubscriptionByRoomId } from '../../lib/database/services/Subscription';
import { ChatsStackParamList } from '../../stacks/types';
import { useAppSelector } from '../../lib/hooks';
import { goRoom } from '../../lib/methods/helpers/goRoom';
@ -22,7 +19,6 @@ interface IHashtag {
const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IHashtag) => {
const { theme } = useTheme();
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
const navigation = useNavigation<StackNavigationProp<ChatsStackParamList, 'RoomView'>>();
const handlePress = async () => {
const index = channels?.findIndex(channel => channel.name === hashtag);
@ -33,7 +29,7 @@ const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IH
};
const room = navParam.rid && (await getSubscriptionByRoomId(navParam.rid));
if (room) {
goRoom({ item: room, isMasterDetail, navigationMethod: isMasterDetail ? navigation.replace : navigation.push });
goRoom({ item: room, isMasterDetail });
} else {
navToRoomInfo(navParam);
}

View File

@ -45,6 +45,7 @@ interface IMarkdownProps {
testID?: string;
style?: StyleProp<TextStyle>[];
onLinkPress?: TOnLinkPress;
isTranslated?: boolean;
}
type TLiteral = {
@ -93,10 +94,8 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
constructor(props: IMarkdownProps) {
super(props);
if (!this.isNewMarkdown) {
this.renderer = this.createRenderer();
}
}
createRenderer = () =>
new Renderer({
@ -310,13 +309,24 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
};
render() {
const { msg, md, mentions, channels, navToRoomInfo, useRealName, username = '', getCustomEmoji, onLinkPress } = this.props;
const {
msg,
md,
mentions,
channels,
navToRoomInfo,
useRealName,
username = '',
getCustomEmoji,
onLinkPress,
isTranslated
} = this.props;
if (!msg) {
return null;
}
if (this.isNewMarkdown) {
if (this.isNewMarkdown && !isTranslated) {
return (
<NewMarkdown
username={username}

View File

@ -18,13 +18,29 @@ import MarkdownContext from './MarkdownContext';
interface IParagraphProps {
value: ParagraphProps['value'];
forceTrim?: boolean;
}
const Inline = ({ value }: IParagraphProps): React.ReactElement | null => {
const Inline = ({ value, forceTrim }: IParagraphProps): React.ReactElement | null => {
const { useRealName, username, navToRoomInfo, mentions, channels } = useContext(MarkdownContext);
return (
<Text style={styles.inline}>
{value.map(block => {
{value.map((block, index) => {
// We are forcing trim when is a `[ ](https://https://open.rocket.chat/) plain_text`
// to clean the empty spaces
if (forceTrim) {
if (index === 0 && block.type === 'LINK') {
block.value.label.value =
// Need to update the @rocket.chat/message-parser to understand that the label can be a Markup | Markup[]
// https://github.com/RocketChat/fuselage/blob/461ecf661d9ff4a46390957c915e4352fa942a7c/packages/message-parser/src/definitions.ts#L141
// @ts-ignore
block.value?.label?.value?.toString().trimLeft() || block?.value?.label?.[0]?.value?.toString().trimLeft();
}
if (index === 1 && block.type !== 'LINK') {
block.value = block.value?.toString().trimLeft();
}
}
switch (block.type) {
case 'IMAGE':
return <Image value={block.value} />;

View File

@ -381,27 +381,6 @@ export const BlockQuote = () => (
</View>
);
const rocketChatLink = [
{
type: 'PARAGRAPH',
value: [
{
type: 'LINK',
value: {
src: {
type: 'PLAIN_TEXT',
value: 'https://rocket.chat'
},
label: {
type: 'PLAIN_TEXT',
value: 'https://rocket.chat'
}
}
}
]
}
];
const markdownLink = [
{
type: 'PARAGRAPH',
@ -487,7 +466,6 @@ const markdownLinkWithEmphasis = [
export const Links = () => (
<View style={styles.container}>
<NewMarkdown tokens={rocketChatLink} />
<NewMarkdown tokens={markdownLink} />
<NewMarkdown tokens={markdownLinkWithEmphasis} />
</View>
@ -806,3 +784,128 @@ export const InlineKatex = () => (
<NewMarkdown tokens={inlineKatex} />
</View>
);
const messageQuote = {
/**
# Hello head 1
[ ](https://google.com)
*/
headAndLink: [
{ type: 'HEADING', level: 1, value: [{ type: 'PLAIN_TEXT', value: 'Hello head 1' }] },
{ type: 'LINE_BREAK' },
{
type: 'PARAGRAPH',
value: [
{
type: 'LINK',
value: { src: { type: 'PLAIN_TEXT', value: 'https://google.com' }, label: { type: 'PLAIN_TEXT', value: ' ' } }
}
]
}
],
/**
# Head 1 as the first line then line break and after paragraph
bla bla bla bla bla bla
bla bla bla bla bla bla
[ ](https://google.com)
*/
headTextAndLink: [
{
type: 'HEADING',
level: 1,
value: [{ type: 'PLAIN_TEXT', value: 'Head 1 as the first line then line break and after paragraph' }]
},
{ type: 'LINE_BREAK' },
{ type: 'PARAGRAPH', value: [{ type: 'PLAIN_TEXT', value: 'bla bla bla bla bla bla ' }] },
{ type: 'PARAGRAPH', value: [{ type: 'PLAIN_TEXT', value: 'bla bla bla bla bla bla ' }] },
{
type: 'PARAGRAPH',
value: [
{
type: 'LINK',
value: { src: { type: 'PLAIN_TEXT', value: 'https://google.com' }, label: { type: 'PLAIN_TEXT', value: ' ' } }
}
]
}
],
/**
[ ](permalink from message)\n# Head 1 after a forced line break
asdas asd asd asd
*/
headTextAndQuote: [
{
type: 'PARAGRAPH',
value: [
{
type: 'LINK',
value: {
src: { type: 'PLAIN_TEXT', value: 'https://open.rocket.chat/direct/subaru123?msg=QB42gWcaO6BgqtLTo' },
label: { type: 'PLAIN_TEXT', value: ' ' }
}
},
{ type: 'PLAIN_TEXT', value: ' ' }
]
},
{ type: 'HEADING', level: 1, value: [{ type: 'PLAIN_TEXT', value: 'Head 1 after a forced line break' }] },
{ type: 'LINE_BREAK' },
{ type: 'PARAGRAPH', value: [{ type: 'PLAIN_TEXT', value: 'Description' }] }
],
/**
[ ](https://google.com) *There is a link before this bold separated by single space*
*/
linkAndBoldText: [
{
type: 'PARAGRAPH',
value: [
{
type: 'LINK',
value: { src: { type: 'PLAIN_TEXT', value: 'https://google.com' }, label: { type: 'PLAIN_TEXT', value: ' ' } }
},
{ type: 'PLAIN_TEXT', value: ' ' },
{ type: 'BOLD', value: [{ type: 'PLAIN_TEXT', value: 'There is a link before this bold separated by single space' }] }
]
}
],
simpleQuote: [
{
type: 'PARAGRAPH',
value: [
{
type: 'LINK',
value: {
src: {
type: 'PLAIN_TEXT',
value: 'https://open.rocket.chat/group/quoteeee9798789?msg=ZZp6t2dCRX4TqExht'
},
// format of label for servers greater or equal than 6.0
label: [
{
type: 'PLAIN_TEXT',
value: ' '
}
]
}
}
]
},
{
type: 'PARAGRAPH',
value: [
{
type: 'PLAIN_TEXT',
value: 'Quoting a message wrote before'
}
]
}
]
};
export const MessageQuote = () => (
<View style={styles.container}>
<NewMarkdown tokens={messageQuote.headAndLink} />
<NewMarkdown tokens={messageQuote.headTextAndLink} />
<NewMarkdown tokens={messageQuote.headTextAndQuote} />
<NewMarkdown tokens={messageQuote.linkAndBoldText} />
<NewMarkdown tokens={messageQuote.simpleQuote} />
</View>
);

View File

@ -17,7 +17,9 @@ const OrderedList = ({ value }: IOrderedListProps): React.ReactElement => {
{value.map(item => (
<View style={styles.row} key={item.number?.toString()}>
<Text style={[styles.text, { color: colors.bodyText }]}>{item.number}. </Text>
<Text style={{ color: colors.bodyText }}>
<Inline value={item.value} />
</Text>
</View>
))}
</View>

View File

@ -12,10 +12,28 @@ interface IParagraphProps {
}
const Paragraph = ({ value }: IParagraphProps) => {
let forceTrim = false;
const { theme } = useTheme();
if (
value?.[0]?.type === 'LINK' &&
// Need to update the @rocket.chat/message-parser to understand that the label can be a Markup | Markup[]
// https://github.com/RocketChat/fuselage/blob/461ecf661d9ff4a46390957c915e4352fa942a7c/packages/message-parser/src/definitions.ts#L141
// @ts-ignore
(value?.[0]?.value?.label?.value?.toString().trim() === '' || value?.[0]?.value?.label?.[0]?.value?.toString().trim() === '')
) {
// We are returning null when we receive a message like this: `[ ](https://open.rocket.chat/)\nplain_text`
// to avoid render a line empty above the the message
if (value.length === 1) {
return null;
}
if (value.length === 2 && value?.[1]?.type === 'PLAIN_TEXT' && value?.[1]?.value?.toString().trim() === '') {
return null;
}
forceTrim = true;
}
return (
<Text style={[styles.text, { color: themes[theme].bodyText }]}>
<Inline value={value} />
<Inline value={value} forceTrim={forceTrim} />
</Text>
);
};

View File

@ -18,7 +18,9 @@ const UnorderedList = ({ value }: IUnorderedListProps) => {
{value.map(item => (
<View style={styles.row}>
<Text style={[styles.text, { color: themes[theme].bodyText }]}>- </Text>
<Text style={{ color: themes[theme].bodyText }}>
<Inline value={item.value} />
</Text>
</View>
))}
</View>

View File

@ -20,6 +20,8 @@ import { TGetCustomEmoji } from '../../definitions/IEmoji';
import { IAttachment } from '../../definitions';
import { TSupportedThemes } from '../../theme';
import { downloadAudioFile } from '../../lib/methods/audioFile';
import EventEmitter from '../../lib/methods/helpers/events';
import { PAUSE_AUDIO } from './constants';
interface IButton {
loading: boolean;
@ -128,6 +130,11 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
this.sound.setOnPlaybackStatusUpdate(this.onPlaybackStatusUpdate);
}
pauseSound = () => {
EventEmitter.removeListener(PAUSE_AUDIO, this.pauseSound);
this.togglePlayPause();
};
async componentDidMount() {
const { file, messageId } = this.props;
const { baseUrl, user } = this.context;
@ -183,6 +190,7 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
}
async componentWillUnmount() {
EventEmitter.removeListener(PAUSE_AUDIO, this.pauseSound);
try {
await this.sound.stopAsync();
} catch {
@ -221,6 +229,7 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
try {
await this.sound.stopAsync();
this.setState({ paused: true, currentTime: 0 });
EventEmitter.removeListener(PAUSE_AUDIO, this.pauseSound);
} catch {
// do nothing
}
@ -243,7 +252,10 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
try {
if (paused) {
await this.sound.pauseAsync();
EventEmitter.removeListener(PAUSE_AUDIO, this.pauseSound);
} else {
EventEmitter.emit(PAUSE_AUDIO);
EventEmitter.addEventListener(PAUSE_AUDIO, this.pauseSound);
await Audio.setAudioModeAsync(mode);
await this.sound.playAsync();
}

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { useRef } from 'react';
import { messageBlockWithContext } from '../UIKit/MessageBlock';
import { IMessageBlocks } from './interfaces';
@ -6,7 +6,9 @@ import { IMessageBlocks } from './interfaces';
const Blocks = React.memo(({ blocks, id: mid, rid, blockAction }: IMessageBlocks) => {
if (blocks && blocks.length > 0) {
const appId = blocks[0]?.appId || '';
return React.createElement(
// eslint-disable-next-line react-hooks/rules-of-hooks
const comp = useRef(
React.createElement(
messageBlockWithContext({
action: async ({ actionId, value, blockId }: { actionId: string; value: string; blockId: string }) => {
if (blockAction) {
@ -24,7 +26,9 @@ const Blocks = React.memo(({ blocks, id: mid, rid, blockAction }: IMessageBlocks
rid
}),
{ blocks }
)
);
return comp.current;
}
return null;
});

View File

@ -1,14 +1,20 @@
import React from 'react';
import { themes } from '../../../../lib/constants';
import { CustomIcon } from '../../../CustomIcon';
import styles from '../../styles';
import { useTheme } from '../../../../theme';
const ReadReceipt = React.memo(({ isReadReceiptEnabled, unread }: { isReadReceiptEnabled?: boolean; unread?: boolean }) => {
const { theme } = useTheme();
if (isReadReceiptEnabled && !unread && unread !== null) {
return <CustomIcon name='check' color={themes[theme].tintColor} size={16} style={styles.rightIcons} />;
const { colors } = useTheme();
if (isReadReceiptEnabled) {
return (
<CustomIcon
name='check'
color={!unread && unread !== null ? colors.tintColor : colors.auxiliaryTintColor}
size={16}
style={styles.rightIcons}
/>
);
}
return null;
});

View File

@ -64,6 +64,7 @@ const Content = React.memo(
useRealName={props.useRealName}
theme={theme}
onLinkPress={onLinkPress}
isTranslated={props.isTranslated}
/>
);
}

View File

@ -1,2 +1,3 @@
export const DISCUSSION = 'discussion';
export const THREAD = 'thread';
export const PAUSE_AUDIO = 'pause_audio';

View File

@ -233,7 +233,9 @@ class MessageContainer extends React.Component<IMessageContainerProps, IMessageC
!(previousItem.groupable === false || item.groupable === false || broadcast === true) &&
// @ts-ignore TODO: IMessage vs IMessageFromServer non-sense
item.ts - previousItem.ts < Message_GroupingPeriod * 1000 &&
previousItem.tmid === item.tmid
previousItem.tmid === item.tmid &&
item.t !== 'rm' &&
previousItem.t !== 'rm'
) {
return false;
}
@ -270,7 +272,7 @@ class MessageContainer extends React.Component<IMessageContainerProps, IMessageC
get isInfo(): string | boolean {
const { item } = this.props;
if (['e2e', 'discussion-created', 'jitsi_call_started'].includes(item.t)) {
if (['e2e', 'discussion-created', 'jitsi_call_started', 'videoconf'].includes(item.t)) {
return false;
}
return item.t;
@ -376,10 +378,13 @@ class MessageContainer extends React.Component<IMessageContainerProps, IMessageC
} = item;
let message = msg;
let isTranslated = false;
// "autoTranslateRoom" and "autoTranslateLanguage" are properties from the subscription
// "autoTranslateMessage" is a toggle between "View Original" and "Translate" state
if (autoTranslateRoom && autoTranslateMessage && autoTranslateLanguage) {
message = getMessageTranslation(item, autoTranslateLanguage) || message;
const messageTranslated = getMessageTranslation(item, autoTranslateLanguage);
isTranslated = !!messageTranslated;
message = messageTranslated || message;
}
return (
@ -455,6 +460,7 @@ class MessageContainer extends React.Component<IMessageContainerProps, IMessageC
blockAction={blockAction}
highlighted={highlighted}
comment={comment}
isTranslated={isTranslated}
/>
</MessageContext.Provider>
);

View File

@ -63,6 +63,7 @@ export interface IMessageContent {
comment?: string;
hasError: boolean;
isHeader: boolean;
isTranslated: boolean;
}
export interface IMessageEmoji {

View File

@ -65,6 +65,7 @@ const messagesWithAuthorName: MessageTypesValues[] = [
'room_changed_avatar',
'room_e2e_disabled',
'room_e2e_enabled',
'room-allowed-reacting',
'room-disallowed-reacting',
'room-set-read-only',
'room-removed-read-only',

View File

@ -8,7 +8,15 @@ import { TThreadMessageModel } from './IThreadMessage';
import { TThreadModel } from './IThread';
import { IUrl, IUrlFromServer } from './IUrl';
export type MessageType = 'jitsi_call_started' | 'discussion-created' | 'e2e' | 'load_more' | 'rm' | 'uj' | MessageTypeLoad | MessageTypesValues;
export type MessageType =
| 'jitsi_call_started'
| 'discussion-created'
| 'e2e'
| 'load_more'
| 'rm'
| 'uj'
| MessageTypeLoad
| MessageTypesValues;
export interface IUserMessage {
_id: string;
@ -222,6 +230,7 @@ export type MessageTypesValues =
| 'room-allowed-reacting'
| 'room-disallowed-reacting'
| 'command'
| 'videoconf'
| LivechatMessageTypes
| TeamMessageTypes
| VoipMessageTypesValues

View File

@ -91,6 +91,7 @@ export interface ISubscription {
livechatData?: any;
tags?: string[];
E2EKey?: string;
E2ESuggestedKey?: string;
encrypted?: boolean;
e2eKeyId?: string;
avatarETag?: string;
@ -145,6 +146,7 @@ export interface IServerSubscription extends IRocketChatRecord {
onHold?: boolean;
encrypted?: boolean;
E2EKey?: string;
E2ESuggestedKey?: string;
unreadAlert?: 'default' | 'all' | 'mentions' | 'nothing';
fname?: unknown;

View File

@ -5,6 +5,7 @@ export interface IUpload {
rid?: string;
path: string;
name?: string;
tmid?: string;
description?: string;
size: number;
type?: string;
@ -12,6 +13,7 @@ export interface IUpload {
progress?: number;
error?: boolean;
subscription?: { id: string };
msg?: string;
}
export type TUploadModel = IUpload & Model;

View File

@ -0,0 +1,95 @@
import type { AtLeast } from './utils';
import type { IRocketChatRecord } from './IRocketChatRecord';
import type { IRoom } from './IRoom';
import type { IUser } from './IUser';
import type { IMessage } from './IMessage';
export enum VideoConferenceStatus {
CALLING = 0,
STARTED = 1,
EXPIRED = 2,
ENDED = 3,
DECLINED = 4
}
export type DirectCallInstructions = {
type: 'direct';
callee: IUser['_id'];
callId: string;
};
export type ConferenceInstructions = {
type: 'videoconference';
callId: string;
rid: IRoom['_id'];
};
export type LivechatInstructions = {
type: 'livechat';
callId: string;
};
export type VideoConferenceType = DirectCallInstructions['type'] | ConferenceInstructions['type'] | LivechatInstructions['type'];
export interface IVideoConferenceUser extends Pick<Required<IUser>, '_id' | 'username' | 'name' | 'avatarETag'> {
ts: Date;
}
export interface IVideoConference extends IRocketChatRecord {
type: VideoConferenceType;
rid: string;
users: IVideoConferenceUser[];
status: VideoConferenceStatus;
messages: {
started?: IMessage['_id'];
ended?: IMessage['_id'];
};
url?: string;
createdBy: Pick<IUser, '_id' | 'username' | 'name'>;
createdAt: Date;
endedBy?: Pick<IUser, '_id' | 'username' | 'name'>;
endedAt?: Date;
providerName: string;
providerData?: Record<string, any>;
ringing?: boolean;
}
export interface IDirectVideoConference extends IVideoConference {
type: 'direct';
}
export interface IGroupVideoConference extends IVideoConference {
type: 'videoconference';
anonymousUsers: number;
title: string;
}
export interface ILivechatVideoConference extends IVideoConference {
type: 'livechat';
}
export type VideoConference = IDirectVideoConference | IGroupVideoConference | ILivechatVideoConference;
export type VideoConferenceInstructions = DirectCallInstructions | ConferenceInstructions | LivechatInstructions;
export const isDirectVideoConference = (call: VideoConference | undefined | null): call is IDirectVideoConference =>
call?.type === 'direct';
export const isGroupVideoConference = (call: VideoConference | undefined | null): call is IGroupVideoConference =>
call?.type === 'videoconference';
export const isLivechatVideoConference = (call: VideoConference | undefined | null): call is ILivechatVideoConference =>
call?.type === 'livechat';
type GroupVideoConferenceCreateData = Omit<IGroupVideoConference, 'createdBy'> & { createdBy: IUser['_id'] };
type DirectVideoConferenceCreateData = Omit<IDirectVideoConference, 'createdBy'> & { createdBy: IUser['_id'] };
type LivechatVideoConferenceCreateData = Omit<ILivechatVideoConference, 'createdBy'> & { createdBy: IUser['_id'] };
export type VideoConferenceCreateData = AtLeast<
DirectVideoConferenceCreateData | GroupVideoConferenceCreateData | LivechatVideoConferenceCreateData,
'createdBy' | 'type' | 'rid' | 'providerName' | 'providerData'
>;

View File

@ -29,6 +29,7 @@ export * from './ISearch';
export * from './TUserStatus';
export * from './IProfile';
export * from './IReaction';
export * from './ERoomType';
export interface IBaseScreen<T extends Record<string, object | undefined>, S extends string> {
navigation: StackNavigationProp<T, S>;

View File

@ -12,6 +12,12 @@ export type E2eEndpoints = {
'e2e.updateGroupKey': {
POST: (params: { uid: string; rid: string; key: string }) => {};
};
'e2e.acceptSuggestedGroupKey': {
POST: (params: { rid: string }) => {};
};
'e2e.rejectSuggestedGroupKey': {
POST: (params: { rid: string }) => {};
};
'e2e.setRoomKeyID': {
POST: (params: { rid: string; keyID: string }) => {};
};

View File

@ -1,3 +1,5 @@
import { VideoConference } from '../../IVideoConference';
export type VideoConferenceEndpoints = {
'video-conference/jitsi.update-timeout': {
POST: (params: { roomId: string }) => void;
@ -8,4 +10,18 @@ export type VideoConferenceEndpoints = {
'video-conference.start': {
POST: (params: { roomId: string }) => { url: string };
};
'video-conference.cancel': {
POST: (params: { callId: string }) => void;
};
'video-conference.info': {
GET: (params: { callId: string }) => VideoConference & {
capabilities: {
mic?: boolean;
cam?: boolean;
title?: boolean;
};
};
};
};

25
app/definitions/utils.ts Normal file
View File

@ -0,0 +1,25 @@
export type Optional<T, K extends keyof T> = Omit<T, K> & Partial<T>;
export type ExtractKeys<T, K extends keyof T, U> = T[K] extends U ? K : never;
export type ValueOf<T> = T[keyof T];
export type UnionToIntersection<T> = (T extends any ? (x: T) => void : never) extends (x: infer U) => void ? U : never;
export type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T;
// `T extends any` is a trick to apply a operator to each member of a union
export type KeyOfEach<T> = T extends any ? keyof T : never;
// Taken from https://effectivetypescript.com/2020/04/09/jsonify/
export type Jsonify<T> = T extends Date
? string
: T extends object
? {
[k in keyof T]: Jsonify<T[k]>;
}
: T;
export type AtLeast<T, K extends keyof T> = Partial<T> & Pick<T, K>;
export type RequiredField<T, K extends keyof T> = T & Required<Pick<T, K>>;

View File

@ -73,19 +73,14 @@ const QueueListView = React.memo(() => {
const onPressItem = (item = {} as IOmnichannelRoom) => {
logEvent(events.QL_GO_ROOM);
if (isMasterDetail) {
navigation.navigate('DrawerNavigator');
} else {
navigation.navigate('RoomsListView');
}
goRoom({
item: {
...item,
// we're calling v as visitor on our mergeSubscriptionsRooms
visitor: item.v
},
isMasterDetail
isMasterDetail,
popToRoot: true
});
};

View File

@ -25,14 +25,9 @@ export const LANGUAGES: ILanguage[] = [
file: () => require('./locales/en.json')
},
{
label: '简体中文',
value: 'zh-CN',
file: () => require('./locales/zh-CN.json')
},
{
label: '繁體中文',
value: 'zh-TW',
file: () => require('./locales/zh-TW.json')
label: 'العربية',
value: 'ar',
file: () => require('./locales/ar.json')
},
{
label: 'Deutsch',
@ -44,11 +39,31 @@ export const LANGUAGES: ILanguage[] = [
value: 'es-ES',
file: () => require('./locales/es-ES.json')
},
{
label: 'Finnish',
value: 'fi',
file: () => require('./locales/fi.json')
},
{
label: 'Français',
value: 'fr',
file: () => require('./locales/fr.json')
},
{
label: 'Italiano',
value: 'it',
file: () => require('./locales/it.json')
},
{
label: '日本語',
value: 'ja',
file: () => require('./locales/ja.json')
},
{
label: 'Nederlands',
value: 'nl',
file: () => require('./locales/nl.json')
},
{
label: 'Português (BR)',
value: 'pt-BR',
@ -65,24 +80,14 @@ export const LANGUAGES: ILanguage[] = [
file: () => require('./locales/ru.json')
},
{
label: 'Nederlands',
value: 'nl',
file: () => require('./locales/nl.json')
label: 'Slovenian (Slovenia)',
value: 'sl-SI',
file: () => require('./locales/sl-SI.json')
},
{
label: 'Italiano',
value: 'it',
file: () => require('./locales/it.json')
},
{
label: '日本語',
value: 'ja',
file: () => require('./locales/ja.json')
},
{
label: 'العربية',
value: 'ar',
file: () => require('./locales/ar.json')
label: 'Swedish',
value: 'sv',
file: () => require('./locales/sv.json')
},
{
label: 'Türkçe',
@ -90,9 +95,14 @@ export const LANGUAGES: ILanguage[] = [
file: () => require('./locales/tr.json')
},
{
label: 'Slovenian (Slovenia)',
value: 'sl-SI',
file: () => require('./locales/sl-SI.json')
label: '简体中文',
value: 'zh-CN',
file: () => require('./locales/zh-CN.json')
},
{
label: '繁體中文',
value: 'zh-TW',
file: () => require('./locales/zh-TW.json')
}
];

View File

@ -256,6 +256,11 @@
"Forward_to_user": "Weiterleiten an Benutzer",
"Full_table": "Klicken, um die ganze Tabelle anzuzeigen",
"Generate_New_Link": "Neuen Link erstellen",
"Get_link": "Link erhalten",
"User_joined_the_channel": "ist dem Kanal beigetreten",
"User_joined_the_conversation": "hat sich dem Gespräch angeschlossen",
"User_joined_the_team": "ist dem Team beigetreten",
"User_left_this_channel": "hat den Kanal verlassen",
"Has_left_the_team": "Hat das Team verlassen",
"Hide_System_Messages": "Systemnachrichten ausblenden",
"Hide_type_messages": "\"{{type}}\"-Nachrichten ausblenden",
@ -321,6 +326,7 @@
"Message_accessibility": "Nachricht von {{user}} um {{time}}: {{message}}",
"Message_actions": "Nachrichtenaktionen",
"Message_pinned": "Eine Nachricht wurde angeheftet",
"Message_removed": "Nachricht entfernt",
"Message_starred": "Nachricht favorisiert",
"Message_unstarred": "Nachricht nicht mehr favorisiert",
"message": "Nachricht",
@ -348,6 +354,7 @@
"No_mentioned_messages": "Keine Nachrichten mit Erwähnungen",
"No_pinned_messages": "Keine angehefteten Nachrichten",
"No_results_found": "Keine Ergebnisse gefunden",
"No_members_found": "Keine Mitglieder gefunden",
"No_starred_messages": "Keine markierten Nachrichten",
"No_thread_messages": "Keine Threadnachrichten",
"No_label_provided": "Kein(e) {{label}} gesetzt.",
@ -443,11 +450,21 @@
"Roles": "Rollen",
"Room_actions": "Room-Aktionen",
"Room_changed_announcement": "Room-Ansage geändert in: {{announcement}} von {{userBy}}",
"room_avatar_changed": "hat den Raum-Avatar geändert",
"Room_changed_description": "Room-beschreibung geändert in: {{description}} von {{userBy}}",
"changed_room_description": "hat die Raumbeschreibung geändert zu: {{description}}",
"changed_room_announcement": "hat die Ankündigung des Raumes geändert zu: {{announcement}}",
"room_changed_type": "hat den Raum geändert zu {{type}}",
"room_changed_topic_to": "hat das Thema des Raumes geändert zu: {{topic}}",
"Room_Files": "Room-Dateien",
"Room_Info_Edit": "Room-Info bearbeiten",
"Room_Info": "Room-Info",
"Room_Members": "Room-Mitglieder",
"Room_name_changed_to": "hat den Namen des Raumes geändert zu: {{name}}",
"room_disallowed_reactions": "hat Reaktionen untersagt",
"room_allowed_reactions": "hat Reaktionen erlaubt",
"room_removed_read_only_permission": "hat die Nur-Lese-Berechtigung entfernt",
"room_set_read_only_permission": "hat den Raum auf \"Nur Lesen\" gesetzt",
"SAVE": "SPEICHERN",
"Save_Changes": "Änderungen speichern",
"Save": "speichern",
@ -463,6 +480,7 @@
"Search_Messages": "Nachrichten suchen",
"Search": "Suche",
"Search_by": "Suche nach",
"Search_emoji": "Emoji suchen",
"Search_global_users": "Nach globalen Benutzern suchen",
"Search_global_users_description": "Wenn aktiviert, Können Sie nach Benutzern von anderen Unternehmen oder Servern suchen.",
"Seconds": "{{second}} Sekunden",
@ -547,13 +565,21 @@
"Unsupported_system_message": "Nicht unterstützte Systemmeldung",
"Updating": "Aktualisierung …",
"Uploading": "Hochladen",
"FileUpload_Error": "Fehler bei Datei-Upload",
"Upload_in_progress": "Upload in Bearbeitung",
"Upload_file_question_mark": "Datei hochladen?",
"User": "Benutzer",
"Users": "Benutzer",
"User_added_to": "hat {{userAdded}} hinzugefügt",
"User_Info": "Benutzerinfo",
"User_has_been_key": "Benutzer wurde {{key}}",
"User_is_no_longer_role_by_": "{{user}} ist nicht länger {{role}} von {{userBy}}",
"User_has_been_muted": "hat {{userMuted}} stumm gestellt",
"User_has_been_removed": "hat {{userRemoved}} entfernt",
"User_sent_an_attachment": "{{user}}: eine Datei gesendet",
"User_has_been_unmuted": "hat {{userUnmuted}} nicht mehr auf stumm gestellt",
"Defined_user_as_role": "hat {{user}} als {{role}} gesetzt",
"Removed_user_as_role": "hat {{user}} als {{role}} entfernt",
"Username_is_empty": "Der Benutzername ist leer",
"Username": "Benutzername",
"Username_or_email": "Benutzername oder E-Mail-Adresse",
@ -695,6 +721,8 @@
"Message_Ignored": "Nachricht ignoriert. Antippen um sie zu zeigen.",
"Enter_workspace_URL": "Arbeitsbereich-URL",
"Workspace_URL_Example": "z.B. https://rocketchat.deine-firma.de",
"Enabled_E2E_Encryption_for_this_room": "hat E2E-Verschlüsselung für diesen Raum aktiviert",
"Disabled_E2E_Encryption_for_this_room": "hat E2E-Verschlüsselung für diesen Raum deaktiviert",
"Teams": "Teams",
"No_team_channels_found": "Keine Kanäle gefunden",
"Team_not_found": "Team nicht gefunden",
@ -778,7 +806,11 @@
"Unsupported_format": "Nicht unterstütztes Format",
"Downloaded_file": "Heruntergeladene Datei",
"Error_Download_file": "Fehler beim Herunterladen der Datei",
"added__roomName__to_this_team": "hat #{{roomName}} diesem Team hinzugefügt",
"Added__username__to_this_team": "hat @{{user_added}} zu einem Team hinzugefügt",
"Converting_team_to_channel": "Team in Channel umwandeln",
"Converted__roomName__to_a_team": "hat #{{roomName}} in ein Team umgewandelt",
"Converted__roomName__to_a_channel": "hat #{{roomName}} in einen Kanal umgewandelt",
"Deleted__roomName__": "#{{roomName}} gelöscht",
"Message_HideType_added_user_to_team": "Nachrichten \"Benutzer zu Team hinzugefügt\" ausblenden",
"Message_HideType_removed_user_from_team": "Nachrichten \"Benutzer aus Team entfernt\" ausblenden",
@ -789,7 +821,10 @@
"Message_HideType_user_converted_to_team": "Nachrichten \"Benutzer hat Channel in Team konvertiert\" ausblenden",
"Message_HideType_user_deleted_room_from_team": "Nachrichten \"Benutzer hat Room aus Team gelöscht\" ausblenden",
"Message_HideType_user_removed_room_from_team": "Nachrichten \"Benutzer hat Room aus Team entfernt\" ausblenden",
"Removed__roomName__from_the_team": "hat #{{roomName}} aus diesem Team entfernt",
"Removed__username__from_the_team": "hat @{{userRemoved}} aus diesem Team entfernt",
"User_joined_team": "ist dem Team beigetreten",
"User_left_team": "hat das Team verlassen",
"Place_chat_on_hold": "Chat in die Warteschleife stellen",
"Would_like_to_place_on_hold": "Möchten Sie diesen Chat in die Warteschleife legen?",
"Open_Livechats": "Offene Livechats",
@ -808,5 +843,20 @@
"totp-invalid": "Code oder Passwort ist falsch",
"Close_Chat": "Chat schließen",
"Select_tags": "Tags auswählen",
"Broadcast_hint": "Nur autorisierte Benutzer können neue Nachrichten schreiben, die anderen Benutzer können jedoch antworten"
"Skip": "Überspringen",
"N_Selected_members": "{{n}} ausgewählt",
"Broadcast": "Broadcast",
"Broadcast_hint": "Nur autorisierte Benutzer können neue Nachrichten schreiben, die anderen Benutzer können jedoch antworten",
"Team_hint_private": "Nur eingeladene Benutzer können beitreten",
"Team_hint_public": "Wenn deaktiviert, kann jeder dem Team beitreten",
"Team_hint_not_read_only": "Jeder Benutzer in diesem Team kann Nachrichten schreiben",
"Team_hint_encrypted": "Ende-zu-Ende-verschlüsseltes Team. In verschlüsselten Team ist keine Suche möglich und Benachrichtigungen zeigen möglicherweise nicht den Nachrichteninhalt an.",
"Team_hint_encrypted_not_available": "Nur für private Teams verfügbar",
"Channel_hint_private": "Nur eingeladene Nutzer können auf diesen Kanal zugreifen",
"Channel_hint_public": "Jeder kann auf diesen Kanal zugreifen",
"Channel_hint_encrypted": "Ende-zu-Ende verschlüsselter Kanal. Die Suche funktioniert nicht mit verschlüsselten Channels. In Benachrichtigungen wird der Inhalt der Nachricht möglicherweise nicht angezeigt.",
"Channel_hint_not_read_only": "Alle Nutzer im Kanal können neue Nachrichten schreiben",
"Channel_hint_encrypted_not_available": "Nicht verfügbar in öffentlichen Kanälen",
"Read_only_hint": "Nur autorisierte Benutzer können neue Nachrichten schreiben",
"Discussion": "Diskussion"
}

View File

@ -565,6 +565,8 @@
"Unsupported_system_message": "Unsupported system message",
"Updating": "Updating...",
"Uploading": "Uploading",
"FileUpload_Error": "File Upload Error",
"Upload_in_progress": "Upload in progress",
"Upload_file_question_mark": "Upload file?",
"User": "User",
"Users": "Users",
@ -862,6 +864,16 @@
"Select_Members": "Select Members",
"Also_send_thread_message_to_channel_behavior": "Also send thread message to channel",
"Accounts_Default_User_Preferences_alsoSendThreadToChannel_Description": "Allow users to select the Also send to channel behavior",
"Waiting_for_answer": "Waiting for answer",
"Call_ended": "Call ended",
"Call_was_not_answered": "Call was not answered",
"Call_back": "Call Back",
"Call_again": "Call Again",
"Call_ongoing": "Call Ongoing",
"Joined": "Joined",
"Calling": "Calling...",
"Start_a_call": "Start a call",
"Call": "Call",
"Reply_in_direct_message": "Reply in Direct Message",
"room_archived": "archived room",
"room_unarchived": "unarchived room"

View File

@ -183,6 +183,7 @@
"Forgot_password": "¿Ha olvidado su contraseña?",
"Forgot_Password": "Olvidé la contraseña",
"Full_table": "Click para ver la tabla completa",
"Get_link": "Obtener enlace",
"In_App_And_Desktop": "En la aplicación y en el escritorio",
"In_App_and_Desktop_Alert_info": "Muestra un banner en la parte superior de la pantalla cuando la aplicación esté abierta y muestra una notificación en el escritorio",
"Invisible": "Invisible",
@ -214,6 +215,7 @@
"Message_accessibility": "Mensaje de {{user}} a las {{time}}: {{message}}",
"Message_actions": "Acciones de mensaje",
"Message_pinned": "Mensaje fijado",
"Message_removed": "Mensaje eliminado",
"message": "mensaje",
"messages": "mensajes",
"Messages": "Mensajes",
@ -423,6 +425,5 @@
"Server_selection_numbers": "Seleccionar servidor 1...9",
"Add_server": "Añadir servidor",
"New_line": "Nueva línea",
"Broadcast_hint": "Sólo los usuarios autorizados pueden escribir nuevos mensajes, el resto podrán responder sobre los mismos.",
"Get_link": "Obtener enlace"
"Broadcast_hint": "Sólo los usuarios autorizados pueden escribir nuevos mensajes, el resto podrán responder sobre los mismos."
}

View File

@ -1 +1,880 @@
{ }
{
"__count__empty_rooms_will_be_removed_automatically": "{{count}} tyhjää huonetta poistetaan.",
"__count__empty_room_will_be_removed_automatically": "{{count}} tyhjä huone poistetaan.",
"1_person_reacted": "1 henkilö reagoi",
"1_user": "1 käyttäjä",
"error-action-not-allowed": "{{action}} ei ole sallittu",
"error-application-not-found": "Sovellusta ei löydy",
"error-archived-duplicate-name": "Nimellä {{room_name}} on arkistoitu kanava",
"error-avatar-invalid-url": "Virheellinen avatarin URL-osoite: {{url}}",
"error-avatar-url-handling": "Virhe käsiteltäessä avatarin asetusta URL-osoitteesta ({{url}}) käyttäjälle {{username}}",
"error-cant-invite-for-direct-room": "Et voi kutsua käyttäjää suoriin huoneisiin",
"error-could-not-change-email": "Ei voitu vaihtaa sähköpostia",
"error-could-not-change-name": "Ei voitu vaihtaa nimeä",
"error-could-not-change-username": "Ei voitu vaihtaa käyttäjätunnusta",
"error-could-not-change-status": "Ei voitu vaihtaa tilaa",
"error-delete-protected-role": "Ei voi poistaa suojattua roolia",
"error-department-not-found": "Osastoa ei löydy",
"error-direct-message-file-upload-not-allowed": "Tiedostojen jakaminen ei ole sallittua suorissa viesteissä",
"error-duplicate-channel-name": "Kanava nimeltä {{room_name}} on jo olemassa",
"error-email-domain-blacklisted": "Sähköpostin toimialue on estettyjen luettelossa",
"error-email-send-failed": "Virhe yritettäessä lähettää sähköpostia: {{message}}",
"error-save-image": "Virhe tallennettaessa kuvaa",
"error-save-video": "Virhe tallennettaessa videota",
"error-field-unavailable": "{{field}} on jo käytössä :(",
"error-file-too-large": "Tiedosto on liian suuri",
"error-not-permission-to-upload-file": "Sinulla ei ole oikeutta ladata tiedostoja",
"error-importer-not-defined": "Tuontia ei ole määritetty oikein, siitä puuttuu Import-luokka.",
"error-input-is-not-a-valid-field": "{{input}} ei ole kelvollinen {{field}}",
"error-invalid-actionlink": "Virheellinen toimintalinkki",
"error-invalid-arguments": "Virheelliset argumentit",
"error-invalid-asset": "Virheellinen resurssi",
"error-invalid-channel": "Virheellinen kanava.",
"error-invalid-channel-start-with-chars": "Virheellinen kanava. Aloita @- tai #-merkillä",
"error-invalid-custom-field": "Virheellinen mukautettu kenttä",
"error-invalid-custom-field-name": "Virheellinen mukautetun kentän nimi. Käytä vain kirjaimia, numeroita, yhdysviivoja ja alaviivoja.",
"error-invalid-date": "Virheellinen päivämäärä.",
"error-invalid-description": "Virheellinen kuvaus",
"error-invalid-domain": "Virheellinen toimialue",
"error-invalid-email": "Virheellinen sähköposti {{email}}",
"error-invalid-email-address": "Virheellinen sähköpostiosoite",
"error-invalid-file-height": "Virheellinen tiedoston korkeus",
"error-invalid-file-type": "Virheellinen tiedostotyyppi",
"error-invalid-file-width": "Virheellinen tiedoston leveys",
"error-invalid-from-address": "Virheellinen lähettäjän osoite.",
"error-invalid-integration": "Virheellinen integraatio",
"error-invalid-message": "Virheellinen viesti",
"error-invalid-method": "Virheellinen tapa",
"error-invalid-name": "Virheellinen nimi",
"error-invalid-password": "Virheellinen salasana",
"error-invalid-redirectUri": "Virheellinen redirectUri",
"error-invalid-role": "Virheellinen rooli",
"error-invalid-room": "Virheellinen huone",
"error-invalid-room-name": "{{room_name}} ei ole kelvollinen huoneen nimi",
"error-invalid-room-type": "{{type}} ei ole kelvollinen huoneen tyyppi.",
"error-invalid-settings": "Asetukset ovat virheelliset",
"error-invalid-subscription": "Virheellinen tilaus",
"error-invalid-token": "Virheellinen tunnus",
"error-invalid-triggerWords": "Virheelliset triggerWords-sanat",
"error-invalid-urls": "Virheelliset URL-osoitteet",
"error-invalid-user": "Virheellinen käyttäjä",
"error-invalid-username": "Virheellinen käyttäjätunnus",
"error-invalid-webhook-response": "Webhook-URL ilmoitti muun tilan kuin 200",
"error-message-deleting-blocked": "Viestin poistaminen on estetty",
"error-message-editing-blocked": "Viestin muokkaus on estetty",
"error-message-size-exceeded": "Viestin koko on yli Message_MaxAllowedSize",
"error-missing-unsubscribe-link": "Sinun on annettava [unsubscribe] -linkki.",
"error-no-owner-channel": "Et omista kanavaa",
"error-no-tokens-for-this-user": "Käyttäjällä ei ole tunnuksia",
"error-not-allowed": "Ei sallittu",
"error-not-authorized": "Ei valtuutettu",
"error-push-disabled": "Push-ilmoitukset ovat poissa käytöstä",
"error-remove-last-owner": "Tämä on viimeinen omistaja. Aseta uusi omistaja ennen kuin poistat tämän.",
"error-role-in-use": "Roolia ei voi poistaa, koska se on käytössä",
"error-role-name-required": "Roolin nimi on pakollinen",
"error-password-same-as-current": "Annettu salasana sama kuin nykyinen salasana",
"error-the-field-is-required": "Kenttä {{field}} on pakollinen.",
"error-too-many-requests": "Virhe, liikaa pyyntöjä. Hidasta vähän. Odota {{seconds}} sekuntia ennen uutta yritystä.",
"error-user-is-not-activated": "Käyttäjää ei ole aktivoitu",
"error-user-has-no-roles": "Käyttäjällä ei ole rooleja",
"error-user-limit-exceeded": "Käyttäjien määrä, jonka yrität kutsua kanavalle #channel_name, ylittää järjestelmänvalvojan asettaman rajan",
"error-user-not-in-room": "Käyttäjä ei ole tässä huoneessa",
"error-user-registration-custom-field": "error-user-registration-custom-field",
"error-user-registration-disabled": "Käyttäjän rekisteröinti on poissa käytöstä",
"error-user-registration-secret": "Käyttäjän rekisteröinti on sallittu ainoastaan salaisen URL-osoitteen kautta",
"error-you-are-last-owner": "Olet viimeinen omistaja. Aseta uusi omistaja ennen huoneesta lähtöä.",
"error-status-not-allowed": "Näkymätön tila on poistettu käytöstä",
"A_new_owner_will_be_assigned_automatically_to__count__rooms": "Uusi omistaja liitetään automaattisesti {{count}} huoneeseen.",
"A_new_owner_will_be_assigned_automatically_to__count__room": "Uusi omistaja liitetään automaattisesti {{count}} huoneeseen.",
"Actions": "Toimet",
"Activity": "Toiminta",
"Add_Reaction": "Lisää reaktio",
"Add_Server": "Lisää palvelin",
"Add_users": "Lisää käyttäjiä",
"Admin_Panel": "Hallintapaneeli",
"Agent": "Agentti",
"Alert": "Hälytys",
"alert": "hälytys",
"alerts": "hälytystä",
"All_users_in_the_channel_can_write_new_messages": "Kaikki kanavan käyttäjät voivat kirjoittaa uusia viestejä",
"All_users_in_the_team_can_write_new_messages": "Kaikki tiimin käyttäjät voivat kirjoittaa uusia viestejä",
"A_meaningful_name_for_the_discussion_room": "Mielekäs nimi keskusteluhuoneelle",
"All": "Kaikki",
"All_Messages": "Kaikki viestit",
"Allow_Reactions": "Salli reaktiot",
"Alphabetical": "Aakkosjärjestys",
"and_more": "ja muuta",
"and": "ja",
"announcement": "ilmoitus",
"Announcement": "Ilmoitus",
"Apply_Your_Certificate": "Käytä varmennetta",
"ARCHIVE": "ARKISTO",
"archive": "arkisto",
"are_typing": "kirjoittavat",
"Are_you_sure_question_mark": "Oletko varma?",
"Are_you_sure_you_want_to_delete_your_account": "Haluatko varmasti poistaa tilisi?",
"Deleting_a_user_will_delete_all_messages": "Käyttäjän poistaminen poistaa myös kaikki kyseisen käyttäjän viestit, huoneet ja tiimit. Tätä ei voi kumota.",
"Are_you_sure_you_want_to_leave_the_room": "Haluatko varmasti poistua huoneesta {{room}}?",
"Audio": "Ääni",
"Authenticating": "Todennetaan",
"Automatic": "Automaattinen",
"Auto_Translate": "Automaattikäännös",
"Avatar_changed_successfully": "Avatar on vaihdettu!",
"Avatar_Url": "Avatarin URL-osoite",
"Away": "Poissa",
"Back": "Takaisin",
"Black": "Musta",
"Block_user": "Estä käyttäjä",
"Browser": "Selain",
"Busy": "Varattu",
"By_proceeding_you_are_agreeing": "Jatkamalla hyväksyt",
"Cancel_editing": "Peruuta muokkaus",
"Cancel_recording": "Peruuta tallennus",
"Cancel": "Peruuta",
"changing_avatar": "vaihtaa avataria",
"creating_channel": "luo kanavaa",
"creating_invite": "luo kutsua",
"Channel_Name": "Kanavan nimi",
"Channels": "Kanavat",
"Chats": "Keskustelut",
"Chat_started": "Keskustelu aloitettu",
"Call_already_ended": "Puhelu on jo päättynyt!",
"Clear_cookies_alert": "Haluatko tyhjentää kaikki evästeet?",
"Clear_cookies_desc": "Tämä toiminto tyhjentää kaikki kirjautumisevästeet, joten voit kirjautua muille tileille.",
"Clear_cookies_yes": "Kyllä, tyhjennä evästeet",
"Clear_cookies_no": "Ei, säilytä evästeet",
"Click_to_join": "Liity napsauttamalla!",
"Close": "Sulje",
"Close_emoji_selector": "Sulje emojien valitsin",
"Closing_chat": "Suljetaan keskustelua",
"Change_language_loading": "Vaihdetaan kieltä.",
"Chat_closed_by_agent": "Agentti sulki keskustelun",
"Choose": "Valitse",
"Choose_from_library": "Valitse kirjastosta",
"Choose_file": "Valitse tiedosto",
"Choose_where_you_want_links_be_opened": "Valitse, missä linkit avataan",
"Code": "Koodi",
"Code_or_password_invalid": "Koodi tai salasana virheellinen",
"Conversation_closed": "Keskustelu suljettu",
"Collaborative": "Yhteistyö",
"Confirm": "Vahvista",
"Connect": "Yhdistä",
"Connected": "Yhdistetty",
"connecting_server": "yhdistää palvelimeen",
"Connecting": "Yhdistetään...",
"Contact_us": "Ota yhteyttä",
"Contact_your_server_admin": "Ota yhteys palvelimen järjestelmänvalvojaan.",
"Continue_with": "Jatka:",
"Copied_to_clipboard": "Kopioitu leikepöydälle!",
"Copy": "Kopioi",
"Conversation": "Keskustelu",
"Certificate_password": "Varmenteen salasana",
"Clear_cache": "Tyhjennä paikallisen palvelimen välimuisti",
"Clear_cache_loading": "Tyhjennetään välimuistia.",
"Whats_the_password_for_your_certificate": "Mikä on varmenteesi salasana?",
"Create_account": "Luo tili",
"Create_Channel": "Luo kanava",
"Create_Direct_Messages": "Luo suoria viestejä",
"Create_Discussion": "Luo keskustelu",
"Created_snippet": "loi katkelman",
"Create_a_new_workspace": "Luo uusi työtila",
"Create": "Luo",
"Custom_Status": "Mukautettu tila",
"Dark": "Tumma",
"Dark_level": "Tumman taso",
"Default": "Oletus",
"Default_browser": "Oletusselain",
"Delete_Room_Warning": "Huomeen poistaminen poistaa kaikki huoneessa olevat viestit. Tätä ei voi kumota.",
"Department": "Osasto",
"delete": "poista",
"Delete": "Poista",
"DELETE": "POISTA",
"Delete_Account": "Poista tili",
"Delete_Account_confirm": "Kyllä, poista",
"move": "siirrä",
"deleting_room": "poistaa huonetta",
"description": "kuvaus",
"Description": "Kuvaus",
"Desktop_Options": "Työpöytäasetukset",
"Desktop_Notifications": "Työpöytäilmoitukset",
"Desktop_Alert_info": "Nämä ilmoitukset näkyvät työpöydällä",
"Directory": "Hakemisto",
"Direct_Messages": "Suorat viestit",
"Disable_notifications": "Poista käytöstä ilmoitukset",
"Discussions": "Keskustelut",
"Discussion_Desc": "Pysy selvillä tapahtumista! Luomalla keskustelun luot valitsemasi kanavan alikanavan, ja keskustelut liitetään yhteen.",
"Discussion_name": "Keskustelun nimi",
"Done": "Valmis",
"Dont_Have_An_Account": "Eikö sinulla ole tiliä?",
"Do_you_have_an_account": "Onko sinulla tili?",
"Do_you_have_a_certificate": "Onko sinulla varmenne?",
"Do_you_really_want_to_key_this_room_question_mark": "Haluatko varmasti {{key}} tämän huoneen?",
"E2E_Encryption": "Täysi salaus",
"E2E_How_It_Works_info1": "Nyt voit luoda salattuja yksityisiä ryhmiä ja suoria viestejä. Voit myös muuttaa nykyisiä yksityisiä ryhmiä tai suoria viestejä salatuksi.",
"E2E_How_It_Works_info2": "Tämä on *täysi salaus*, joten viestiesi salaamiseen ja salauksen purkuun käytettävää avainta ei tallenneta palvelimeen. Siksi *sinun on tallennettava tämä salasana turvalliseen paikkaan*, johon pääset myöhemmin tarvittaessa.",
"E2E_How_It_Works_info3": "Jos jatkat, täyden salauksen salasana luodaan automaattisesti.",
"E2E_How_It_Works_info4": "Voit myös määrittää uuden salasanan salausavaimellesi milloin tahansa missä tahansa selaimessa, jossa olet antanut nykyisen täyden salauksen salasanan.",
"edit": "muokkaa",
"edited": "muokkasi",
"Edit": "Muokkaa",
"Edit_Status": "Muokkaa tilaa",
"Edit_Invite": "Muokkaa kutsua",
"End_to_end_encrypted_room": "Täysin salattu huone",
"end_to_end_encryption": "täysi salaus",
"Email_Notification_Mode_All": "Jokainen maininta/SV",
"Email_Notification_Mode_Disabled": "Ei käytössä",
"Email_or_password_field_is_empty": "Sähköposti- tai salasanakenttä on tyhjä",
"Email": "Sähköposti",
"email": "sähköposti",
"Empty_title": "Tyhjä otsikko",
"Enable_Auto_Translate": "Ota käyttöön automaattikäännös",
"Enable_notifications": "Ota käyttöön ilmoitukset",
"Encrypted": "Salattu",
"Encrypted_message": "Salattu viesti",
"Enter_Your_E2E_Password": "Anna täyden salauksen salasanasi",
"Enter_Your_Encryption_Password_desc1": "Näin pääset käyttämään salattuja yksityisiä ryhmiäsi ja suoria viestejäsi.",
"Enter_Your_Encryption_Password_desc2": "Sinun on annettava viestien salauksen / salauksen purkamisen salasana kaikkialla, missä käytät keskustelua.",
"Encryption_error_title": "Salauksen salasanasi näyttää väärältä",
"Encryption_error_desc": "Tuotavan salausavaimesi salausta ei voitu purkaa.",
"Everyone_can_access_this_channel": "Kaikilla on pääsy tälle kanavalle",
"Everyone_can_access_this_team": "Kaikilla on pääsy tähän tiimiin",
"Error_uploading": "Virhe ladattaessa",
"Expiration_Days": "Vanheneminen (päivää)",
"Favorites": "Suosikit",
"Files": "Tiedostot",
"File_description": "Tiedoston kuvaus",
"File_name": "Tiedostonimi",
"Finish_recording": "Lopeta tallennus",
"Following_thread": "Seurataan ketjua",
"For_your_security_you_must_enter_your_current_password_to_continue": "Jatka antamalla nykyinen salasanasi turvallisuussyistä",
"Forgot_password_If_this_email_is_registered": "Jos tämä sähköpostiosoite on rekisteröity, lähetämme salasanan nollausohjeet. Jos et saa sähköpostia pian, palaa ja yritä uudelleen.",
"Forgot_password": "Unohditko salasanasi?",
"Forgot_Password": "Unohtunut salasana",
"Forward": "Välitä",
"Forward_Chat": "Välitä keskustelu",
"Forward_to_department": "Välitä osastolle",
"Forward_to_user": "Välitä käyttäjälle",
"Full_table": "Näytä koko taulukko napsauttamalla",
"Generate_New_Link": "Luo uusi linkki",
"Get_link": "Hanki linkki",
"User_joined_the_channel": "liittyi kanavalle",
"User_joined_the_conversation": "liittyi keskusteluun",
"User_joined_the_team": "liittyi tähän tiimiin",
"User_left_this_channel": "poistui kanavalta",
"Has_left_the_team": "on poistunut tiimistä",
"Hide_System_Messages": "Piilota järjestelmäilmoitukset",
"Hide_type_messages": "Piilota \"{{type}}\"-viestit",
"How_It_Works": "Toimintatapa",
"Message_HideType_uj": "Käyttäjän liittyminen",
"Message_HideType_ul": "Käyttäjän poistuminen",
"Message_HideType_ru": "Käyttäjä poistettu",
"Message_HideType_au": "Käyttäjä lisätty",
"Message_HideType_mute_unmute": "Käyttäjä mykistetty / mykistys poistettu",
"Message_HideType_r": "Huoneen nimi vaihdettu",
"Message_HideType_ut": "Käyttäjä liittyi keskusteluun",
"Message_HideType_wm": "Tervetuloa",
"Message_HideType_rm": "Viesti poistettu",
"Message_HideType_subscription_role_added": "Annettiin rooli",
"Message_HideType_subscription_role_removed": "Rooli ei enää määritetty",
"Message_HideType_room_archived": "Huone arkistoitu",
"Message_HideType_room_unarchived": "Huone palautettu arkistosta",
"I_Saved_My_E2E_Password": "Tallensin täyden salauksen salasanani",
"IP": "IP",
"In_app": "Sovelluksessa",
"In_App_And_Desktop": "Sovelluksessa ja työpöydällä",
"In_App_and_Desktop_Alert_info": "Näyttää bannerin näytön yläreunassa, kun sovellus on avoinna, ja näyttää ilmoituksen työpöydällä",
"Invisible": "Näkymätön",
"Invite": "Kutsu",
"is_a_valid_RocketChat_instance": "on kelvollinen Rocket.Chat-esiintymä",
"is_not_a_valid_RocketChat_instance": "ei ole kelvollinen Rocket.Chat-esiintymä",
"is_typing": "kirjoittaa",
"Invalid_or_expired_invite_token": "Virheellinen tai vanhentunut kutsutunnus",
"Invalid_server_version": "Palvelin, johon yrität muodostaa yhteyttä, käyttää versiota, jota sovellus ei enää tue: {{currentVersion}}.\n\nEdellytämme versiota {{minVersion}}",
"Invite_Link": "Kutsulinkki",
"Invite_users": "Kutsu käyttäjiä",
"Join": "Liity",
"Join_Code": "Liittymiskoodi",
"Insert_Join_Code": "Lisää liittymiskoodi",
"Join_our_open_workspace": "Liity avoimeen työtilaamme",
"Join_your_workspace": "Liity työtilaasi",
"Just_invited_people_can_access_this_channel": "Vain kutsutut ihmiset voivat käyttää tätä kanavaa",
"Just_invited_people_can_access_this_team": "Vain kutsutut ihmiset voivat käyttää tätä tiimiä",
"Language": "Kieli",
"last_message": "viimeinen viesti",
"Leave_channel": "Poistu kanavalta",
"leaving_room": "poistuu huoneesta",
"Leave": "Poistu",
"leave": "poistu",
"Legal": "Oikeudellinen",
"Light": "Vaalea",
"License": "Käyttöoikeus",
"Livechat": "Livechat",
"Livechat_edit": "Livechat-muokkaus",
"Livechat_transfer_return_to_the_queue": "palautti keskustelun jonoon",
"Login": "Kirjaudu",
"Login_error": "Tunnistetietojasi ei hyväksytty! Yritä uudelleen.",
"Login_with": "Kirjaudu:",
"Logging_out": "Kirjaudutaan ulos.",
"Logout": "Kirjaudu ulos",
"Max_number_of_uses": "Käyttökertojen enimmäismäärä",
"Max_number_of_users_allowed_is_number": "Suurin sallittu käyttäjämäärä on {{maxUsers}}",
"members": "jäsentä",
"Members": "Jäsenet",
"Mentioned_Messages": "Mainitut viestit",
"mentioned": "mainitsi",
"Mentions": "Maininnat",
"Message_accessibility": "Viesti käyttäjältä {{user}} klo {{time}}: {{message}}",
"Message_actions": "Viestitoimet",
"Message_pinned": "Viesti kiinnitetty",
"Message_removed": "viesti poistettu",
"Message_starred": "Viesti merkitty tähdellä",
"Message_unstarred": "Viestin tähtimerkintä poistettu",
"message": "viesti",
"messages": "viestiä",
"Message": "Viesti",
"Messages": "Viestit",
"Message_Reported": "Viestistä ilmoitettu",
"Microphone_Permission_Message": "Rocket.Chat tarvitsee mikrofonin käyttöoikeuden, jotta voit lähettää ääniviestejä.",
"Microphone_Permission": "Mikrofonin oikeus",
"Mute": "Mykistä",
"muted": "mykistetty",
"My_servers": "Omat palvelimet",
"N_people_reacted": "{{n}} henkilöä reagoi",
"N_users": "{{n}} käyttäjää",
"N_channels": "{{n}} kanavaa",
"Name": "Nimi",
"Never": "Ei koskaan",
"New_chat_transfer": "Uuden keskustelun siirto: {{agent}} palautti keskustelun jonoon",
"New_Message": "Uusi viesti",
"New_Password": "Uusi salasana",
"New_Server": "Uusi palvelin",
"Next": "Seuraava",
"No_files": "Ei tiedostoja",
"No_limit": "Ei rajoitusta",
"No_mentioned_messages": "Ei mainittuja viestejä",
"No_pinned_messages": "Ei kiinnitettyjä viestejä",
"No_results_found": "Tuloksia ei löydy",
"No_members_found": "Jäseniä ei löydy",
"No_starred_messages": "Ei tähdellä merkittyjä viestejä",
"No_thread_messages": "Ei ketjuviestejä",
"No_label_provided": "Ei merkintää {{label}} provided.",
"No_Message": "Ei viestiä",
"No_messages_yet": "Ei vielä viestejä",
"No_Reactions": "Ei reaktioita",
"No_Read_Receipts": "Ei lukukuittauksia",
"Not_logged": "Ei kirjautunut",
"Not_RC_Server": "Tämä ei ole Rocket.Chat-palvelin.\n{{contact}}",
"Nothing": "Ei mitään",
"Nothing_to_save": "Ei tallennettavaa!",
"Notify_active_in_this_room": "Ilmoita tämän huoneen aktiivisille käyttäjille",
"Notify_all_in_this_room": "Ilmoita kaikille tässä huoneessa",
"Notifications": "Ilmoitukset",
"Notification_Duration": "Ilmoituksen kesto",
"Notification_Preferences": "Ilmoitusasetukset",
"No_available_agents_to_transfer": "Ei vapaita agentteja siirrettäviksi",
"Offline": "Offline",
"Oops": "Oho!",
"Omnichannel": "Omnichannel",
"Omnichannel_enable_alert": "Et ole saavutettavissa Omnichannelissa. Haluatko olla saavutettavissa?",
"Onboarding_description": "Työtila on tiimisi tai organisaatiosi tila yhteistyölle. Pyydä työtilan järjestelmänvalvojalta osoite, jotta voit liittyä työtilaan tai luoda sellaisen tiimillesi.",
"Onboarding_join_workspace": "Liity työtilaan",
"Onboarding_subtitle": "Enemmän kuin ryhmäyhteistyötä",
"Onboarding_title": "Tervetuloa Rocket.Chatiin",
"Onboarding_join_open_description": "Liity avoimeen työtilaamme keskustelemaan Rocket.Chat-tiimin ja -yhteisön kanssa.",
"Onboarding_agree_terms": "Jatkamalla hyväksyt Rocket.Chatin",
"Onboarding_less_options": "Vähemmän vaihtoehtoja",
"Onboarding_more_options": "Enemmän vaihtoehtoja",
"Online": "Online",
"Only_authorized_users_can_write_new_messages": "Vain valtuutetut käyttäjät voivat kirjoittaa uusia viestejä",
"Open_emoji_selector": "Avaa emojien valitsin",
"Open_Source_Communication": "Avoimen lähdekoodin viestintä",
"Open_your_authentication_app_and_enter_the_code": "Avaa todennussovellus ja anna koodi.",
"OR": "TAI",
"OS": "Käyttöjärjestelmä",
"Overwrites_the_server_configuration_and_use_room_config": "Korvaa palvelimen määritykset ja käyttää huoneen määrityksiä",
"Password": "Salasana",
"Parent_channel_or_group": "Pääkanava tai -ryhmä",
"Permalink_copied_to_clipboard": "Pysyvä linkki kopioitu leikepöydälle!",
"Phone": "Puhelin",
"Pin": "Kiinnitä",
"Pinned_Messages": "Kiinnitetyt viestit",
"pinned": "kiinnitetty",
"Pinned": "Kiinnitetty",
"Please_add_a_comment": "Lisää kommentti",
"Please_enter_your_password": "Anna salasana",
"Please_wait": "Odota.",
"Preferences": "Asetukset",
"Preferences_saved": "Asetukset tallennettu!",
"Privacy_Policy": "Tietosuojakäytäntö",
"Private": "Yksityinen",
"Processing": "Käsitellään...",
"Profile_saved_successfully": "Profiili on tallennettu!",
"Profile": "Profiili",
"Public_Channel": "Julkinen kanava",
"Public": "Julkinen",
"Push_Notifications": "Push-ilmoitukset",
"Push_Notifications_Alert_Info": "Saat näitä ilmoituksia, kun sovellus ei ole auki",
"Quote": "Lainaus",
"Reactions_are_disabled": "Reaktiot eivät ole käytössä",
"Reactions_are_enabled": "Reaktiot ovat käytössä",
"Reactions": "Reaktiot",
"Read_External_Permission_Message": "Rocket.Chat tarvitsee pääsyn laitteesi valokuviin, mediaan ja tiedostoihin",
"Read_External_Permission": "Median lukuoikeus",
"Read_Only": "Vain luku",
"Read_Receipt": "Lukukuittaus",
"Receive_Group_Mentions": "Vastaanota ryhmämainintoja",
"Receive_Group_Mentions_Info": "Vastaanota @all- ja @here-maininnat",
"Register": "Rekisteröi",
"Repeat_Password": "Toista salasana",
"Replied_on": "Vastasi:",
"replies": "vastausta",
"reply": "vastaus",
"Reply": "Vastaa",
"Report": "Ilmoita",
"Receive_Notification": "Vastaanota ilmoitus",
"Receive_notifications_from": "Vastaanota ilmoitukset käyttäjältä {{name}}",
"Resend": "Lähetä uudelleen",
"Reset_password": "Nollaa salasana",
"resetting_password": "nollaa salasanaa",
"RESET": "NOLLAA",
"Return_to_waiting_line": "Palaa jonoon",
"Review_app_title": "Pidätkö tästä sovelluksesta?",
"Review_app_desc": "Anna meille 5 tähteä: {{store}}",
"Review_app_yes": "Selvä!",
"Review_app_no": "Ei",
"Review_app_later": "Ehkä myöhemmin",
"Review_app_unable_store": "Ei voida avata: {{store}}",
"Review_this_app": "Arvioi tämä sovellus",
"Remove": "Poista",
"remove": "poista",
"Roles": "Roolit",
"Room_actions": "Huoneen toimet",
"Room_changed_announcement": "Huoneen ilmoitukseksi vaihdettu: {{announcement}} käyttäjä: {{userBy}}",
"room_avatar_changed": "vaihtoi huoneen avatarin",
"Room_changed_description": "Huoneen kuvaukseksi vaihdettu: {{description}} käyttäjä: {{userBy}}",
"changed_room_description": "vaihtoi huoneen kuvaukseksi: {{description}}",
"changed_room_announcement": "vaihtoi huoneen ilmoitukseksi: {{announcement}}",
"room_changed_type": "vaihtoi huoneen tyypiksi {{type}}",
"room_changed_topic_to": "vaihtoi huoneen aiheeksi: {{topic}}",
"Room_Files": "Huoneen tiedostot",
"Room_Info_Edit": "Huoneen tietojen muokkaus",
"Room_Info": "Huoneen tiedot",
"Room_Members": "Huoneen jäsenet",
"Room_name_changed_to": "vaihtoi huoneen nimeksi: {{name}}",
"room_disallowed_reactions": "kielsi reaktiot",
"room_allowed_reactions": "salli reaktiot",
"room_removed_read_only_permission": "poisti vain luku -oikeuden",
"room_set_read_only_permission": "asetti huoneen vain luku -tilaan",
"SAVE": "TALLENNA",
"Save_Changes": "Tallenna muutokset",
"Save": "Tallenna",
"Saved": "Tallennettu",
"saving_preferences": "tallentaa asetuksia",
"saving_profile": "tallentaa profiilia",
"saving_settings": "tallentaa asetuksia",
"saved_to_gallery": "Tallennettu galleriaan",
"Save_Your_E2E_Password": "Tallenna täyen salauksen salasanasi",
"Save_Your_Encryption_Password": "Tallenna salauksen salasanasi",
"Save_Your_Encryption_Password_warning": "Tätä salasanaa ei tallenneta mihinkään, joten tallenna se huolellisesti muualle.",
"Save_Your_Encryption_Password_info": "Huomioi, että jos kadotat salasanasi, sitä ei voi palauttaa etkä pääse enää viesteihisi.",
"Search_Messages": "Hae viestejä",
"Search": "Haku",
"Search_by": "Hakuperuste",
"Search_emoji": "Hae emojia",
"Search_global_users": "Hae käyttäjiä kaikkialta",
"Search_global_users_description": "Jos otat tämän käyttöön, voit etsiä käyttäjiä muista yrityksistä tai palvelimista.",
"Seconds": "{{second}} sekuntia",
"Security_and_privacy": "Turvallisuus ja yksityisyys",
"Select_Avatar": "Valitse avatar",
"Select_Server": "Valitse palvelin",
"Select_Users": "Valitse käyttäjät",
"Select_a_Channel": "Valitse kanava",
"Select_a_Department": "Valitse osasto",
"Select_an_option": "Valitse vaihtoehto",
"Select_a_User": "Valitse käyttäjä",
"Send": "Lähetä",
"Send_audio_message": "Lähetä ääniviesti",
"Send_crash_report": "Lähetä kaatumisraportti",
"Send_message": "Lähetä viesti",
"Send_me_the_code_again": "Lähetä koodi uudelleen",
"Send_to": "Lähetä kohteeseen...",
"Sending_to": "Lähetetään kohteeseen",
"Sent_an_attachment": "Lähetettiin liite",
"Server": "Palvelin",
"Servers": "Palvelimet",
"Server_version": "Palvelimen versio: {{version}}",
"Set_username_subtitle": "Käyttäjätunnuksen avulla muut voivat mainita sinut viesteissä",
"Set_custom_status": "Aseta mukautettu tila",
"Set_status": "Aseta tila",
"Status_saved_successfully": "Tila on tallennettu!",
"Settings": "Asetukset",
"Settings_succesfully_changed": "Asetukset on muutettu!",
"Share": "Jaa",
"Share_Link": "Jaa linkki",
"Share_this_app": "Jaa tämä sovellus",
"Show_more": "Näytä lisää...",
"Sign_in_your_server": "Kirjaudu palvelimeesi",
"Sign_Up": "Rekisteröidy",
"Some_field_is_invalid_or_empty": "Jokin kenttä on virheellinen tai tyhjä",
"Sound": "Ääni",
"Star_room": "Tähdellinen huone",
"Star": "Tähti",
"Starred_Messages": "Tähdellä merkityt viestit",
"starred": "tähdellä merkittyä",
"Starred": "Tähdellä merkityt",
"Start_of_conversation": "Keskustelun alku",
"Start_a_Discussion": "Aloita keskustelu",
"Started_discussion": "Aloitettiin keskustelu:",
"Started_call": "Puhelun aloitti {{userBy}}",
"Submit": "Lähetä",
"Table": "Taulukko",
"Tags": "Tunnisteet",
"Take_a_photo": "Ota valokuva",
"Take_a_video": "Kuvaa video",
"Take_it": "Ota!",
"tap_to_change_status": "vaihda tilaa napauttamalla",
"Tap_to_view_servers_list": "Näytä palvelinluettelo napauttamalla",
"Terms_of_Service": "Käyttöehdot",
"Theme": "Teema",
"The_user_wont_be_able_to_type_in_roomName": "Käyttäjä ei voi kirjoittaa huoneessa {{roomName}}",
"The_user_will_be_able_to_type_in_roomName": "Käyttäjä voi kirjoittaa huoneessa {{roomName}}",
"There_was_an_error_while_action": "Virhe toiminnon {{action}} aikana!",
"This_room_is_blocked": "Huone on estetty",
"This_room_is_read_only": "Huone on vain luku -tilassa",
"Thread": "Ketju",
"Threads": "Ketjut",
"Timezone": "Aikavyöhyke",
"To": "Osoitteeseen",
"topic": "aihe",
"Topic": "Aihe",
"Translate": "Käännä",
"Try_again": "Yritä uudelleen",
"Two_Factor_Authentication": "Kaksivaiheinen tunnistautuminen",
"Type_the_channel_name_here": "Kirjoita kanavan nimi tähän",
"unarchive": "palauta arkistosta",
"UNARCHIVE": "PALAUTA ARKISTOSTA",
"Unblock_user": "Poista käyttäjän esto",
"Unfollowed_thread": "Lopetettiin ketjun seuraaminen",
"Unmute": "Mykistys poistettu",
"unmuted": "poisti mykistyksen",
"Unpin": "Poista kiinnitys",
"unread_messages": "lukematonta",
"Unread": "Lukemattomat",
"Unread_on_top": "Lukemattomat ylinnä",
"Unstar": "Poista tähti",
"Unsupported_system_message": "Ei tuettu järjestelmäilmoitus",
"Updating": "Päivitetään...",
"Uploading": "Ladataan",
"FileUpload_Error": "Tiedoston latausvirhe",
"Upload_in_progress": "Lataus käynnissä",
"Upload_file_question_mark": "Ladataanko tiedosto?",
"User": "Käyttäjä",
"Users": "Käyttäjät",
"User_added_to": "lisäsi käyttäjän {{userAdded}}",
"User_Info": "Käyttäjän tiedot",
"User_has_been_key": "Käyttäjä on {{key}}",
"User_is_no_longer_role_by_": "{{user}} ei ole enää {{role}}, muutti {{userBy}}",
"User_has_been_muted": "mykisti käyttäjän {{userMuted}}",
"User_has_been_removed": "poisti käyttäjän {{userRemoved}}",
"User_sent_an_attachment": "{{user}} lähetti liitteen",
"User_has_been_unmuted": "poisti käyttäjän {{userUnmuted}} mykistyksen",
"Defined_user_as_role": "määritti käyttäjän {{user}} rooliin {{role}}",
"Removed_user_as_role": "poisti käyttäjän {{user}} roolista {{role}}",
"Username_is_empty": "Käyttäjätunnus on tyhjä",
"Username": "Käyttäjätunnus",
"Username_or_email": "Sähköpostiosoite tai käyttäjätunnus",
"Uses_server_configuration": "Käyttää palvelimen määrityksiä",
"Validating": "Vahvistetaan",
"Registration_Succeeded": "Rekisteröinti onnistui!",
"Verify": "Varmista",
"Verify_email_title": "Rekisteröinti onnistui!",
"Verify_email_desc": "Lähetimme rekisteröitymisvahvistuksen sähköpostiisi. Jos et saa sähköpostia pian, yritä uudelleen.",
"Verify_your_email_for_the_code_we_sent": "Vahvista sähköpostiosoitteesi lähettämällämme koodilla",
"Video_call": "Videopuhelu",
"View_Original": "Näytä alkuperäinen",
"Voice_call": "Äänipuhelu",
"Waiting_for_network": "Odotetaan verkkoa...",
"Websocket_disabled": "Websocket ei ole käytössä tässä palvelimessa.\n{{contact}}",
"Welcome": "Tervetuloa",
"What_are_you_doing_right_now": "Mitä teet juuri nyt?",
"Whats_your_2fa": "Mikä on 2FA-koodisi?",
"Without_Servers": "Ilman palvelimia",
"Workspaces": "Työtilat",
"Would_you_like_to_return_the_inquiry": "Haluatko palauttaa kyselyn?",
"Write_External_Permission_Message": "Rocket.Chat tarvitsee gallerian käyttöoikeuden, jotta voit tallentaa kuvia.",
"Write_External_Permission": "Gallerian käyttöoikeus",
"Yes": "Kyllä",
"Yes_action_it": "Kyllä, {{action}} se!",
"Yesterday": "Eilen",
"You_are_in_preview_mode": "Olet esikatselutilassa",
"You_are_offline": "Olet offline-tilassa",
"You_can_search_using_RegExp_eg": "Voit käyttää säännöllisiä lausekkeita, esim. `/^teksti$/i`",
"You_colon": "Sinä:",
"you_were_mentioned": "sinut mainittiin",
"You_were_removed_from_channel": "Sinut on poistettu kanavalta {{channel}}",
"you": "sinä",
"You": "Sinä",
"Logged_out_by_server": "Palvelin on kirjannut sinut ulos. Kirjaudu uudelleen.",
"Token_expired": "Istuntosi on vanhentunut. Kirjaudu uudelleen.",
"You_need_to_access_at_least_one_RocketChat_server_to_share_something": "Tarvitset pääsyn vähintään Rocket.Chat-palvelimeen jakamista varten.",
"You_need_to_verifiy_your_email_address_to_get_notications": "Vahvista sähköpostiosoitteesi, jotta saat ilmoituksia",
"Your_certificate": "Varmenteesi",
"Your_invite_link_will_expire_after__usesLeft__uses": "Kutsulinkkisi vanhenee {{usesLeft}} käyttökerran jälkeen.",
"Your_invite_link_will_expire_on__date__or_after__usesLeft__uses": "Kutsulinkkisi vanhenee {{date}} tai {{usesLeft}} käyttökerran jälkeen.",
"Your_invite_link_will_expire_on__date__": "Kutsulinkkisi vanhenee {{date}}.",
"Your_invite_link_will_never_expire": "Kutsulinkkisi ei vanhene.",
"Your_workspace": "Työtilasi",
"Your_password_is": "Salasanasi on",
"Version_no": "Versio: {{version}}",
"You_will_not_be_able_to_recover_this_message": "Tätä viestiä ei voi palauttaa!",
"You_will_unset_a_certificate_for_this_server": "Poistat tämän palvelimen varmenteen asetuksen",
"Change_Language": "Vaihda kieltä",
"Crash_report_disclaimer": "Keskustelujesi sisältöä ei seurata. Kaatumisraportti ja analytiikkatapahtumat sisältävät vain olennaiset tiedot, jotta pystymme tunnistamaan ja korjaamaan ongelmat.",
"Type_message": "Kirjoita viesti",
"Room_search": "Huonehaku",
"Room_selection": "Huoneen valinta 1...9",
"Next_room": "Seuraava huone",
"Previous_room": "Edellinen huone",
"New_room": "Uusi huone",
"Upload_room": "Lataa huoneeseen",
"Search_messages": "Hae viestejä",
"Scroll_messages": "Vieritä viestejä",
"Reply_latest": "Vastaa uusimpaan",
"Reply_in_Thread": "Vastaa viestiketjussa",
"Server_selection": "Palvelimen valinta",
"Server_selection_numbers": "Palvelimen valinta 1...9",
"Add_server": "Lisää palvelin",
"New_line": "Uusi rivi",
"You_will_be_logged_out_of_this_application": "Sinut kirjataan ulos tästä sovelluksesta.",
"Clear": "Tyhjennä",
"This_will_clear_all_your_offline_data": "Tämä poistaa kaikki offline-tietosi.",
"This_will_remove_all_data_from_this_server": "Tämä poistaa kaikki tiedot tästä palvelimesta.",
"Mark_unread": "Merkitse lukemattomaksi",
"Wait_activation_warning": "Ennen kuin voit kirjautua, järjestelmänvalvojan on aktivoitava tilisi manuaalisesti.",
"Screen_lock": "Näytön lukitus",
"Local_authentication_biometry_title": "Todenna",
"Local_authentication_biometry_fallback": "Käytä salasanaa",
"Local_authentication_unlock_option": "Avaa lukitus salasanalla",
"Local_authentication_change_passcode": "Vaihda salasana",
"Local_authentication_info": "Huomautus: jos unohdat salasanan, sinun on poistettava sovellus ja asennettava se uudelleen.",
"Local_authentication_facial_recognition": "kasvojentunnistus",
"Local_authentication_fingerprint": "sormenjälki",
"Local_authentication_unlock_with_label": "Avaa lukitus: {{label}}",
"Local_authentication_auto_lock_60": "1 minuutin kuluttua",
"Local_authentication_auto_lock_300": "5 minuutin kuluttua",
"Local_authentication_auto_lock_900": "15 minuutin kuluttua",
"Local_authentication_auto_lock_1800": "30 minuutin kuluttua",
"Local_authentication_auto_lock_3600": "1 tunnin kuluttua",
"Passcode_enter_title": "Anna salasanasi",
"Passcode_choose_title": "Valitse uusi salasana",
"Passcode_choose_confirm_title": "Vahvista uusi salasana",
"Passcode_choose_error": "Salasanat eivät täsmää. Yritä uudelleen.",
"Passcode_choose_force_set": "Järjestelmänvalvoja edellyttää salasanaa",
"Passcode_app_locked_title": "Sovellus lukittu",
"Passcode_app_locked_subtitle": "Yritä uudelleen {{timeLeft}} sekunnin kuluttua",
"After_seconds_set_by_admin": "{{seconds}} sekunnin kuluttua (järjestelmänvalvojan asettama)",
"Dont_activate": "Älä aktivoi nyt",
"Queued_chats": "Jonossa olevat keskustelut",
"Logout_from_other_logged_in_locations": "Kirjaa ulos muista sijainneista",
"You_will_be_logged_out_from_other_locations": "Sinut kirjataan ulos muista sijainneista.",
"Logged_out_of_other_clients_successfully": "Kirjattu ulos muissa asiakkaissa",
"Logout_failed": "Uloskirjaus epäonnistui!",
"Log_analytics_events": "Kirjaa analytiikkatapahtumat",
"E2E_encryption_change_password_title": "Vaihda salauksen salasana",
"E2E_encryption_change_password_description": "Nyt voit luoda salattuja yksityisiä ryhmiä ja suoria viestejä. Voit myös muuttaa nykyisiä yksityisiä ryhmiä tai suoria viestejä salatuksi. \nTämä on täysi salaus, joten viestiesi salaamiseen ja salaukseen purkuun käytettävää avainta ei tallenneta palvelimeen. Siksi sinun on tallennettava salasanasi turvalliseen paikkaan. Sinun on annettava se muissa laitteissa, joissa haluat käyttää täyttä salausta.",
"E2E_encryption_change_password_error": "Virhe vaihdettaessa täyden salauksen avaimen salasanaa!",
"E2E_encryption_change_password_success": "Täyden salauksen avaimen salasana on vaihdettu!",
"E2E_encryption_change_password_message": "Varmista, että olet tallentanut sen huolellisesti muualle.",
"E2E_encryption_change_password_confirmation": "Kyllä, vaihda se",
"E2E_encryption_reset_title": "Nollaa täyden salauksen avain",
"E2E_encryption_reset_description": "Tämä asetus poistaa nykyisen täyden salauksen avaimesi ja kirjaa sinut ulos. \nKun kirjaudut uudelleen sisään, Rocket.Chat luo sinulle uuden avaimen ja palauttaa pääsysi kaikkiin salattuihin huoneisiin, joissa on aktiivisia jäseniä. \nTäyden salauksen luonteen vuoksi Rocket.Chat ei pysty palauttamaan pääsyä salattuihin huoneisiin, joissa ei ole aktiivisia jäseniä.",
"E2E_encryption_reset_button": "Nollaa täyden salauksen avain",
"E2E_encryption_reset_error": "Virhe nollattaessa täyden salauksen avainta!",
"E2E_encryption_reset_message": "Sinut kirjataan ulos.",
"E2E_encryption_reset_confirmation": "Kyllä, nollaa se",
"Following": "Seurataan",
"Threads_displaying_all": "Näytetään kaikki",
"Threads_displaying_following": "Näytetään seuratut",
"Threads_displaying_unread": "Näytetään lukemattomat",
"No_threads": "Ei ketjuja",
"No_threads_following": "Et seuraa ketjuja",
"No_threads_unread": "Ei lukemattomia ketjuja",
"Messagebox_Send_to_channel": "Lähetä kanavalle",
"Leader": "Johtaja",
"Moderator": "Moderaattori",
"Owner": "Omistaja",
"Remove_from_room": "Poista huoneesta",
"Ignore": "Ohita",
"Unignore": "Kumoa ohitus",
"User_has_been_ignored": "Käyttäjä on ohitettu",
"User_has_been_unignored": "Käyttäjää ei enää ohiteta",
"User_has_been_removed_from_s": "Käyttäjä on poistettu kohteesta {{s}}",
"User__username__is_now_a_leader_of__room_name_": "Käyttäjä {{username}} on nyt huoneen {{room_name}} johtaja",
"User__username__is_now_a_moderator_of__room_name_": "Käyttäjä {{username}} on nyt huoneen {{room_name}} moderaattori",
"User__username__is_now_a_owner_of__room_name_": "Käyttäjä {{username}} on nyt huoneen {{room_name}} omistaja",
"User__username__removed_from__room_name__leaders": "Käyttäjä {{username}} poistettiin huoneen {{room_name}} johtajista",
"User__username__removed_from__room_name__moderators": "Käyttäjä {{username}} poistettiin huoneen {{room_name}} moderaattoreista",
"User__username__removed_from__room_name__owners": "Käyttäjä {{username}} poistettiin huoneen {{room_name}} omistajista",
"The_user_will_be_removed_from_s": "Käyttäjä poistetaan kohteesta {{s}}",
"Yes_remove_user": "Kyllä, poista käyttäjä!",
"Direct_message": "Suora viesti",
"Message_Ignored": "Viesti ohitettu. Näytä se napauttamalla.",
"Enter_workspace_URL": "Anna työtilan URL-osoite",
"Workspace_URL_Example": "Esim. yrityksesi.rocket.chat",
"Enabled_E2E_Encryption_for_this_room": "otti täyden salauksen käyttöön tässä huoneessa",
"Disabled_E2E_Encryption_for_this_room": "poisti täyden salauksen käytöstä tässä huoneessa",
"Teams": "Tiimit",
"No_team_channels_found": "Kanavia ei löydy",
"Team_not_found": "Tiimiä ei löydy",
"Create_Team": "Luo tiimi",
"Team_Name": "Tiimin nimi",
"creating_team": "luo tiimiä",
"team-name-already-exists": "Tiimi tällä nimellä on jo olemassa",
"Add_Channel_to_Team": "Lisää kanava tiimille",
"Left_The_Team_Successfully": "Poistuttiin tiimistä",
"Create_New": "Luo uusi",
"Add_Existing": "Lisää olemassa oleva",
"Add_Existing_Channel": "Lisää olemassa oleva kanava",
"Remove_from_Team": "Poista tiimistä",
"Auto-join": "Automaattinen liittyminen",
"Remove_Team_Room_Warning": "Haluatko poistaa tämän kanavan tiimiltä? Kanava siirretään takaisin työtilaan",
"Confirmation": "Vahvistus",
"invalid-room": "Virheellinen huone",
"You_are_leaving_the_team": "Poistut tiimistä {{team}}",
"Leave_Team": "Poistu tiimistä",
"Select_Team": "Valitse tiimi",
"Select_Team_Channels": "Valitse tiimin kanavat, joilta haluat poistua.",
"Cannot_leave": "Ei voi poistua",
"Cannot_remove": "Ei voi poistaa",
"Cannot_delete": "Ei voi poistaa",
"Last_owner_team_room": "Olet tämän kanavan viimeinen omistaja. Kun lähdet tiimistä, kanava säilyy tiimin sisällä, mutta hallinnoit sitä ulkopuolelta.",
"last-owner-can-not-be-removed": "Viimeistä omistajaa ei voi poistaa",
"Remove_User_Teams": "Valitse kanavat, joilta haluat poistaa käyttäjän.",
"Deleting_account": "Poistetaan tiliä",
"Delete_my_account": "Poista tilini",
"Delete_Team": "Poista tiimi",
"Select_channels_to_delete": "Tätä ei voi kumota. Kun poistat tiimin, kaikki keskustelusisältö ja määritykset poistetaan. \n\nValitse poistettavat kanavat. Säilyttämäsi kanavat ovat käytettävissä työtilassasi. Huomioi, että julkiset kanavat ovat edelleen julkisia ja näkyvissä kaikille.",
"You_are_deleting_the_team": "Olet poistamassa tämän tiimin.",
"Removing_user_from_this_team": "Poistat käyttäjää {{user}} tästä tiimistä",
"Remove_User_Team_Channels": "Valitse kanavat, joilta haluat poistaa käyttäjän.",
"Remove_Member": "Poista jäsen",
"leaving_team": "poistuu tiimistä",
"removing_team": "poistaa tiimistä",
"moving_channel_to_team": "siirtää kanavaa tiimille",
"deleting_team": "poistaa tiimiä",
"member-does-not-exist": "Jäsentä ei ole",
"Convert": "Muunna",
"Convert_to_Team": "Muunna tiimiksi",
"Convert_to_Team_Warning": "Muunnat tätä kanavaa tiimiksi. Kaikki jäsenet säilytetään.",
"Move_to_Team": "Siirrä tiimiin",
"Move_Channel_Paragraph": "Kanavan siirtäminen tiimiin tarkoittaa, että tämä kanava lisätään tiimin kontekstiin. Kaikki kanavan jäsenet, jotka eivät ole vastaavan tiimin jäseniä, pääsevät silti edelleen tälle kanavalle, mutta heitä ei lisätä tiimin jäseniksi. \n\nKanavan omistajat hoitavat edelleen kaiken kanavan hallinnan.\n\nTiimin jäsenillä ja jopa tiimin omistajilla, jotka eivät ole tämän kanavan jäseniä, ei ole pääsyä kanavan sisältöön. \n\nHuomioi, että tiimin omistaja voi poistaa jäseniä kanavalta.",
"Move_to_Team_Warning": "Nyt kun olet lukenut edellä olevat tätä toimintaa koskevat ohjeet, haluatko silti siirtää tämän kanavan valitulle tiimille?",
"Load_More": "Lataa lisää",
"Load_Newer": "Lataa uudemmat",
"Load_Older": "Lataa vanhemmat",
"room-name-already-exists": "Huoneen nimi on jo olemassa",
"error-team-creation": "Tiimin luontivirhe",
"unauthorized": "Valtuuttamaton",
"Left_The_Room_Successfully": "Poistuttiin huoneesta",
"Deleted_The_Team_Successfully": "Tiimi on poistettu",
"Deleted_The_Room_Successfully": "Huone on poistettu",
"Convert_to_Channel": "Muunna kanavaksi",
"Converting_Team_To_Channel": "Muunnetaan tiimiä kanavaksi",
"Select_Team_Channels_To_Delete": "Valitse poistettavat tiimin kanavat. Valitsematta jättämäsi kanavat siirretään työtilaan. \n\nHuomioi, että julkiset kanavat ovat julkisia ja näkyvät kaikille.",
"You_are_converting_the_team": "Muunnat tätä tiimiä kanavaksi",
"Display": "Näytä",
"Avatars": "Avatarit",
"Sort_by": "Lajitteluperuste",
"Group_by": "Ryhmittelyperuste",
"Types": "Tyypit",
"Expanded": "Laajennettu",
"Condensed": "Tiivistetty",
"creating_discussion": "luo keskustelua",
"Canned_Responses": "Valmiit vastaukset",
"No_match_found": "Vastineita ei löydy.",
"No_discussions": "Ei keskusteluja",
"Check_canned_responses": "Valmiiden vastausten tarkistus.",
"Searching": "Haetaan",
"Use": "Käytä",
"Shortcut": "Pikavalinta",
"Content": "Sisältö",
"Sharing": "Jaetaan",
"No_canned_responses": "Ei valmiita vastauksia",
"Send_email_confirmation": "Lähetä vahvistussähköposti",
"sending_email_confirmation": "lähettää sähköpostivahvistusta",
"Enable_Message_Parser": "Ota käyttöön viestin jäsentäjä",
"Unsupported_format": "Muotoa ei tueta",
"Downloaded_file": "Ladattu tiedosto",
"Error_Download_file": "Virhe ladattaessa tiedostoa",
"added__roomName__to_this_team": "lisäsi huoneen #{{roomName}} tähän tiimiin",
"Added__username__to_this_team": "lisäsi käyttäjän @{{user_added}} tähän tiimiin",
"Converting_team_to_channel": "Muunnetaan tiimiä kanavaksi",
"Converted__roomName__to_a_team": "muunsi huoneen #{{roomName}} tiimiksi",
"Converted__roomName__to_a_channel": "muunsi huoneen #{{roomName}} kanavaksi",
"Deleted__roomName__": "poisti huoneen #{{roomName}}",
"Message_HideType_added_user_to_team": "Piilota Käyttäjä lisätty tiimiin -ilmoitukset",
"Message_HideType_removed_user_from_team": "Piilota Käyttäjä poistettu tiimistä -ilmoitukset",
"Message_HideType_ujt": "Piilota Käyttäjä liittyi tiimiin -ilmoitukset",
"Message_HideType_ult": "Piilota Käyttäjä poistui tiimistä -ilmoitukset",
"Message_HideType_user_added_room_to_team": "Piilota Käyttäjä lisäsi huoneen tiimille -ilmoitukset",
"Message_HideType_user_converted_to_channel": "Piilota Käyttäjä muunsi tiimin kanavaksi -ilmoitukset",
"Message_HideType_user_converted_to_team": "Piilota Käyttäjä muunsi kanavan tiimiksi -ilmoitukset",
"Message_HideType_user_deleted_room_from_team": "Piilota Käyttäjä poisti huoneen tiimiltä -ilmoitukset",
"Message_HideType_user_removed_room_from_team": "Piilota Käyttäjä poisti huoneen tiimiltä -ilmoitukset",
"Removed__roomName__from_the_team": "poisti huoneen #{{roomName}} tästä tiimistä",
"Removed__username__from_the_team": "poisti käyttäjän @{{userRemoved}} tästä tiimistä",
"User_joined_team": "liittyi tähän tiimiin",
"User_left_team": "poistui tästä tiimistä",
"Place_chat_on_hold": "Aseta keskustelu pitoon",
"Would_like_to_place_on_hold": "Haluatko asettaa tämän keskustelun pitoon?",
"Open_Livechats": "Meneillään olevat Omnichannel-keskustelut",
"On_hold_Livechats": "Pidossa olevat Omnichannel-keskustelut",
"Chat_is_on_hold": "Tämä keskustelu on pidossa käyttämättömyyden vuoksi",
"Resume": "Jatka",
"Omnichannel_placed_chat_on_hold": "Keskustelu pidossa: {{comment}}",
"Omnichannel_on_hold_chat_resumed": "Pidossa olevaa keskustelua jatkettiin: {{comment}}",
"Omnichannel_queue": "Omnichannel-jono",
"Empty": "Tyhjä",
"Mark_as_unread": "Merkitse lukemattomaksi",
"Mark_as_unread_Info": "Näytä huone lukemattomana, kun siinä on lukemattomia viestejä",
"Show_badge_for_mentions": "Näytä mainintojen merkki",
"Show_badge_for_mentions_Info": "Näytä vain suorien mainintojen merkki",
"error-init-video-conf": "Virhe aloitettaessa videopuhelua",
"totp-invalid": "Koodi tai salasana virheellinen",
"Close_Chat": "Sulje keskustelu",
"Select_tags": "Valitse tunnisteet",
"Skip": "Ohita",
"N_Selected_members": "{{n}} valittu",
"Broadcast": "Lähetys",
"Broadcast_hint": "Vain valtuutetut käyttäjät voivat kirjoittaa uusia viestejä, mutta muut käyttäjät voivat vastata",
"Team_hint_private": "Vain kutsutut henkilöt voivat liittyä",
"Team_hint_public": "Kun tämä ei ole käytössä, kuka tahansa voi liittyä tiimiin",
"Team_hint_not_read_only": "Kaikki tämän tiimin käyttäjät voivat kirjoittaa viestejä",
"Team_hint_encrypted": "Täysin salattu tiimi. Haku ei toimi salatuissa tiimeissä eikä viestien sisältö välttämättä näy ilmoituksissa.",
"Team_hint_encrypted_not_available": "Käytettävissä vain yksityisessä tiimissä",
"Channel_hint_private": "Vain kutsutuilla käyttäjillä on pääsy tälle kanavalle",
"Channel_hint_public": "Kaikilla on pääsy tälle kanavalle",
"Channel_hint_encrypted": "Täysin salattu kanava. Haku ei toimi salatuilla kanavilla eikä viestien sisältö välttämättä näy ilmoituksissa.",
"Channel_hint_not_read_only": "Kaikki kanavan käyttäjät voivat kirjoittaa uusia viestejä",
"Channel_hint_encrypted_not_available": "Ei käytettävissä julkisilla kanavilla",
"Read_only_hint": "Vain valtuutetut käyttäjät voivat kirjoittaa uusia viestejä",
"Discussion": "Keskustelu",
"Channel": "Kanava",
"Team": "Tiimi",
"Select_Members": "Valitse jäsenet",
"Also_send_thread_message_to_channel_behavior": "Lähetä ketjun viesti myös kanavalle",
"Accounts_Default_User_Preferences_alsoSendThreadToChannel_Description": "Salli käyttäjien valita Lähetä myös kanavalle -toiminta",
"Waiting_for_answer": "Odotetaan vastausta",
"Call_ended": "Puhelu päättyi",
"Call_was_not_answered": "Puheluun ei vastattu",
"Call_back": "Soita takaisin",
"Call_again": "Soita uudelleen",
"Call_ongoing": "Puhelu käynnissä",
"Joined": "Liitytty",
"Calling": "Soitetaan...",
"Start_a_call": "Aloita puhelu",
"Call": "Soita",
"Reply_in_direct_message": "Vastaa suoralla viestillä",
"room_archived": "arkistoi huoneen",
"room_unarchived": "palautti huoneen arkistosta"
}

View File

@ -313,6 +313,7 @@
"Message_accessibility": "Message de {{user}} à {{time}} : {{message}}",
"Message_actions": "Actions de message",
"Message_pinned": "Message épinglé",
"Message_removed": "Message supprimé",
"Message_starred": "Message suivi",
"Message_unstarred": "Message non suivi",
"message": "message",
@ -779,6 +780,7 @@
"Message_HideType_user_deleted_room_from_team": "Masquer les messages \"L'utilisateur a supprimé le salon de l'équipe\"",
"Message_HideType_user_removed_room_from_team": "Masquer les messages \"L'utilisateur a enlevé le salon de l'équipe\"",
"User_joined_team": "a rejoint cette équipe",
"User_left_team": "a quitté cette équipe",
"Place_chat_on_hold": "Mettre le chat en attente",
"Would_like_to_place_on_hold": "Souhaitez-vous mettre ce chat en attente ?",
"Open_Livechats": "Chats omnicanaux en cours",

View File

@ -191,6 +191,7 @@
"DELETE": "ELIMINA",
"Delete_Account": "Elimina account",
"Delete_Account_confirm": "Sì, Elimina",
"move": "spostare",
"deleting_room": "cancellazione stanza",
"description": "descrizione",
"Description": "Descrizione",
@ -320,6 +321,7 @@
"Message_accessibility": "Messaggio da {{user}} alle {{time}}: {{message}}",
"Message_actions": "Azioni messaggio",
"Message_pinned": "Messaggio appuntato",
"Message_removed": "Messaggio rimosso",
"Message_starred": "Messaggio importante",
"Message_unstarred": "Messaggio non importante",
"message": "messaggio",
@ -687,5 +689,6 @@
"Enter_workspace_URL": "Inserisci la url del workspace",
"Workspace_URL_Example": "Es. tua-azienda.rocket.chat",
"invalid-room": "Canale non valido",
"Open_Livechats": "Chat in corso",
"Broadcast_hint": "Solo gli utenti autorizzati possono scrivere messaggi, ma gli altri utenti saranno in grado di rispondere"
}

View File

@ -313,6 +313,7 @@
"Message_accessibility": "Bericht van {{user}} om {{time}}: {{message}}",
"Message_actions": "Berichtacties",
"Message_pinned": "Bericht vastgezet",
"Message_removed": "Bericht verwijderd",
"Message_starred": "Bericht met ster",
"Message_unstarred": "Bericht zonder ster",
"message": "bericht",
@ -779,6 +780,7 @@
"Message_HideType_user_deleted_room_from_team": "Verberg \"Gebruiker heeft kamer uit team verwijderd\" berichten",
"Message_HideType_user_removed_room_from_team": "Verberg \"Gebruiker heeft kamer uit team verwijderd\" berichten",
"User_joined_team": "is lid geworden van dit team",
"User_left_team": "heeft dit team verlaten",
"Place_chat_on_hold": "Chat in de wacht zetten",
"Would_like_to_place_on_hold": "Wil je deze chat in de wacht zetten?",
"Open_Livechats": "Omnichannels-chats bezig",

View File

@ -12,6 +12,7 @@
"error-could-not-change-email": "Não foi possível mudar e-mail",
"error-could-not-change-name": "Não foi possível mudar o nome",
"error-could-not-change-username": "Não foi possível alterar o nome de usuário",
"error-could-not-change-status": "Não foi possível alterar o status",
"error-delete-protected-role": "Não é possível remover um papel protegido",
"error-department-not-found": "Departamento não encontrado",
"error-direct-message-file-upload-not-allowed": "Compartilhamento de arquivos não está permitido em mensagens diretas",
@ -19,6 +20,7 @@
"error-email-domain-blacklisted": "O domínio de e-mail está na lista negra",
"error-email-send-failed": "Erro ao tentar enviar e-mail: {{message}}",
"error-save-image": "Erro ao salvar imagem",
"error-save-video": "Erro ao salvar vídeo",
"error-field-unavailable": "{{field}} já está sendo usado :(",
"error-file-too-large": "Arquivo é muito grande",
"error-not-permission-to-upload-file": "Você não tem permissão para enviar arquivos",
@ -88,6 +90,7 @@
"Add_Reaction": "Reagir",
"Add_Server": "Adicionar servidor",
"Add_users": "Adicionar usuário",
"Admin_Panel": "Painel de admin",
"Agent": "Agente",
"Alert": "Alerta",
"alert": "alerta",
@ -96,6 +99,7 @@
"All_users_in_the_team_can_write_new_messages": "Todos usuários no canal podem enviar mensagens novas",
"A_meaningful_name_for_the_discussion_room": "Um nome significativo para o canal de discussão",
"All": "Todos",
"All_Messages": "Todas as mensagens",
"Allow_Reactions": "Permitir reagir",
"Alphabetical": "Alfabético",
"and_more": "e mais",
@ -163,7 +167,10 @@
"Copied_to_clipboard": "Copiado para a área de transferência!",
"Copy": "Copiar",
"Conversation": "Conversação",
"Certificate_password": "Senha do certificado",
"Clear_cache": "Limpar cache da workspace",
"Clear_cache_loading": "Limpando cache.",
"Whats_the_password_for_your_certificate": "Qual é a senha para o seu certificado?",
"Create_account": "Criar conta",
"Create_Channel": "Criar Canal",
"Create_Direct_Messages": "Criar Mensagens Diretas",
@ -171,6 +178,7 @@
"Created_snippet": "criou um snippet",
"Create_a_new_workspace": "Criar nova área de trabalho",
"Create": "Criar",
"Custom_Status": "Status personalizado",
"Dark": "Escuro",
"Dark_level": "Nível escuro",
"Default": "Padrão",
@ -248,9 +256,14 @@
"Full_table": "Clique para ver a tabela completa",
"Generate_New_Link": "Gerar novo convite",
"Get_link": "Obter link",
"User_joined_the_channel": "entrou no canal",
"User_joined_the_conversation": "entrou na conversa",
"User_joined_the_team": "entrou na equipe",
"User_left_this_channel": "saiu da conversa",
"Has_left_the_team": "saiu da equipe",
"Hide_System_Messages": "Esconder mensagens do sistema",
"Hide_type_messages": "Esconder mensagens de \"{{type}}\"",
"How_It_Works": "Como funciona",
"Message_HideType_uj": "Utilizador Entrou",
"Message_HideType_ul": "Utilizador Saiu",
"Message_HideType_ru": "Utilizador Removido",
@ -264,11 +277,15 @@
"Message_HideType_subscription_role_removed": "Papel removido",
"Message_HideType_room_archived": "Sala arquivada",
"Message_HideType_room_unarchived": "Sala desarquivada",
"I_Saved_My_E2E_Password": "Salvei minha senha ponta-a-ponta",
"IP": "IP",
"In_app": "No app",
"In_App_And_Desktop": "In-app e área de trabalho",
"In_App_and_Desktop_Alert_info": "Exibe um banner na parte superior da tela quando o aplicativo é aberto e exibe uma notificação na área de trabalho",
"Invisible": "Invisível",
"Invite": "Convidar",
"is_a_valid_RocketChat_instance": "é uma instância Rocket.Chat",
"is_not_a_valid_RocketChat_instance": "não é uma instância Rocket.Chat",
"is_typing": "está digitando",
"Invalid_or_expired_invite_token": "Token de convite inválido ou vencido",
"Invalid_server_version": "O servidor que você está conectando não é suportado mais por esta versão do aplicativo: {{currentVersion}}.\n\nEsta versão do aplicativo requer a versão {{minVersion}} do servidor para funcionar corretamente.",
@ -289,7 +306,9 @@
"leave": "sair",
"Legal": "Legal",
"Light": "Claro",
"License": "Licença",
"Livechat": "Livechat",
"Livechat_edit": "Editar livechat",
"Livechat_transfer_return_to_the_queue": "retornou conversa para a fila",
"Login": "Entrar",
"Login_error": "Suas credenciais foram rejeitadas. Tente novamente por favor!",
@ -298,6 +317,7 @@
"Logout": "Sair",
"Max_number_of_uses": "Número máximo de usos",
"Max_number_of_users_allowed_is_number": "Número máximo de usuários é {{maxUsers}}",
"members": "membros",
"Members": "Membros",
"Mentioned_Messages": "Mensagens mencionadas",
"mentioned": "mencionado",
@ -305,14 +325,19 @@
"Message_accessibility": "Mensagem de {{user}} às {{time}}: {{message}}",
"Message_actions": "Ações",
"Message_pinned": "Fixou uma mensagem",
"Message_removed": "mensagem removida",
"Message_starred": "Mensagem adicionada aos favoritos",
"Message_unstarred": "Mensagem removida dos favoritos",
"message": "mensagem",
"messages": "mensagens",
"Message": "Mensagem",
"Messages": "Mensagens",
"Message_Reported": "Mensagem reportada",
"Microphone_Permission_Message": "Rocket.Chat precisa de acesso ao seu microfone para enviar mensagens de áudio.",
"Microphone_Permission": "Acesso ao Microfone",
"Mute": "Mudo",
"muted": "mudo",
"My_servers": "Minhas workspaces",
"N_people_reacted": "{{n}} pessoas reagiram",
"N_users": "{{n}} usuários",
"N_channels": "{{n}} canais",
@ -321,18 +346,22 @@
"New_chat_transfer": "Nova transferência de conversa: {{agent}} retornou conversa para a fila",
"New_Message": "Nova Mensagem",
"New_Password": "Nova Senha",
"New_Server": "Nova workspace",
"Next": "Próximo",
"No_files": "Não há arquivos",
"No_limit": "Sem limite",
"No_mentioned_messages": "Não há menções",
"No_pinned_messages": "Não há mensagens fixadas",
"No_results_found": "Nenhum resultado encontrado",
"No_members_found": "Nenhum usuário encontrado",
"No_starred_messages": "Não há mensagens favoritas",
"No_thread_messages": "Não há tópicos",
"No_label_provided": "Sem {{label}}.",
"No_Message": "Não há mensagens",
"No_messages_yet": "Não há mensagens ainda",
"No_Reactions": "Sem reações",
"No_Read_Receipts": "Não lida",
"Not_logged": "Desconectado",
"Not_RC_Server": "Este não é um servidor Rocket.Chat.\n{{contact}}",
"Nothing": "Nada",
"Nothing_to_save": "Nada para salvar!",
@ -420,11 +449,21 @@
"Roles": "Papéis",
"Room_actions": "Ações",
"Room_changed_announcement": "O anúncio da sala foi alterado para: {{announcement}} por {{userBy}}",
"room_avatar_changed": "alterou avatar da sala",
"Room_changed_description": "A descrição da sala foi alterada para: {{description}} por {{userBy}}",
"changed_room_description": "alterou a descrição da sala para: {{description}}",
"changed_room_announcement": "alterou o anúncio da sala para: {{announcement}}",
"room_changed_type": "mudou sala para {{type}}",
"room_changed_topic_to": "mudou tópico da sala para: {{topic}}",
"Room_Files": "Arquivos",
"Room_Info_Edit": "Editar",
"Room_Info": "Informações da Sala",
"Room_Members": "Membros",
"Room_name_changed_to": "alterou o nome da sala para: {{name}}",
"room_disallowed_reactions": "removeu a permissão de reagir",
"room_allowed_reactions": "adicionou permissão de reagir",
"room_removed_read_only_permission": "removeu permissão de escrita da sala",
"room_set_read_only_permission": "adicionou permissão de escrita à sala",
"SAVE": "SALVAR",
"Save_Changes": "Salvar Alterações",
"Save": "Salvar",
@ -440,8 +479,10 @@
"Search_Messages": "Buscar Mensagens",
"Search": "Buscar",
"Search_by": "Buscar por",
"Search_emoji": "Buscar emoji",
"Search_global_users": "Busca por usuários globais",
"Search_global_users_description": "Caso ativado, busca por usuários de outras empresas ou servidores.",
"Seconds": "{{second}} segundos",
"Security_and_privacy": "Segurança e privacidade",
"Select_Avatar": "Selecionar Avatar",
"Select_Server": "Selecionar Servidor",
@ -456,13 +497,20 @@
"Send_message": "Enviar mensagem",
"Send_me_the_code_again": "Envie-me o código novamente",
"Send_to": "Enviar para...",
"Sending_to": "Envio para",
"Sent_an_attachment": "Enviou um anexo",
"Server": "Servidor",
"Servers": "Workspaces",
"Server_version": "Versão da workspace: {{version}}",
"Set_username_subtitle": "O usuário é utilizado para permitir que você seja mencionado em mensagens",
"Set_custom_status": "Definir status personalizado",
"Set_status": "Definir status",
"Status_saved_successfully": "Status salvo com sucesso!",
"Settings": "Configurações",
"Settings_succesfully_changed": "Configurações salvas com sucesso!",
"Share": "Compartilhar",
"Share_Link": "Share Link",
"Share_this_app": "Compartilhar esse app",
"Show_more": "Mostrar mais..",
"Sign_in_your_server": "Entrar no seu servidor",
"Sign_Up": "Registrar",
@ -479,9 +527,12 @@
"Started_call": "Chamada iniciada por {{userBy}}",
"Submit": "Enviar",
"Table": "Tabela",
"Tags": "Tags",
"Take_a_photo": "Tirar uma foto",
"Take_a_video": "Gravar um vídeo",
"Take_it": "Pegue!",
"tap_to_change_status": "toque para mudar de status",
"Tap_to_view_servers_list": "Toque para visualizar as workspaces",
"Terms_of_Service": " Termos de Serviço ",
"Theme": "Tema",
"The_user_wont_be_able_to_type_in_roomName": "O usuário não poderá digitar em {{roomName}}",
@ -513,17 +564,27 @@
"Unsupported_system_message": "Mensagem de sistema não suportada",
"Updating": "Atualizando...",
"Uploading": "Subindo arquivo",
"FileUpload_Error": "Erro de upload de arquivo",
"Upload_in_progress": "Carregamento em andamento",
"Upload_file_question_mark": "Enviar arquivo?",
"User": "Usuário",
"Users": "Usuários",
"User_added_to": "adicionou o usuário {{userAdded}}",
"User_Info": "Informações do usuário",
"User_has_been_key": "Usuário foi {{key}}",
"User_is_no_longer_role_by_": "{{user}} não pertence mais à {{role}} por {{userBy}}",
"User_has_been_muted": "silenciou o usuário {{userMuted}}",
"User_has_been_removed": "removeu {{userRemoved}}",
"User_sent_an_attachment": "{{user}} enviou um anexo",
"User_has_been_unmuted": "permitiu que {{userUnmuted}} fale na sala",
"Defined_user_as_role": "definiu {{user}} como {{role}}",
"Removed_user_as_role": "removeu {{user}} como {{role}}",
"Username_is_empty": "Usuário está vazio",
"Username": "Usuário",
"Username_or_email": "Usuário ou email",
"Uses_server_configuration": "Usar configuração do servidor",
"Validating": "Validando...",
"Registration_Succeeded": "Registrado com sucesso!",
"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.",
@ -552,7 +613,9 @@
"You_were_removed_from_channel": "Você foi removido de {{channel}}",
"you": "você",
"You": "Você",
"Logged_out_by_server": "Você foi desconectado pela workspace. Por favor entre novamente.",
"Token_expired": "Sua sessão expirou. Por favor entre novamente.",
"You_need_to_access_at_least_one_RocketChat_server_to_share_something": "Você precisa acessar pelo menos uma workspace Rocket.Chat para compartilhar.",
"You_need_to_verifiy_your_email_address_to_get_notications": "Você precisa confirmar seu endereço de e-mail para obter notificações",
"Your_certificate": "Seu certificado",
"Your_invite_link_will_expire_after__usesLeft__uses": "Seu link de convite irá vencer depois de {{usesLeft}} usos.",
@ -560,6 +623,8 @@
"Your_invite_link_will_expire_on__date__": "Seu link de convite irá vencer em {{date}}.",
"Your_invite_link_will_never_expire": "Seu link de convite nunca irá vencer.",
"Your_workspace": "Sua workspace",
"Your_password_is": "Sua senha é",
"Version_no": "Versão: {{version}}",
"You_will_not_be_able_to_recover_this_message": "Você não será capaz de recuperar essa mensagem!",
"You_will_unset_a_certificate_for_this_server": "Você cancelará a configuração de um certificado para este servidor",
"Change_Language": "Alterar idioma",
@ -655,9 +720,15 @@
"Message_Ignored": "Mensagem ignorada. Toque para mostrar.",
"Enter_workspace_URL": "Digite a URL da sua workspace",
"Workspace_URL_Example": "Ex. sua-empresa.rocket.chat",
"Enabled_E2E_Encryption_for_this_room": "habilitou criptografia para essa sala",
"Disabled_E2E_Encryption_for_this_room": "desabilitou criptografia para essa sala",
"Teams": "Times",
"No_team_channels_found": "Nenhum canal encontrado",
"Team_not_found": "Time não encontrado",
"Create_Team": "Criar time",
"Team_Name": "Nome do time",
"creating_team": "criando time",
"team-name-already-exists": "Um time com esse nome já existe",
"Add_Channel_to_Team": "Adicionar Canal ao Time",
"Left_The_Team_Successfully": "Saiu do time com sucesso",
"Create_New": "Criar",
@ -734,7 +805,11 @@
"Unsupported_format": "Formato não suportado",
"Downloaded_file": "Arquivo baixado",
"Error_Download_file": "Erro ao baixar o arquivo",
"added__roomName__to_this_team": "adicionou #{{roomName}} a esta equipe",
"Added__username__to_this_team": "adicionou @{{user_added}} a esta equipe",
"Converting_team_to_channel": "Convertendo equipe em canal",
"Converted__roomName__to_a_team": "converteu #{{roomName}} em equipe",
"Converted__roomName__to_a_channel": "converteu #{{roomName}} em canal",
"Deleted__roomName__": "#{{roomName}} apagada",
"Message_HideType_added_user_to_team": "Ocultar mensagens de \"Usuário adicionado à equipe\"",
"Message_HideType_removed_user_from_team": "Ocultar mensagens de \"Usuário removido da equipe\"",
@ -745,7 +820,10 @@
"Message_HideType_user_converted_to_team": "Ocultar mensagens de \"Usuário converteu canal em equipe\"",
"Message_HideType_user_deleted_room_from_team": "Ocultar mensagens de \"Usuário apagou sala da equipe\"",
"Message_HideType_user_removed_room_from_team": "Ocultar mensagens de \"Usuário removeu sala da equipe\"",
"Removed__roomName__from_the_team": "removeu #{{roomName}} desta equipe",
"Removed__username__from_the_team": "removeu @{{userRemoved}} desta equipe",
"User_joined_team": "entrou nesta equipe",
"User_left_team": "saiu desta equipe",
"Place_chat_on_hold": "Colocar conversa em espera",
"Would_like_to_place_on_hold": "Gostaria de colocar essa conversa Em Espera?",
"Open_Livechats": "Bate-papos em Andamento",
@ -760,6 +838,7 @@
"Mark_as_unread_Info": "Mostrar sala como não lida quando houver mensagens não lidas",
"Show_badge_for_mentions": "Mostrar contador para menções",
"Show_badge_for_mentions_Info": "Mostrar contador somente para menções diretas",
"error-init-video-conf": "Erro ao iniciar chamada de video",
"totp-invalid": "Código ou senha inválida",
"Close_Chat": "Fechar Conversa",
"Select_tags": "Selecionar tag(s)",
@ -772,18 +851,28 @@
"Team_hint_not_read_only": "Todos os usuários nesta equipe podem escrever mensagens",
"Team_hint_encrypted": "Equipe criptografada de ponta a ponta. A pesquisa não funcionará com equipes criptografadas e as notificações poderão não exibir o conteúdo das mensagens.",
"Team_hint_encrypted_not_available": "Disponível apenas para equipes privadas",
"Channel_hint_private":"Apenas usuários convidados podem acessar este canal",
"Channel_hint_public":"Todos podem acessar este canal",
"Channel_hint_private": "Apenas usuários convidados podem acessar este canal",
"Channel_hint_public": "Todos podem acessar este canal",
"Channel_hint_encrypted": "Canal criptografado de ponta a ponta. A pesquisa não funcionará com canais criptografados e as notificações podem não mostrar o conteúdo das mensagens.",
"Channel_hint_not_read_only": "Todos usuários no canal podem enviar mensagens novas",
"Channel_hint_encrypted_not_available": "Indisponível para canais públicos",
"Read_only_hint":"Somente usuários autorizados podem escrever novas mensagens",
"Read_only_hint": "Somente usuários autorizados podem escrever novas mensagens",
"Discussion": "Discussão",
"Channel": "Canal",
"Team": "Time",
"Select_Members": "Selecionar Membros",
"Also_send_thread_message_to_channel_behavior": "Também enviar mensagem do tópico para o canal",
"Accounts_Default_User_Preferences_alsoSendThreadToChannel_Description": "Permitir que os usuários selecionem o comportamento Também enviar para o canal",
"Waiting_for_answer": "Esperando por resposta",
"Call_ended": "Chamada finalizada",
"Call_was_not_answered": "A chamada não foi atendida",
"Call_back": "Ligue de volta",
"Call_again": "Ligue novamente",
"Call_ongoing": "Chamada em andamento",
"Joined": "Ingressou",
"Calling": "Chamando...",
"Start_a_call": "Inicie uma chamada",
"Call": "Ligar",
"Reply_in_direct_message": "Responder por mensagem direta",
"room_archived": "{{username}} arquivou a sala",
"room_unarchived": "{{username}} desarquivou a sala"

View File

@ -307,6 +307,7 @@
"Message_accessibility": "Mensagem de {{user}} às {{time}}: {{message}}",
"Message_actions": "Acções de mensagem",
"Message_pinned": "Mensagem afixada",
"Message_removed": "Mensagem removida",
"Message_starred": "Mensagem estrelada",
"Message_unstarred": "Mensagem não estrelada",
"message": "mensagem",
@ -492,5 +493,6 @@
"you": "você",
"You": "Você",
"You_will_not_be_able_to_recover_this_message": "Você será incapaz de recuperar esta mensagem!",
"Open_Livechats": "Chats em andamento",
"Broadcast_hint": "Apenas utilizadores autorizados podem escrever novas mensagens, mas os outros utilizadores poderão responder"
}

View File

@ -256,6 +256,7 @@
"Forward_to_user": "Перенаправить пользователю",
"Full_table": "Нажмите, чтобы увидеть полную таблицу",
"Generate_New_Link": "Сгенерировать Новую Ссылку",
"Get_link": "Получить ссылку",
"Has_left_the_team": "покинул Команду",
"Hide_System_Messages": "Скрыть Системные Сообщения",
"Hide_type_messages": "Скрыть \"{{type}}\" сообщения",
@ -321,6 +322,7 @@
"Message_accessibility": "Сообщение от {{user}} в {{time}}: {{message}}",
"Message_actions": "Действия с сообщением",
"Message_pinned": "Сообщение прикреплено",
"Message_removed": "Сообщение удалено",
"Message_starred": "Сообщение отмечено звездой",
"Message_unstarred": "Отметка сообщения звездой удалена",
"message": "сообщение",
@ -348,6 +350,7 @@
"No_mentioned_messages": "Нет упоминаний",
"No_pinned_messages": "Нет прикрепленных сообщений",
"No_results_found": "Ничего не найдено",
"No_members_found": "Участники не найдены",
"No_starred_messages": "Нет отмеченных сообщений",
"No_thread_messages": "Нет сообщений в теме",
"No_label_provided": "{{label}} не указан.",
@ -463,6 +466,7 @@
"Search_Messages": "Поиск сообщений",
"Search": "Поиск",
"Search_by": "Поиск по",
"Search_emoji": "Поиск эмодзи",
"Search_global_users": "Глобальный поиск пользователей",
"Search_global_users_description": "При активации станет возможен поиск пользователей на других серверах.",
"Seconds": "{{second}} секунд",
@ -790,6 +794,7 @@
"Message_HideType_user_deleted_room_from_team": "Скрыть сообщения \"Пользователь удалил чат из Команды\"",
"Message_HideType_user_removed_room_from_team": "Скрыть сообщения \"Пользователь удалил чат из Команды\"",
"User_joined_team": "присоединился к Команде",
"User_left_team": "покинул Команду",
"Place_chat_on_hold": "Поставить чат на удержание",
"Would_like_to_place_on_hold": "Вы хотите поставить этот чат на удержание?",
"Open_Livechats": "Omnichannel чаты в работе",
@ -808,5 +813,36 @@
"totp-invalid": "Код или пароль не верны",
"Close_Chat": "Закрыть чат",
"Select_tags": "Выберите теги",
"Broadcast_hint": "Только авторизованные пользователи могут писать новые сообщения, но другие пользователи смогут ответить"
"Skip": "Пропустить",
"N_Selected_members": "{{n}} выбрано",
"Broadcast": "Широковещательный",
"Broadcast_hint": "Только авторизованные пользователи могут писать новые сообщения, но другие пользователи смогут ответить",
"Team_hint_private": "Только приглашенные люди могут присоединиться",
"Team_hint_public": "Когда отключено, кто угодно может присоединиться к команде",
"Team_hint_not_read_only": "Все пользователи в этой команде могут писать сообщения",
"Team_hint_encrypted": "Команда со сквозным шифрованием. Поиск не будет работать в зашифрованных командах, а уведомления могут не отображать содержание сообщений.",
"Team_hint_encrypted_not_available": "Доступно только для приватной команды",
"Channel_hint_private": "Только приглашенные пользователи могут получить доступ к этому каналу",
"Channel_hint_public": "У всех есть доступ к этому каналу",
"Channel_hint_encrypted": "Канал со сквозным шифрованием. Поиск не будет работать в зашифрованных каналах, а уведомления могут не отображать содержание сообщений.",
"Channel_hint_not_read_only": "Все пользователи канала могут писать новые сообщения",
"Channel_hint_encrypted_not_available": "Недоступно для публичных каналов",
"Read_only_hint": "Только авторизованные пользователи могут писать новые сообщения",
"Discussion": "Обсуждение",
"Channel": "Канал",
"Team": "Команда",
"Select_Members": "Выбрать участников",
"Also_send_thread_message_to_channel_behavior": "Также отправить в чат",
"Accounts_Default_User_Preferences_alsoSendThreadToChannel_Description": "Разрешить пользователям выбирать поведение \"Также отправить в чат\"",
"Waiting_for_answer": "Ожидание ответа",
"Call_ended": "Звонок завершен",
"Call_was_not_answered": "Звонок не был отвечен",
"Call_back": "Обратный звонок",
"Call_again": "Позвонить снова",
"Call_ongoing": "Идет вызов",
"Joined": "участвовал",
"Calling": "Идет вызов",
"Start_a_call": "Начать звонок",
"Call": "Звонок",
"Reply_in_direct_message": "Ответить в личном сообщении"
}

View File

@ -126,8 +126,6 @@
"Black": "Črn",
"Block_user": "Blokiraj uporabnika",
"Browser": "Brskalnik",
"Broadcast_channel_Description": "Samo pooblaščeni uporabniki lahko napišejo nova sporočila, drugi uporabniki pa bodo lahko odgovorili",
"Broadcast_Channel": "Oddajni kanal",
"Busy": "Zaseden",
"By_proceeding_you_are_agreeing": "Z nadaljevanjem se strinjate z našimi",
"Cancel_editing": "Prekliči urejanje",
@ -258,10 +256,6 @@
"Forward_to_user": "Posreduj uporabniku",
"Full_table": "Kliknite za ogled celotne tabele",
"Generate_New_Link": "Ustvari novo povezavo",
"Has_joined_the_channel": "se je pridružil kanalu",
"Has_joined_the_team": "se je pridružil ekipi",
"Has_joined_the_conversation": "se je pridružil pogovoru",
"Has_left_the_channel": "je zapustil kanal",
"Has_left_the_team": "je zapustil ekipo",
"Hide_System_Messages": "Skrij sistemska sporočila",
"Hide_type_messages": "Skrij sporočila \"{{type}}\"",
@ -406,7 +400,6 @@
"Preferences": "Nastavitve",
"Preferences_saved": "Shranjene nastavitve",
"Privacy_Policy": "Varovanje zasebnosti",
"Private_Channel": "Zasebni kanal",
"Private": "Zasebno",
"Processing": "Procesiranje...",
"Profile_saved_successfully": "Profil uspešno shranjen",
@ -421,7 +414,6 @@
"Reactions": "Odzivi",
"Read_External_Permission_Message": "Rocket.Chat potrebuje dostop do fotografij, medijev in datotek v vaši napravi",
"Read_External_Permission": "Dovoljenje za branje medija",
"Read_Only_Channel": "Kanal samo za branje",
"Read_Only": "Le branje",
"Read_Receipt": "Preberite potrdilo",
"Receive_Group_Mentions": "Prejemajte omembe skupine",
@ -452,19 +444,11 @@
"Roles": "Vloge",
"Room_actions": "Sobna dejanja",
"Room_changed_announcement": "Objava sobe se je spremenila v: {{announcement}} avtor {{userBy}}",
"Room_changed_avatar": "Soba Avatar je spremenil {{userBy}}",
"Room_changed_description": "Opis sobe spremeni v: {{description}} avtor {{userBy}}",
"Room_changed_privacy": "Vrsta sobe se je spremenila v: {{type}} avtor {{userBy}}",
"Room_changed_topic": "Tema sobe se je spremenila v: {{topic}} avtor {{userBy}}",
"Room_Files": "Sobne datoteke",
"Room_Info_Edit": "Uredi informacije o sobi",
"Room_Info": "Informacije o sobi",
"Room_Members": "Člani sobe",
"Room_name_changed": "Ime sobe je spremenjeno v: {{name}}. Spremenil: {{userBy}}",
"Room_disallowed_reacting": "{{userBy}} je prepovedala reakcijo v sobi",
"Room_allowed_reacting": "{{userBy}} je dovolil reakcijo v sobi",
"Room_set_read_only": "{{UserBy}} je sobo označil \"samo za branje\"",
"Room_removed_read_only": "{{userBy}} je odstranil oznako \"samo za branje\" s sobe",
"SAVE": "Shrani",
"Save_Changes": "Shrani spremembe",
"Save": "Shrani",
@ -567,15 +551,10 @@
"Upload_file_question_mark": "Naloži datoteko?",
"User": "Uporabnik",
"Users": "Uporabniki",
"User_added_by": "Uporabnika {{userAdded}} je dodal {{userBy}}",
"User_Info": "Uporabniške informacije",
"User_has_been_key": "Uporabnik je bil {{key}}",
"User_is_no_longer_role_by_": "{{user}} ni več {{role}} z {{userBy}}",
"User_muted_by": "Uporabnika {{userMuted}} je utišal uporabnik {{userBy}}.",
"User_removed_by": "Uporabnika {{userRemoved}} je odstranil {{userBy}}",
"User_sent_an_attachment": "{{user}} je poslal prilogo",
"User_unmuted_by": "Uporabniku {{userUnmuted}} je vključil zvok uporabnika {{userBy}}",
"User_was_set_role_by_": "{{user}} je nastavil {{role}} uporabnik {{userBy}}",
"Username_is_empty": "Uporabniško ime je prazno",
"Username": "Uporabniško ime",
"Username_or_email": "Uporabnisko ime ali e-posta",
@ -717,16 +696,11 @@
"Message_Ignored": "To sporočilo je bilo prezrto. Tapnite, da ga prikažete",
"Enter_workspace_URL": "Vnesite URL delovnega prostora",
"Workspace_URL_Example": "Npr. your-company.rocket.chat",
"This_room_encryption_has_been_enabled_by__username_": "Šifriranje te sobe je omogočilo {{username}}",
"This_room_encryption_has_been_disabled_by__username_": "Šifriranje te sobe je onemogočeno z {{username}}",
"Teams": "Ekipe",
"No_team_channels_found": "Kanalov ni mogoče najti",
"Team_not_found": "Ekipa ni bila najdena",
"Create_Team": "Ustvari ekipo",
"Team_Name": "Ime ekipe",
"Private_Team": "Zasebna ekipa",
"Read_Only_Team": "Preberite samo ekipo",
"Broadcast_Team": "Oddajna ekipa",
"creating_team": "ustvarjanje ekipe",
"team-name-already-exists": "Ekipa s tem imenom že obstaja",
"Add_Channel_to_Team": "Dodajte kanal v ekipo",
@ -805,10 +779,6 @@
"Unsupported_format": "Nepodprta oblika",
"Downloaded_file": "Prenesena datoteka",
"Error_Download_file": "Napaka med prenosom datoteke",
"added__roomName__to_team": "v to ekipo dodano #{{roomName}}",
"Added__username__to_team": "v to ekipo dodan @{{user_added}}",
"Converted__roomName__to_team": "pretvorjeno #{{roomName}} v ekipo",
"Converted__roomName__to_channel": "pretvorjeno #{{roomName}} v kanal",
"Converting_team_to_channel": "Preoblikovanje ekipe v kanal",
"Deleted__roomName__": "izbrisano #{{roomName}}",
"Message_HideType_added_user_to_team": "Skrij sporočila \"Uporabnik dodan v ekipo\"",
@ -820,8 +790,6 @@
"Message_HideType_user_converted_to_team": "Skrij sporočila \"Uporabnik pretvoril kanal v ekipo\"",
"Message_HideType_user_deleted_room_from_team": "Skrij sporočila \"Uporabnik izbrisal sobo iz ekipe\"",
"Message_HideType_user_removed_room_from_team": "Skrij sporočila \"Uporabnika odstranil sobo iz ekipe\"",
"Removed__roomName__from_this_team": "odstranjena #{{roomName}} iz te ekipe",
"Removed__username__from_team": "odstranjen @{{user_removed}} iz te ekipe",
"User_joined_team": "pridružil tej ekipi",
"User_left_team": "zapustil to ekipo",
"Place_chat_on_hold": "Postavite klepet na \"pridržan\"",

View File

@ -1 +1,878 @@
{ }
{
"__count__empty_rooms_will_be_removed_automatically": "{{count}} tomma rum tas bort.",
"__count__empty_room_will_be_removed_automatically": "{{count}} tomt rum tas bort.",
"1_person_reacted": "1 person reagerade",
"1_user": "1 användare",
"error-action-not-allowed": "{{action}} tillåts inte",
"error-application-not-found": "Appen hittades inte",
"error-archived-duplicate-name": "Det finns en arkiverad kanal med namnet {{room_name}}",
"error-avatar-invalid-url": "Ogiltig avatar-URL: {{url}}",
"error-avatar-url-handling": "Fel vid hantering av avatarinställningar via en URL ({{url}}) för {{username}}",
"error-cant-invite-for-direct-room": "Det går inte att bjuda in användare till direkta rum",
"error-could-not-change-email": "Det gick inte att ändra e-postadressen",
"error-could-not-change-name": "Det gick inte att ändra namnet",
"error-could-not-change-username": "Det gick inte att ändra användarnamnet",
"error-could-not-change-status": "Det gick inte att ändra status",
"error-delete-protected-role": "Det går inte att radera en skyddad roll",
"error-department-not-found": "Avdelningen hittades inte",
"error-direct-message-file-upload-not-allowed": "Fildelning tillåts inte i direktmeddelanden",
"error-duplicate-channel-name": "Det finns redan en kanal med namnet {{room_name}} ",
"error-email-domain-blacklisted": "E-postdomänen är svartlistad",
"error-email-send-failed": "Fel vid försök att skicka e-postmeddelandet: {{message}}",
"error-save-image": "Fel när bilden skulle sparas",
"error-save-video": "Fel när videon skulle sparas",
"error-field-unavailable": "{{field}} används redan :(",
"error-file-too-large": "Filen är för stor",
"error-not-permission-to-upload-file": "Du har inte behörighet att ladda upp filer",
"error-importer-not-defined": "Importören har inte definierats rätt eller saknar importklass.",
"error-input-is-not-a-valid-field": "{{input}} är inte ett giltigt {{field}}",
"error-invalid-actionlink": "Ogiltig åtgärdslänk",
"error-invalid-arguments": "Ogiltiga argument",
"error-invalid-asset": "Ogiltig resurs",
"error-invalid-channel": "Ogiltig kanal.",
"error-invalid-channel-start-with-chars": "Ogiltig kanal. Börja med @ eller #",
"error-invalid-custom-field": "Ogiltigt anpassat fält",
"error-invalid-custom-field-name": "Ogiltigt anpassat fältnamn. Använd endast bokstäver, siffror, bindestreck och understreck.",
"error-invalid-date": "Ett ogiltigt datum har angetts.",
"error-invalid-description": "Ogiltig beskrivning",
"error-invalid-domain": "Ogiltig domän",
"error-invalid-email": "Ogiltig e-post {{email}}",
"error-invalid-email-address": "Ogiltig e-postadress",
"error-invalid-file-height": "Ogiltig filhöjd",
"error-invalid-file-type": "Ogiltig filtyp",
"error-invalid-file-width": "Ogiltig filbredd",
"error-invalid-from-address": "Du angav en ogiltig avsändaradress",
"error-invalid-integration": "Ogiltig integrering",
"error-invalid-message": "Ogiltigt meddelande",
"error-invalid-method": "Ogiltigt metod",
"error-invalid-name": "Ogiltigt namn",
"error-invalid-password": "Ogiltigt lösenord",
"error-invalid-redirectUri": "Ogiltig redirectUri",
"error-invalid-role": "Ogiltig roll",
"error-invalid-room": "Ogiltigt rum",
"error-invalid-room-name": "{{room_name}} är inte ett giltigt rumsnamn",
"error-invalid-room-type": "{{type}} är inte en giltig typ av rum.",
"error-invalid-settings": "Ogiltiga inställningar har angetts",
"error-invalid-subscription": "Ogiltig prenumeration",
"error-invalid-token": "Ogiltig token",
"error-invalid-triggerWords": "Ogiltiga triggerWords",
"error-invalid-urls": "Ogiltiga URL:er",
"error-invalid-user": "Ogiltig användare",
"error-invalid-username": "Ogiltigt användarnamn",
"error-invalid-webhook-response": "Webhook-URL:en returnerade en annan status än 200",
"error-message-deleting-blocked": "Borttagande av meddelanden har blockerats",
"error-message-editing-blocked": "Redigering av meddelanden har blockerats",
"error-message-size-exceeded": "Meddelandets storlek överstiger Message_MaxAllowedSize",
"error-missing-unsubscribe-link": "Du måste ange länken [unsubscribe].",
"error-no-owner-channel": "Du äger inte kanalen",
"error-no-tokens-for-this-user": "Det finns inga token för användaren",
"error-not-allowed": "Tillåts inte",
"error-not-authorized": "Inte behörig",
"error-push-disabled": "Pushmeddelanden är inaktiverade",
"error-remove-last-owner": "Det här är den sista ägaren. Ange en ny ägare innan du tar bort den här.",
"error-role-in-use": "Det går inte att ta bort rollen eftersom den används",
"error-role-name-required": "Rollnamnet måste anges",
"error-password-same-as-current": "Lösenordet du angett är detsamma som det nuvarande lösenordet",
"error-the-field-is-required": "Fältet {{field}} är obligatoriskt.",
"error-too-many-requests": "Fel. För många begäranden. Sakta ned. Du måste vänta {{seconds}} sekunder innan du försöker igen.",
"error-user-is-not-activated": "Användaren är inte aktiverad",
"error-user-has-no-roles": "Användaren har inga roller",
"error-user-limit-exceeded": "Antalet användare som du försöker bjuda in till #channel_name överskrider gränsen som administratören har angett",
"error-user-not-in-room": "Användaren är inte i det här rummet",
"error-user-registration-custom-field": "error-user-registration-custom-field",
"error-user-registration-disabled": "Användarregistrering är inaktiverad",
"error-user-registration-secret": "Registrering av användare tillåts bara via en säker URL",
"error-you-are-last-owner": "Du är den senaste ägaren. Ange en ny ägare innan du lämnar rummet.",
"error-status-not-allowed": "Dold status är inaktiverat",
"A_new_owner_will_be_assigned_automatically_to__count__rooms": "En ny ägare utses automatiskt för {{count}} rum.",
"A_new_owner_will_be_assigned_automatically_to__count__room": "En ny ägare utses automatiskt för {{count}} rum.",
"Actions": "Åtgärder",
"Activity": "Aktivitet",
"Add_Reaction": "Lägg till reaktion",
"Add_Server": "Lägg till server",
"Add_users": "Lägg till användare",
"Admin_Panel": "Administratörspanel",
"Agent": "Agent",
"Alert": "Varning",
"alert": "varning",
"alerts": "varningar",
"All_users_in_the_channel_can_write_new_messages": "Alla användare i kanalen kan skriva nya meddelanden",
"All_users_in_the_team_can_write_new_messages": "Alla användare i teamet kan skriva nya meddelanden",
"A_meaningful_name_for_the_discussion_room": "Ett beskrivande namn på diskussionsrummet",
"All": "Alla",
"All_Messages": "Alla meddelanden",
"Allow_Reactions": "Tillåt reaktioner",
"Alphabetical": "Alfabetisk ordning",
"and_more": "med mera",
"and": "och",
"announcement": "meddelande",
"Announcement": "Meddelande",
"Apply_Your_Certificate": "Tillämpa certifikatet",
"ARCHIVE": "ARKIV",
"archive": "arkiv",
"are_typing": "skriver",
"Are_you_sure_question_mark": "Är du säker?",
"Are_you_sure_you_want_to_delete_your_account": "Vill du ta bort kontot?",
"Deleting_a_user_will_delete_all_messages": "När du tar bort en användare tas alla meddelanden, rum och team för användaren också bort. Åtgärden kan inte ångras.",
"Are_you_sure_you_want_to_leave_the_room": "Vill du lämna rummet {{room}}?",
"Audio": "Ljud",
"Authenticating": "Autentiserar",
"Automatic": "Automatiskt",
"Auto_Translate": "Automatisk översättning",
"Avatar_changed_successfully": "Avataren har ändrats",
"Avatar_Url": "URL till avatar",
"Away": "Borta",
"Back": "Tillbaka",
"Black": "Svart",
"Block_user": "Blockera användare",
"Browser": "Webbläsare",
"Busy": "Upptagen",
"By_proceeding_you_are_agreeing": "Genom att fortsätta anses du ha godkänt vår",
"Cancel_editing": "Avbryt redigering",
"Cancel_recording": "Avbryt inspelning",
"Cancel": "Avbryt",
"changing_avatar": "byter avatar",
"creating_channel": "skapar kanal",
"creating_invite": "skapar inbjudan",
"Channel_Name": "Kanalnamn",
"Channels": "Kanaler",
"Chats": "Chattar",
"Chat_started": "Chatten har startats",
"Call_already_ended": "Samtalet är redan avslutat.",
"Clear_cookies_alert": "Vill du rensa alla cookies?",
"Clear_cookies_desc": "Med den här åtgärden tas alla inloggningscookies bort så att du kan logga in på andra konton.",
"Clear_cookies_yes": "Ja, rensa cookies",
"Clear_cookies_no": "Nej, behåll cookies",
"Click_to_join": "Klicka för att ansluta.",
"Close": "Stäng",
"Close_emoji_selector": "Stäng emojiväljaren",
"Closing_chat": "Stänger chatten",
"Change_language_loading": "Ändrar språk.",
"Chat_closed_by_agent": "Chatten stängdes av agenten",
"Choose": "Välj",
"Choose_from_library": "Välj från biblioteket",
"Choose_file": "Välj fil",
"Choose_where_you_want_links_be_opened": "Välj var du vill att länkar ska öppnas",
"Code": "Kod",
"Code_or_password_invalid": "Koden eller lösenordet är ogiltigt",
"Conversation_closed": "Konversationen har stängts",
"Collaborative": "Samarbete",
"Confirm": "Bekräfta",
"Connect": "Anslut",
"Connected": "Ansluten",
"connecting_server": "ansluter till servern",
"Connecting": "Ansluter...",
"Contact_us": "Kontakta oss",
"Contact_your_server_admin": "Kontakta serveradministratören.",
"Continue_with": "Fortsätt med",
"Copied_to_clipboard": "Kopierades till urklipp.",
"Copy": "Kopiera",
"Conversation": "Konversation",
"Certificate_password": "Lösenord för certifikat",
"Clear_cache": "Rensa den lokala servercachen",
"Clear_cache_loading": "Rensar cachen.",
"Whats_the_password_for_your_certificate": "Ange lösenordet för certifikatet.",
"Create_account": "Skapa ett konto",
"Create_Channel": "Skapa kanal",
"Create_Direct_Messages": "Skapa direktmeddelande",
"Create_Discussion": "Skapa diskussion",
"Created_snippet": "skapade ett utdrag",
"Create_a_new_workspace": "Skapa en ny arbetsyta",
"Create": "Skapa",
"Custom_Status": "Anpassad status",
"Dark": "Mörk",
"Dark_level": "Mörk nivå",
"Default": "Standard",
"Default_browser": "Standardwebbläsare",
"Delete_Room_Warning": "Om du tar bort ett rum tas alla meddelanden bort som publicerats i rummet. Åtgärden kan inte ångras.",
"Department": "Avdelning",
"delete": "ta bort",
"Delete": "Ta bort",
"DELETE": "TA BORT",
"Delete_Account": "Ta bort konto",
"Delete_Account_confirm": "Ja, ta bort",
"move": "flytta",
"deleting_room": "tar bort rum",
"description": "beskrivning",
"Description": "Beskrivning",
"Desktop_Options": "Skrivbordsalternativ",
"Desktop_Notifications": "Skrivbordsaviseringar",
"Desktop_Alert_info": "De här aviseringarna levereras via skrivbordet",
"Directory": "Katalog",
"Direct_Messages": "Direktmeddelanden",
"Disable_notifications": "Inaktivera aviseringar",
"Discussions": "Diskussioner",
"Discussion_Desc": "Bidra till att skapa en översikt över det som händer. När du skapar en diskussion skapas en underkanal till den du valt och båda kanalerna är kopplade.",
"Discussion_name": "Diskussionsnamn",
"Done": "Klart",
"Dont_Have_An_Account": "Har du inget konto?",
"Do_you_have_an_account": "Har du ett konto?",
"Do_you_have_a_certificate": "Har du ett certifikat?",
"Do_you_really_want_to_key_this_room_question_mark": "Vill du verkligen {{key}} rummet?",
"E2E_Encryption": "E2E-kryptering",
"E2E_How_It_Works_info1": "Nu kan du skapa krypterade privata grupper och direktmeddelanden. Du kan också kryptera befintliga privata grupper och direktmeddelanden.",
"E2E_How_It_Works_info2": "Det här görs med *end-to-end-kryptering*, så nyckeln för att koda/avkoda meddelanden sparas inte på servern. Därför *måste du lagra lösenordet på en säker plats* där du kan åtkomma det senare om du behöver.",
"E2E_How_It_Works_info3": "Om du fortsätter genereras ett E2E-lösenord automatiskt",
"E2E_How_It_Works_info4": "Du kan när som helst ställa in ett nytt lösenord för krypteringsnyckeln i en webbläsare där du har angett det befintliga E2E-lösenordet.",
"edit": "redigera",
"edited": "redigerade",
"Edit": "Redigera",
"Edit_Status": "Redigera status",
"Edit_Invite": "Redigera inbjudan",
"End_to_end_encrypted_room": "End-to-end-krypterat rum",
"end_to_end_encryption": "End-to-end-kryptering",
"Email_Notification_Mode_All": "Varje omnämnande/direktmeddelande",
"Email_Notification_Mode_Disabled": "Inaktiverat",
"Email_or_password_field_is_empty": "Fältet för e-post eller lösenord är tomt",
"Email": "E-post",
"email": "e-post",
"Empty_title": "Tom rubrik",
"Enable_Auto_Translate": "Aktivera automatisk översättning",
"Enable_notifications": "Aktivera aviseringar",
"Encrypted": "Krypterat",
"Encrypted_message": "Krypterat meddelande",
"Enter_Your_E2E_Password": "Ange ditt E2E-lösenord",
"Enter_Your_Encryption_Password_desc1": "Det här gör att du kan komma åt dina krypterade privata grupper och direktmeddelanden.",
"Enter_Your_Encryption_Password_desc2": "Du måste ange lösenordet för att koda/avkoda meddelanden på varje plats där du använder chatten.",
"Encryption_error_title": "Ditt krypteringslösenord verkar vara felaktigt",
"Encryption_error_desc": "Det gick inte att avkoda krypteringsnyckeln som skulle importeras.",
"Everyone_can_access_this_channel": "Kanalen är öppen för alla",
"Everyone_can_access_this_team": "Teamet är öppet för alla",
"Error_uploading": "Fel vid uppladdning",
"Expiration_Days": "Förfallotid (dagar)",
"Favorites": "Favoriter",
"Files": "Filer",
"File_description": "Filbeskrivning",
"File_name": "Filnamn",
"Finish_recording": "Avsluta inspelningen",
"Following_thread": "Följer tråd",
"For_your_security_you_must_enter_your_current_password_to_continue": "För din egen säkerhet måste du ange ditt nuvarande lösenord för att fortsätta",
"Forgot_password_If_this_email_is_registered": "Om e-postadressen är registrerad skickar vi anvisningar för hur du återställer lösenordet. Om du inte får ett e-postmeddelande inom kort får du komma tillbaka och försöka igen.",
"Forgot_password": "Glömt lösenordet?",
"Forgot_Password": "Glömt lösenordet",
"Forward": "Vidarebefordra",
"Forward_Chat": "Vidarebefordra chatt",
"Forward_to_department": "Vidarebefordra till avdelning",
"Forward_to_user": "Vidarebefordra till användare",
"Full_table": "Klicka för att visa hela tabellen",
"Generate_New_Link": "Generera ny länk",
"Get_link": "Hämta länk",
"User_joined_the_channel": "anslöt till kanalen",
"User_joined_the_conversation": "anslöt till samtalet",
"User_joined_the_team": "gick med i teamet",
"User_left_this_channel": "lämnade kanalen",
"Has_left_the_team": "har lämnat kanalen",
"Hide_System_Messages": "Dölj systemmeddelanden",
"Hide_type_messages": "Dölj meddelanden av typen {{type}}",
"How_It_Works": "Så fungerar det",
"Message_HideType_uj": "Användare ansluter",
"Message_HideType_ul": "Användare lämnar",
"Message_HideType_ru": "Användare borttagen",
"Message_HideType_au": "Användare tillagd",
"Message_HideType_mute_unmute": "Användaren har tystats/ljudet har slagits på för användaren",
"Message_HideType_r": "Rummets namn har ändrats",
"Message_HideType_ut": "Användare har anslutit till samtalet",
"Message_HideType_wm": "Välkommen",
"Message_HideType_rm": "Meddelande borttaget",
"Message_HideType_subscription_role_added": "Fick rollen",
"Message_HideType_subscription_role_removed": "Rollen är inte definierad längre",
"Message_HideType_room_archived": "Rum arkiverat",
"Message_HideType_room_unarchived": "Rummet har avarkiverats",
"I_Saved_My_E2E_Password": "Jag har sparat mitt E2E-lösenord",
"IP": "IP",
"In_app": "I appen",
"In_App_And_Desktop": "I appen och på skrivbordet",
"In_App_and_Desktop_Alert_info": "En banderoll visas högst upp på skärmen och en avisering visas på skrivbordet när appen är öppen",
"Invisible": "Dold",
"Invite": "Bjud in",
"is_a_valid_RocketChat_instance": "är en giltig Rocket.Chat-instans",
"is_not_a_valid_RocketChat_instance": "är inte en giltig Rocket.Chat-instans",
"is_typing": "skriver",
"Invalid_or_expired_invite_token": "Ogiltig eller utgången inbjudningstoken",
"Invalid_server_version": "Den server du försöker ansluta till använder en version som appen inte har stöd för längre: {{currentVersion}}.\n\nDu måste ha version {{minVersion}}",
"Invite_Link": "Inbjudningslänk",
"Invite_users": "Bjud in användare",
"Join": "Gå med",
"Join_Code": "Anslutningskod",
"Insert_Join_Code": "Ange anslutningskoden",
"Join_our_open_workspace": "Anslut till vår öppna arbetsyta",
"Join_your_workspace": "Anslut till vår arbetsyta",
"Just_invited_people_can_access_this_channel": "Endast inbjudna har åtkomst till den här kanalen",
"Just_invited_people_can_access_this_team": "Endast inbjudna har åtkomst till det här teamet",
"Language": "Språk",
"last_message": "senaste meddelandet",
"Leave_channel": "Lämna kanal",
"leaving_room": "lämnar rummet",
"Leave": "Lämna",
"leave": "lämna",
"Legal": "Juridisk information",
"Light": "Ljus",
"License": "Licens",
"Livechat": "Livechatt",
"Livechat_edit": "Livechattredigering",
"Livechat_transfer_return_to_the_queue": "återförde chatten till kön",
"Login": "Logga in",
"Login_error": "Dina inloggningsuppgifter avvisades. Försök igen.",
"Login_with": "Logga in med",
"Logging_out": "Loggar ut.",
"Logout": "Logga ut",
"Max_number_of_uses": "Högsta antal användningar",
"Max_number_of_users_allowed_is_number": "Det högsta tillåtna antalet användare är {{maxUsers}}",
"members": "medlemmar",
"Members": "Medlemmar",
"Mentioned_Messages": "Omnämnda meddelanden",
"mentioned": "omnämnda",
"Mentions": "Omnämnanden",
"Message_accessibility": "Meddelande från {{user}} kl. {{time}}: {{message}}",
"Message_actions": "Åtgärder för meddelanden",
"Message_pinned": "Meddelandet har fästs",
"Message_removed": "meddelande borttaget",
"Message_starred": "Meddelandet har stjärnmarkerats",
"Message_unstarred": "Stjärnmarkering borttagen för meddelande",
"message": "meddelande",
"messages": "meddelanden",
"Message": "Meddelande",
"Messages": "Meddelanden",
"Message_Reported": "Meddelandet har anmälts",
"Microphone_Permission_Message": "Om du ska kunna skicka ljudmeddelanden behöver Rocket.Chat åtkomst till din mikrofon.",
"Microphone_Permission": "Mikrofonbehörighet",
"Mute": "Tysta",
"muted": "tystade",
"My_servers": "Mina servrar",
"N_people_reacted": "{{n}} personer reagerade",
"N_users": "{{n}} användare",
"N_channels": "{{n}} kanaler",
"Name": "Namn",
"Never": "Aldrig",
"New_chat_transfer": "Ny chattöverföring: {{agent}} återförde chatten till kön",
"New_Message": "Nytt meddelande",
"New_Password": "Nytt lösenord",
"New_Server": "Ny server",
"Next": "Nästa",
"No_files": "Inga filer",
"No_limit": "Ingen gräns",
"No_mentioned_messages": "Inga nämnda meddelanden",
"No_pinned_messages": "Inga fästa meddelanden",
"No_results_found": "Inga resultat hittades",
"No_members_found": "Inga medlemmar hittades",
"No_starred_messages": "Inga stjärnmarkerade meddelanden",
"No_thread_messages": "Inga trådmeddelanden",
"No_label_provided": "Ingen {{label}} har angetts.",
"No_Message": "Inga meddelanden",
"No_messages_yet": "Inga meddelanden ännu",
"No_Reactions": "Inga reaktioner",
"No_Read_Receipts": "Inga läskvitton",
"Not_logged": "Inte loggat",
"Not_RC_Server": "Det här är inte en Rocket.Chat-server.\n{{contact}}",
"Nothing": "Ingenting",
"Nothing_to_save": "Ingenting att spara.",
"Notify_active_in_this_room": "Meddela aktiva användare i rummet",
"Notify_all_in_this_room": "Meddela alla i rummet",
"Notifications": "Meddelanden",
"Notification_Duration": "Tidslängd för meddelande",
"Notification_Preferences": "Inställningar för meddelanden",
"No_available_agents_to_transfer": "Inga agenter att överföra till",
"Offline": "Offline",
"Oops": "Ojdå!",
"Omnichannel": "Omnichannel",
"Omnichannel_enable_alert": "Du är inte tillgänglig i Omnichannel. Vill du vara tillgänglig?",
"Onboarding_description": "En arbetsyta är en plats där ditt team eller din organisation kan samarbeta. Be arbetsyteadministratören om en adress att ansluta till eller skapa en åt teamet.",
"Onboarding_join_workspace": "Anslut till en arbetsyta",
"Onboarding_subtitle": "Mer än teamsamarbete",
"Onboarding_title": "Välkommen till Rocket.Chat",
"Onboarding_join_open_description": "Anslut till vår öppna arbetsyta där du kan chatta med Rocket.Chats team och community.",
"Onboarding_agree_terms": "Genom att fortsätta anses du ha godkänt Rocket.Chat",
"Onboarding_less_options": "Färre alternativ",
"Onboarding_more_options": "Fler alternativ",
"Online": "Online",
"Only_authorized_users_can_write_new_messages": "Endast behöriga användare kan skriva nya meddelanden",
"Open_emoji_selector": "Öppna emojiväljaren",
"Open_Source_Communication": "Kommunikation via öppen källkod",
"Open_your_authentication_app_and_enter_the_code": "Öppna autentiseringsappen och ange koden.",
"OR": "ELLER",
"OS": "Operativsystem",
"Overwrites_the_server_configuration_and_use_room_config": "Skriver över serverkonfigurationen och använder rumskonfigurationen",
"Password": "Lösenord",
"Parent_channel_or_group": "Överordnad kanal eller grupp",
"Permalink_copied_to_clipboard": "Permalänken har kopierats till urklipp.",
"Phone": "Telefonnummer",
"Pin": "Fäst",
"Pinned_Messages": "Fästa meddelanden",
"pinned": "fäst",
"Pinned": "Fästa",
"Please_add_a_comment": "Lägg till en kommentar",
"Please_enter_your_password": "Ange lösenordet",
"Please_wait": "Vänta.",
"Preferences": "Inställningar",
"Preferences_saved": "Inställningarna har sparats.",
"Privacy_Policy": "Integritetspolicy",
"Private": "Privat",
"Processing": "Bearbetar...",
"Profile_saved_successfully": "Profilen har sparats.",
"Profile": "Profil",
"Public_Channel": "Offentlig kanal",
"Public": "Offentlig",
"Push_Notifications": "Pushmeddelanden",
"Push_Notifications_Alert_Info": "De här meddelandena levereras till dig när appen är stängd",
"Quote": "Citat",
"Reactions_are_disabled": "Reaktioner är inaktiverade",
"Reactions_are_enabled": "Reaktioner är aktiverade",
"Reactions": "Reaktioner",
"Read_External_Permission_Message": "Rocket.Chat behöver åtkomst till foton, media och filer på enheten",
"Read_External_Permission": "Behörighet att läsa media",
"Read_Only": "Skrivskyddat",
"Read_Receipt": "Läskvitto",
"Receive_Group_Mentions": "Ta emot gruppomnämnanden",
"Receive_Group_Mentions_Info": "Ta emot omnämnanden av typen @all och @here",
"Register": "Registrera",
"Repeat_Password": "Upprepa lösenordet",
"Replied_on": "Svarade:",
"replies": "svar",
"reply": "svar",
"Reply": "Svara",
"Report": "Anmäl",
"Receive_Notification": "Ta emot meddelanden",
"Receive_notifications_from": "Ta emot meddelanden från {{name}}",
"Resend": "Skicka igen",
"Reset_password": "Återställ lösenordet",
"resetting_password": "återställer lösenordet",
"RESET": "ÅTERSTÄLL",
"Return_to_waiting_line": "Återgå till kön",
"Review_app_title": "Tycker du om appen?",
"Review_app_desc": "Ge oss fem stjärnor på {{store}}",
"Review_app_yes": "Visst!",
"Review_app_no": "Nej",
"Review_app_later": "Kanske senare",
"Review_app_unable_store": "Det går inte att öppna {{store}}",
"Review_this_app": "Betygsätt appen",
"Remove": "Ta bort",
"remove": "ta bort",
"Roles": "Roller",
"Room_actions": "Rumsåtgärder",
"Room_changed_announcement": "Rummets presentation har ändrats till: {{announcement}} av {{userBy}}",
"room_avatar_changed": "ändrade rummets avatar",
"Room_changed_description": "Rummets beskrivning har ändrats till: {{description}} av {{userBy}}",
"changed_room_description": "ändrade rummets beskrivning till: {{description}}",
"changed_room_announcement": "ändrade rummets presentation till: {{announcement}}",
"room_changed_type": "ändrade rummet till {{type}}",
"room_changed_topic_to": "ändrade rummets ämne till: {{topic}}",
"Room_Files": "Filer för rum",
"Room_Info_Edit": "Redigera information om rum",
"Room_Info": "Information om rum",
"Room_Members": "Rummets medlemmar",
"Room_name_changed_to": "ändrade rummets namn till: {{name}}",
"room_disallowed_reactions": "otillåtna reaktioner",
"room_allowed_reactions": "tillåtna reaktioner",
"room_removed_read_only_permission": "tog bort behörighet för skrivskyddad åtkomst",
"room_set_read_only_permission": "skrivskydda rummet",
"SAVE": "SPARA",
"Save_Changes": "Spara ändringar",
"Save": "Spara",
"Saved": "Sparade",
"saving_preferences": "sparar inställningarna",
"saving_profile": "sparar profilen",
"saving_settings": "sparar inställningarna",
"saved_to_gallery": "Sparat i galleriet",
"Save_Your_E2E_Password": "Spara ditt E2E-lösenord",
"Save_Your_Encryption_Password": "Spara ditt krypteringslösenord",
"Save_Your_Encryption_Password_warning": "Lösenordet lagras inte, så se till att spara det någon annanstans.",
"Save_Your_Encryption_Password_info": "Obs! Om du förlorar lösenordet går det inte att återställa det och du förlorar åtkomsten till alla dina meddelanden.",
"Search_Messages": "Sök bland meddelanden",
"Search": "Sök",
"Search_by": "Sök efter",
"Search_emoji": "Sök efter emoji",
"Search_global_users": "Sök efter globala användare",
"Search_global_users_description": "Om du aktiverar alternativet kan du söka efter användare från andra företag eller servrar.",
"Seconds": "{{second}} sekunder",
"Security_and_privacy": "Säkerhet och integritet",
"Select_Avatar": "Välj en avatar",
"Select_Server": "Välj server",
"Select_Users": "Välj användare",
"Select_a_Channel": "Välj en kanal",
"Select_a_Department": "Välj en avdelning",
"Select_an_option": "Välj ett alternativ",
"Select_a_User": "Välj en användare",
"Send": "Skicka",
"Send_audio_message": "Skicka ljudmeddelande",
"Send_crash_report": "Skicka kraschrapport",
"Send_message": "Skicka meddelande",
"Send_me_the_code_again": "Skicka mig koden igen",
"Send_to": "Skicka till...",
"Sending_to": "Skickar till",
"Sent_an_attachment": "Skickade en bilaga",
"Server": "Server",
"Servers": "Servrar",
"Server_version": "Serverversion: {{version}}",
"Set_username_subtitle": "Användarnamnet används till att låta andra nämna dig i meddelanden",
"Set_custom_status": "Ange anpassad status",
"Set_status": "Ange status",
"Status_saved_successfully": "Statusen har sparats.",
"Settings": "Inställningar",
"Settings_succesfully_changed": "Inställningarna har ändrats.",
"Share": "Dela",
"Share_Link": "Dela länk",
"Share_this_app": "Dela den här appen",
"Show_more": "Visa mer.",
"Sign_in_your_server": "Logga in på din server",
"Sign_Up": "Registrera dig",
"Some_field_is_invalid_or_empty": "Vissa fält är ogiltiga eller tomma",
"Sound": "Ljud",
"Star_room": "Stjärnmarkera rum",
"Star": "Stjärna",
"Starred_Messages": "Stjärnmarkerade meddelanden",
"starred": "stjärnmarkerat",
"Starred": "Stjärnmarkerat",
"Start_of_conversation": "Början på konversation",
"Start_a_Discussion": "Starta en diskussion",
"Started_discussion": "Startade en diskussion:",
"Started_call": "Samtalet startades av {{userBy}}",
"Submit": "Skicka",
"Table": "Tabell",
"Tags": "Taggar",
"Take_a_photo": "Ta ett foto",
"Take_a_video": "Spela in en video",
"Take_it": "Spela in!",
"tap_to_change_status": "tryck för att ändra status",
"Tap_to_view_servers_list": "Tryck för att visa serverlistan",
"Terms_of_Service": "Användarvillkor",
"Theme": "Tema",
"The_user_wont_be_able_to_type_in_roomName": "Användaren kan inte ange {{roomName}}",
"The_user_will_be_able_to_type_in_roomName": "Användaren kan ange {{roomName}}",
"There_was_an_error_while_action": "Ett fel inträffade vid {{action}}.",
"This_room_is_blocked": "Rummet är blockerat",
"This_room_is_read_only": "Rummet är skrivskyddat",
"Thread": "Tråd",
"Threads": "Trådar",
"Timezone": "Tidszon",
"To": "Till",
"topic": "ämne",
"Topic": "Ämne",
"Translate": "Översätt",
"Try_again": "Försök igen",
"Two_Factor_Authentication": "Tvåfaktorsautentisering",
"Type_the_channel_name_here": "Ange kanalens namn här",
"unarchive": "avarkivera",
"UNARCHIVE": "AVARKIVERA",
"Unblock_user": "Avblockera användare",
"Unfollowed_thread": "Sluta följa tråd",
"Unmute": "Slå på ljudet",
"unmuted": "slog på ljudet",
"Unpin": "Lossa",
"unread_messages": "oläst",
"Unread": "Oläst",
"Unread_on_top": "Olästa överst",
"Unstar": "Ta bort stjärnmarkering",
"Unsupported_system_message": "Systemmeddelande som inte stöds",
"Updating": "Uppdaterar...",
"Uploading": "Laddar upp",
"Upload_file_question_mark": "Ladda upp fil?",
"User": "Användare",
"Users": "Användare",
"User_added_to": "lade till {{userAdded}}",
"User_Info": "Användarinfo",
"User_has_been_key": "Användaren har {{key}}",
"User_is_no_longer_role_by_": "{{user}} har inte längre rollen {{role}} per {{userBy}}",
"User_has_been_muted": "tystade {{userMuted}}",
"User_has_been_removed": "tog bort {{userRemoved}}",
"User_sent_an_attachment": "{{user}} skickade en bilaga",
"User_has_been_unmuted": "slog på ljudet för {{userUnmuted}}",
"Defined_user_as_role": "angav {{user}} som {{role}}",
"Removed_user_as_role": "tog bort {{user}} som {{role}}",
"Username_is_empty": "Användarnamnet har inte angetts",
"Username": "Användarnamn",
"Username_or_email": "Användarnamn eller e-postadress",
"Uses_server_configuration": "Använder serverkonfiguration",
"Validating": "Validerar",
"Registration_Succeeded": "Registreringen är klar.",
"Verify": "Verifiera",
"Verify_email_title": "Registreringen är klar.",
"Verify_email_desc": "Vi har skickat ett e-postmeddelande för att bekräfta din registrering. Om du inte får e-postmeddelandet försöker du igen.",
"Verify_your_email_for_the_code_we_sent": "Titta efter koden i din e-post",
"Video_call": "Videosamtal",
"View_Original": "Visa original",
"Voice_call": "Röstsamtal",
"Waiting_for_network": "Väntar på nätverket...",
"Websocket_disabled": "Websocket är inaktiverad för servern.\n{{contact}}",
"Welcome": "Välkommen",
"What_are_you_doing_right_now": "Vad gör du just nu?",
"Whats_your_2fa": "Vilken är din kod för tvåfaktorsautentisering?",
"Without_Servers": "Utan servrar",
"Workspaces": "Arbetsytor",
"Would_you_like_to_return_the_inquiry": "Vill du skicka tillbaka förfrågan?",
"Write_External_Permission_Message": "Rocket.Chat behöver åtkomst till ditt galleri så att du kan spara bilder.",
"Write_External_Permission": "Galleribehörighet",
"Yes": "Ja",
"Yes_action_it": "Ja, {{action}}!",
"Yesterday": "Igår",
"You_are_in_preview_mode": "Du är i förhandsgranskningsläge",
"You_are_offline": "Du är offline",
"You_can_search_using_RegExp_eg": "Du kan använda reguljära uttryck, till exempel /^text$/i",
"You_colon": "Du:",
"you_were_mentioned": "du nämndes",
"You_were_removed_from_channel": "Du togs bort från {{channel}}",
"you": "du",
"You": "Du",
"Logged_out_by_server": "Du har loggats ut av servern. Logga in på nytt.",
"Token_expired": "Din session har upphört. Logga in igen.",
"You_need_to_access_at_least_one_RocketChat_server_to_share_something": "Du behöver åtkomst till åtminstone en Rocket.Chat-server för att kunna dela.",
"You_need_to_verifiy_your_email_address_to_get_notications": "Du måste bekräfta din e-postadress för att få meddelanden",
"Your_certificate": "Ditt certifikat",
"Your_invite_link_will_expire_after__usesLeft__uses": "Inbjudningslänken upphör att gälla efter {{usesLeft}} användningar.",
"Your_invite_link_will_expire_on__date__or_after__usesLeft__uses": "Inbjudningslänken upphör att gälla den {{date}} eller efter {{usesLeft}} användningar.",
"Your_invite_link_will_expire_on__date__": "Inbjudningslänken upphör att gälla den {{date}}.",
"Your_invite_link_will_never_expire": "Inbjudningslänken upphör aldrig att gälla.",
"Your_workspace": "Din arbetsyta",
"Your_password_is": "Ditt lösenord är",
"Version_no": "Version: {{version}}",
"You_will_not_be_able_to_recover_this_message": "Du kan inte återställa meddelandet senare.",
"You_will_unset_a_certificate_for_this_server": "Du tar bort ett certifikat för servern",
"Change_Language": "Ändra språk",
"Crash_report_disclaimer": "Vi spårar aldrig innehållet i chattar. Kraschrapporten och analyshändelserna innehåller bara information som vi behöver för att kunna identifiera och åtgärda fel.",
"Type_message": "Skriv meddelande",
"Room_search": "Sök efter rum",
"Room_selection": "Val av rum: 1 till 9",
"Next_room": "Nästa rum",
"Previous_room": "Föregående rum",
"New_room": "Nytt rum",
"Upload_room": "Ladda upp till rum",
"Search_messages": "Sök efter meddelanden",
"Scroll_messages": "Bläddra bland meddelanden",
"Reply_latest": "Svara på senaste",
"Reply_in_Thread": "Svara i tråden",
"Server_selection": "Val av server",
"Server_selection_numbers": "Val av server: 1 till 9",
"Add_server": "Lägg till server",
"New_line": "Ny rad",
"You_will_be_logged_out_of_this_application": "Du loggas ut från programmet",
"Clear": "Rensa",
"This_will_clear_all_your_offline_data": "Med den här åtgärden rensas alla dina offlinedata.",
"This_will_remove_all_data_from_this_server": "Med den här åtgärden tas alla data bort från servern.",
"Mark_unread": "Markera som oläst",
"Wait_activation_warning": "Innan du kan logga in måste ditt konto aktiveras manuellt av en administratör.",
"Screen_lock": "Skärmlås",
"Local_authentication_biometry_title": "Autentisering",
"Local_authentication_biometry_fallback": "Använd lösenkod",
"Local_authentication_unlock_option": "Lås upp med lösenkod",
"Local_authentication_change_passcode": "Ändra lösenkod",
"Local_authentication_info": "Obs! Om du glömmer bort lösenkoden måste du ta bort och sedan installera appen på nytt.",
"Local_authentication_facial_recognition": "ansiktsigenkänning",
"Local_authentication_fingerprint": "fingeravtryck",
"Local_authentication_unlock_with_label": "Lås upp med {{label}}",
"Local_authentication_auto_lock_60": "Efter 1 minut",
"Local_authentication_auto_lock_300": "Efter 5 minuter",
"Local_authentication_auto_lock_900": "Efter 15 minuter",
"Local_authentication_auto_lock_1800": "Efter 30 minuter",
"Local_authentication_auto_lock_3600": "Efter 1 timme",
"Passcode_enter_title": "Ange din lösenkod",
"Passcode_choose_title": "Välj en ny lösenkod",
"Passcode_choose_confirm_title": "Bekräfta den nya lösenkoden",
"Passcode_choose_error": "Lösenkoderna matchar inte. Försök igen.",
"Passcode_choose_force_set": "En lösenkod krävs av administratören",
"Passcode_app_locked_title": "Appen har låsts",
"Passcode_app_locked_subtitle": "Försök igen om {{timeLeft}} sekunder",
"After_seconds_set_by_admin": "Efter {{seconds}} sekunder (anges av administratören)",
"Dont_activate": "Aktivera inte nu",
"Queued_chats": "Köplacerade chattar",
"Logout_from_other_logged_in_locations": "Logga ut från andra platser där du är inloggad",
"You_will_be_logged_out_from_other_locations": "Du loggas ut från andra platser.",
"Logged_out_of_other_clients_successfully": "Du har loggats ut från andra klienter",
"Logout_failed": "Det gick inte att logga ut.",
"Log_analytics_events": "Logga analyshändelser",
"E2E_encryption_change_password_title": "Ändra krypteringslösenord",
"E2E_encryption_change_password_description": "Nu kan du skapa krypterade privata grupper och direktmeddelanden. Du kan också kryptera befintliga privata grupper och direktmeddelande. \nHär används end-to-end-kryptering, så nyckeln för att koda/avkoda dina meddelanden sparas inte på servern. Därför måste du spara ditt lösenord på en säker plats. Du behöver ange den på andra enheter som du vill använda end-to-end-kryptering på.",
"E2E_encryption_change_password_error": "Ett fel inträffade när lösenordet för E2E-nyckeln skulle ändras.",
"E2E_encryption_change_password_success": "Lösenordet för E2E-nyckeln har ändrats.",
"E2E_encryption_change_password_message": "Se till att spara det på en säker plats.",
"E2E_encryption_change_password_confirmation": "Ja, ändra",
"E2E_encryption_reset_title": "Återställ E2E-nyckel",
"E2E_encryption_reset_description": "Med det här alternativet tas din aktuella E2E-nyckel bort och du loggas ut. \nNär du loggar in igen genererar Rocket.Chat en ny nyckel åt dig och återställer din åtkomst till krypterade rum för vilka en eller flera aktiva medlemmar är online. \nPå grund av hur E2E-krypteringen fungerar kan Rocket.Chat inte återställa åtkomsten till krypterade rum som inte har medlemmar online.",
"E2E_encryption_reset_button": "Återställ E2E-nyckel",
"E2E_encryption_reset_error": "Fel vid återställning av E2E-nyckel.",
"E2E_encryption_reset_message": "Du loggas ut.",
"E2E_encryption_reset_confirmation": "Ja, återställ",
"Following": "Följer",
"Threads_displaying_all": "Visar alla",
"Threads_displaying_following": "Visar de du följer",
"Threads_displaying_unread": "Visar olästa",
"No_threads": "Det finns inga trådar",
"No_threads_following": "Du följer inte några trådar",
"No_threads_unread": "Det finns inga olästa trådar",
"Messagebox_Send_to_channel": "Skicka till kanal",
"Leader": "Ledare",
"Moderator": "Moderator",
"Owner": "Ägare",
"Remove_from_room": "Ta bort från rummet",
"Ignore": "Ignorera",
"Unignore": "Sluta ignorera",
"User_has_been_ignored": "Användaren ignoreras",
"User_has_been_unignored": "Användaren ignoreras inte längre",
"User_has_been_removed_from_s": "Användaren har tagits bort från {{s}}",
"User__username__is_now_a_leader_of__room_name_": "Användaren {{username}} är nu ledare för {{room_name}}",
"User__username__is_now_a_moderator_of__room_name_": "Användaren {{username}} är nu moderator för{{room_name}}",
"User__username__is_now_a_owner_of__room_name_": "Användaren {{username}} är nu ägare av {{room_name}}",
"User__username__removed_from__room_name__leaders": "Användaren {{username}} har tagits bort som en av ledarna för {{room_name}}",
"User__username__removed_from__room_name__moderators": "Användaren {{username}} har tagits bort som en av moderatorerna för {{room_name}}",
"User__username__removed_from__room_name__owners": "Användaren {{username}} har tagits bort som en av ägarna av {{room_name}}",
"The_user_will_be_removed_from_s": "Användaren tas bort från {{s}}",
"Yes_remove_user": "Ja. Ta bort användaren.",
"Direct_message": "Direktmeddelande",
"Message_Ignored": "Meddelandet har ignorerats. Tryck på det om du vill visa det.",
"Enter_workspace_URL": "Ange arbetsytans URL",
"Workspace_URL_Example": "Till exempel ditt-foretag.rocket.chat",
"Enabled_E2E_Encryption_for_this_room": "aktivera E2E-kryptering för det här rummet",
"Disabled_E2E_Encryption_for_this_room": "inaktivera E2E-kryptering för det här rummet",
"Teams": "Team",
"No_team_channels_found": "Inga kanaler hittades",
"Team_not_found": "Teamet hittades inte",
"Create_Team": "Skapa team",
"Team_Name": "Teamnamn",
"creating_team": "skapar team",
"team-name-already-exists": "Det finns redan ett team med det namnet",
"Add_Channel_to_Team": "Lägg till kanal för team",
"Left_The_Team_Successfully": "Lämnade teamet",
"Create_New": "Skapa ny",
"Add_Existing": "Lägg till befintlig",
"Add_Existing_Channel": "Lägg till befintlig kanal",
"Remove_from_Team": "Ta bort från team",
"Auto-join": "Anslut automatiskt",
"Remove_Team_Room_Warning": "Vill du ta bort kanalen från teamet? Kanalen flyttas tillbaka till arbetsytan",
"Confirmation": "Bekräftelse",
"invalid-room": "Ogiltigt rum",
"You_are_leaving_the_team": "Du lämnar teamet {{team}}",
"Leave_Team": "Lämna team",
"Select_Team": "Välj team",
"Select_Team_Channels": "Välj de teamkanaler du vill lämna.",
"Cannot_leave": "Kan inte lämna",
"Cannot_remove": "Kan inte ta bort",
"Cannot_delete": "Kan inte ta bort",
"Last_owner_team_room": "Du är den sista ägaren av kanalen. När du har lämnat teamet behålls kanalen i teamet, men du får hantera den utifrån.",
"last-owner-can-not-be-removed": "Den sista ägaren kan inte tas bort",
"Remove_User_Teams": "Välj de kanaler du vill ta bort användaren från.",
"Deleting_account": "Tar bort kontot",
"Delete_my_account": "Ta bort mitt konto",
"Delete_Team": "Ta bort team",
"Select_channels_to_delete": "Åtgärden kan inte ångras. När du tar bort ett team tas allt chattinnehåll och alla inställningar bort. \n\nVälj de kanaler du vill ta bort. De du bestämmer dig för att behålla är tillgängliga i arbetsytan. Tänk på att offentliga kanaler fortfarande är offentliga och synliga för alla.",
"You_are_deleting_the_team": "Du håller på att ta bort det här teamet.",
"Removing_user_from_this_team": "Du tar bort {{user}} från det här teamet",
"Remove_User_Team_Channels": "Välj de kanaler du vill ta bort användaren från.",
"Remove_Member": "Ta bort medlem",
"leaving_team": "lämnar team",
"removing_team": "tar bort från team",
"moving_channel_to_team": "flyttar kanal till team",
"deleting_team": "tar bort team",
"member-does-not-exist": "Medlemmen finns inte",
"Convert": "Konvertera",
"Convert_to_Team": "Konvertera till team",
"Convert_to_Team_Warning": "Du konverterar kanalen till ett team. Alla medlemmar behålls.",
"Move_to_Team": "Flytta till team",
"Move_Channel_Paragraph": "Om du flyttar in en kanal i ett team läggs kanalen till i teamets kontext. Alla kanalmedlemmar som inte är medlemmar i respektive team har dock fortfarande åtkomst till kanalen, men läggs inte till som teammedlemmar. \n\nAll kanalhantering görs fortfarande av kanalens ägare.\n\nTeammedlemmar och teamägare som inte är medlemmar i kanalen får inte ha åtkomst till kanalens innehåll. \n\nObs! Teamets ägare kan ta bort medlemmar från kanalen.",
"Move_to_Team_Warning": "Vill du fortfarande flytta kanalen till det valda teamet nu när du har läst informationen om den här funktionen?",
"Load_More": "Läs in fler",
"Load_Newer": "Läs in nyare",
"Load_Older": "Läs in äldre",
"room-name-already-exists": "Rummets namn används redan",
"error-team-creation": "Ett fel inträffade när teamet skulle skapas",
"unauthorized": "Obehörig",
"Left_The_Room_Successfully": "Lämnade rummet",
"Deleted_The_Team_Successfully": "Teamet har tagits bort",
"Deleted_The_Room_Successfully": "Rummet har tagits bort",
"Convert_to_Channel": "Konvertera till kanal",
"Converting_Team_To_Channel": "Konverterar team till kanal",
"Select_Team_Channels_To_Delete": "Välj den teamkanal du vill ta bort. De du inte väljer flyttas till arbetsytan. \n\nTänk på att offentliga kanaler är öppna och synliga för alla.",
"You_are_converting_the_team": "Du konverterar det här teamet till en kanal",
"Display": "Visa",
"Avatars": "Avatarer",
"Sort_by": "Sortera per",
"Group_by": "Gruppera per",
"Types": "Typer",
"Expanded": "Expanderade",
"Condensed": "Komprimerade",
"creating_discussion": "skapar diskussion",
"Canned_Responses": "Standardsvar",
"No_match_found": "Inga resultat hittades.",
"No_discussions": "Inga diskussioner",
"Check_canned_responses": "Kontrollera standardsvar.",
"Searching": "Söker",
"Use": "Använd",
"Shortcut": "Genväg",
"Content": "Innehåll",
"Sharing": "Delar",
"No_canned_responses": "Inga standardsvar",
"Send_email_confirmation": "Skicka e-postbekräftelse",
"sending_email_confirmation": "skickar e-postbekräftelse",
"Enable_Message_Parser": "Aktivera meddelandetolken",
"Unsupported_format": "Format som inte stöds",
"Downloaded_file": "Nedladdad fil",
"Error_Download_file": "Fel vid nedladdning av fil",
"added__roomName__to_this_team": "lade till #{{roomName}} till teamet",
"Added__username__to_this_team": "lade till @{{user_added}} i teamet",
"Converting_team_to_channel": "Konverterar team till kanal",
"Converted__roomName__to_a_team": "konverterade #{{roomName}} till ett team",
"Converted__roomName__to_a_channel": "konverterade #{{roomName}} till kanal",
"Deleted__roomName__": "tog bort #{{roomName}}",
"Message_HideType_added_user_to_team": "Dölj meddelandet \"Användare lades till i teamet\"",
"Message_HideType_removed_user_from_team": "Dölj meddelandet \"Användare togs bort från teamet\"",
"Message_HideType_ujt": "Dölj meddelandet \"Användare anslöt till teamet\"",
"Message_HideType_ult": "Dölj meddelandet \"Användare lämnade teamet\"",
"Message_HideType_user_added_room_to_team": "Dölj meddelandet \"Användaren lade till rummet till teamet\"",
"Message_HideType_user_converted_to_channel": "Dölj meddelandet \"Användaren konverterade teamet till en kanal\"",
"Message_HideType_user_converted_to_team": "Dölj meddelandet \"Användaren konverterade kanalen till ett team\"",
"Message_HideType_user_deleted_room_from_team": "Dölj meddelandet \"Användaren tog bort rummet från teamet\"",
"Message_HideType_user_removed_room_from_team": "Dölj meddelandet \"Användaren tog bort rummet från teamet\"",
"Removed__roomName__from_the_team": "tog bort #{{roomName}} från teamet",
"Removed__username__from_the_team": "tog bort @{{userRemoved}} från teamet",
"User_joined_team": "anslöt till teamet",
"User_left_team": "lämnade teamet",
"Place_chat_on_hold": "Parkera chatten",
"Would_like_to_place_on_hold": "Vill du parkera chatten?",
"Open_Livechats": "Pågående Omnichannel-chattar",
"On_hold_Livechats": "Parkerade Omnichannel-chattar",
"Chat_is_on_hold": "Den här chatten är parkerad på grund av inaktivitet",
"Resume": "Återuppta",
"Omnichannel_placed_chat_on_hold": "Parkerad chatt: {{comment}}",
"Omnichannel_on_hold_chat_resumed": "Återupptog parkerad chatt: {{comment}}",
"Omnichannel_queue": "Omnichannel-kö",
"Empty": "Tom",
"Mark_as_unread": "Markera som oläst",
"Mark_as_unread_Info": "Visa rummet som oläst när det finns olästa meddelanden",
"Show_badge_for_mentions": "Visa märke för omnämnanden",
"Show_badge_for_mentions_Info": "Visa endast märke för direkta omnämnanden",
"error-init-video-conf": "Ett fel inträffade när videosamtalet skulle startas",
"totp-invalid": "Koden eller lösenordet är ogiltigt",
"Close_Chat": "Stäng chatt",
"Select_tags": "Välj taggar",
"Skip": "Hoppa över",
"N_Selected_members": "{{n}} har valts",
"Broadcast": "Sändning",
"Broadcast_hint": "Det är bara behöriga användare som kan skriva nya meddelanden, men alla andra användare kan svara",
"Team_hint_private": "Endast inbjudna personer kan ansluta",
"Team_hint_public": "När alternativet är inaktiverat kan vem som helst ansluta till teamet",
"Team_hint_not_read_only": "Alla användare i teamet kan skriva meddelanden",
"Team_hint_encrypted": "End-to-end-krypterat team. Sökfunktionen fungerar inte med krypterade team och innehållet i meddelanden kanske inte visas.",
"Team_hint_encrypted_not_available": "Endast tillgängligt för privata team",
"Channel_hint_private": "Endast inbjudna användare kan öppna kanalen",
"Channel_hint_public": "Alla kan öppna kanalen",
"Channel_hint_encrypted": "End-to-end-krypterad kanal. Sökfunktionen fungerar inte med krypterade kanaler och innehållet i meddelanden kanske inte visas.",
"Channel_hint_not_read_only": "Alla användare i kanalen kan skriva meddelanden",
"Channel_hint_encrypted_not_available": "Inte tillgängligt för offentliga kanaler",
"Read_only_hint": "Endast behöriga användare kan skriva nya meddelanden",
"Discussion": "Diskussion",
"Channel": "Kanal",
"Team": "Team",
"Select_Members": "Välj medlemmar",
"Also_send_thread_message_to_channel_behavior": "Skicka även trådmeddelande till kanalen",
"Accounts_Default_User_Preferences_alsoSendThreadToChannel_Description": "Tillåt användare att välja alternativet Skicka även till kanal",
"Waiting_for_answer": "Väntar på svar",
"Call_ended": "Samtal avslutat",
"Call_was_not_answered": "Samtalet besvarades inte",
"Call_back": "Ring upp",
"Call_again": "Ring igen",
"Call_ongoing": "Samtal pågår",
"Joined": "Anslöt",
"Calling": "Ringer upp...",
"Start_a_call": "Starta ett samtal",
"Call": "Ring",
"Reply_in_direct_message": "Svara med direktmeddelande",
"room_archived": "arkiverade rum",
"room_unarchived": "avarkiverade rum"
}

View File

@ -302,6 +302,7 @@
"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",
@ -668,5 +669,6 @@
"Enter_workspace_URL": "Çalışma alanı URL'nizi girin",
"Workspace_URL_Example": "Örn. sirketiniz.rocket.chat",
"invalid-room": "Geçersiz oda",
"Open_Livechats": "Devam Eden Sohbetler",
"Broadcast_hint": "Yalnızca yetkili kullanıcılar yeni ileti yazabilir, ancak diğer kullanıcılar yanıt verebilir"
}

View File

@ -177,6 +177,8 @@
"delete": "刪除",
"Delete": "刪除",
"DELETE": "刪除",
"Delete_Account": "刪除帳號",
"Delete_Account_confirm": "是的,請刪除",
"deleting_room": "正在刪除聊天室",
"description": "描述",
"Description": "描述",
@ -222,6 +224,7 @@
"Encryption_error_title": "您的加密密碼似乎有誤",
"Encryption_error_desc": "無法使用匯入的加密金鑰來解密",
"Everyone_can_access_this_channel": "所有人皆可存取此頻道",
"Everyone_can_access_this_team": "所有人皆可存取此團隊",
"Error_uploading": "錯誤上傳",
"Expiration_Days": "到期 (日)",
"Favorites": "我的最愛",
@ -301,6 +304,7 @@
"Message_accessibility": "{{time}}來自{{user}}的訊息: {{message}}",
"Message_actions": "訊息操作",
"Message_pinned": "訊息被釘選",
"Message_removed": "訊息被刪除",
"Message_starred": "訊息被標註",
"Message_unstarred": "訊息被取消標註",
"message": "訊息",
@ -650,5 +654,6 @@
"Messagebox_Send_to_channel": "發送至頻道",
"Confirmation": "確認",
"invalid-room": "無效的房間",
"Open_Livechats": "打開即時聊天",
"Broadcast_hint": "只有經過授權的使用者才能發送新訊息,但其他使用者可以回覆"
}

View File

@ -1,15 +1,20 @@
const localeKeys: { [key: string]: string } = {
en: 'en',
ru: 'ru',
'pt-BR': 'pt-br',
'zh-CN': 'zh-cn',
fr: 'fr',
ar: 'ar',
de: 'de',
'pt-PT': 'pt',
'es-ES': 'es',
fi: 'fi',
fr: 'fr',
it: 'it',
ja: 'ja',
nl: 'nl',
'es-ES': 'es',
'pt-BR': 'pt-br',
'pt-PT': 'pt',
ru: 'ru',
'sl-SI': 'sl',
sv: 'sv',
tr: 'tr',
'zh-CN': 'zh-cn',
'zh-TW': 'zh-tw'
};

View File

@ -70,6 +70,22 @@ export const colors = {
collapsibleQuoteBorder: '#CBCED1',
collapsibleChevron: '#6C727A',
cancelButton: '#E4E7EA',
conferenceCallBorder: '#F2F3F5',
conferenceCallBackground: '#F7F8FA',
conferenceCallOngoingPhoneBackground: '#C0F6E4',
conferenceCallIncomingPhoneBackground: '#D1EBFE',
conferenceCallEndedPhoneBackground: '#E4E7EA',
conferenceCallOngoingPhoneIcon: '#158D65',
conferenceCallIncomingPhoneIcon: '#095AD2',
conferenceCallEndedPhoneIcon: '#6C727A',
conferenceCallPlusUsersButton: '#E4E7EA',
conferenceCallPlusUsersText: '#6C727A',
conferenceCallCallBackButton: '#EEEFF1',
conferenceCallCallBackText: '#1F2329',
conferenceCallDisabledIcon: '#6C727A',
conferenceCallEnabledIcon: '#FFFFFF',
conferenceCallEnabledIconBackground: '#156FF5',
conferenceCallPhotoBackground: '#E4E7EA',
textInputSecondaryBackground: '#E4E7EA',
...mentions
},
@ -123,6 +139,22 @@ export const colors = {
collapsibleQuoteBorder: '#CBCED1',
collapsibleChevron: '#6C727A',
cancelButton: '#E4E7EA',
conferenceCallBorder: '#1F2329',
conferenceCallBackground: '#1F2329',
conferenceCallOngoingPhoneBackground: '#106D4F',
conferenceCallIncomingPhoneBackground: '#D1EBFE',
conferenceCallEndedPhoneBackground: '#6C727A',
conferenceCallOngoingPhoneIcon: '#F7F8FA',
conferenceCallIncomingPhoneIcon: '#095AD2',
conferenceCallEndedPhoneIcon: '#F7F8FA',
conferenceCallPlusUsersButton: '#2F343D',
conferenceCallPlusUsersText: '#9EA2A8',
conferenceCallCallBackButton: '#E4E7EA',
conferenceCallCallBackText: '#FFFFFF',
conferenceCallDisabledIcon: '#6C727A',
conferenceCallEnabledIcon: '#FFFFFF',
conferenceCallEnabledIconBackground: '#156FF5',
conferenceCallPhotoBackground: '#E4E7EA',
textInputSecondaryBackground: '#030b1b', // backgroundColor
...mentions
},
@ -176,6 +208,22 @@ export const colors = {
collapsibleQuoteBorder: '#CBCED1',
collapsibleChevron: '#6C727A',
cancelButton: '#E4E7EA',
conferenceCallBorder: '#1F2329',
conferenceCallBackground: '#1F2329',
conferenceCallOngoingPhoneBackground: '#106D4F',
conferenceCallIncomingPhoneBackground: '#D1EBFE',
conferenceCallEndedPhoneBackground: '#6C727A',
conferenceCallOngoingPhoneIcon: '#F7F8FA',
conferenceCallIncomingPhoneIcon: '#095AD2',
conferenceCallEndedPhoneIcon: '#F7F8FA',
conferenceCallPlusUsersButton: '#2F343D',
conferenceCallPlusUsersText: '#9EA2A8',
conferenceCallCallBackButton: '#E4E7EA',
conferenceCallCallBackText: '#FFFFFF',
conferenceCallDisabledIcon: '#6C727A',
conferenceCallEnabledIcon: '#FFFFFF',
conferenceCallEnabledIconBackground: '#156FF5',
conferenceCallPhotoBackground: '#E4E7EA',
textInputSecondaryBackground: '#000000', // backgroundColor
...mentions
}

View File

@ -123,6 +123,8 @@ export default class Subscription extends Model {
@field('e2e_key') E2EKey;
@field('e2e_suggested_key') E2ESuggestedKey;
@field('encrypted') encrypted;
@field('e2e_key_id') e2eKeyId;

View File

@ -16,6 +16,8 @@ export default class Upload extends Model {
@field('name') name;
@field('tmid') tmid;
@field('description') description;
@field('size') size;

View File

@ -239,6 +239,24 @@ export default schemaMigrations({
columns: [{ name: 'hide_mention_status', type: 'boolean', isOptional: true }]
})
]
},
{
toVersion: 19,
steps: [
addColumns({
table: 'uploads',
columns: [{ name: 'tmid', type: 'string', isOptional: true }]
})
]
},
{
toVersion: 20,
steps: [
addColumns({
table: 'subscriptions',
columns: [{ name: 'e2e_suggested_key', type: 'string', isOptional: true }]
})
]
}
]
});

View File

@ -1,7 +1,7 @@
import { appSchema, tableSchema } from '@nozbe/watermelondb';
export default appSchema({
version: 18,
version: 20,
tables: [
tableSchema({
name: 'subscriptions',
@ -55,6 +55,7 @@ export default appSchema({
{ name: 'livechat_data', type: 'string', isOptional: true },
{ name: 'tags', type: 'string', isOptional: true },
{ name: 'e2e_key', type: 'string', isOptional: true },
{ name: 'e2e_suggested_key', type: 'string', isOptional: true },
{ name: 'encrypted', type: 'boolean', isOptional: true },
{ name: 'e2e_key_id', type: 'string', isOptional: true },
{ name: 'avatar_etag', type: 'string', isOptional: true },
@ -222,6 +223,7 @@ export default appSchema({
{ name: 'path', type: 'string', isOptional: true },
{ name: 'rid', type: 'string', isIndexed: true },
{ name: 'name', type: 'string', isOptional: true },
{ name: 'tmid', type: 'string', isOptional: true },
{ name: 'description', type: 'string', isOptional: true },
{ name: 'size', type: 'number' },
{ name: 'type', type: 'string', isOptional: true },

View File

@ -34,6 +34,7 @@ class Encryption {
handshake: Function;
decrypt: Function;
encrypt: Function;
importRoomKey: Function;
};
};
@ -97,6 +98,10 @@ class Encryption {
});
};
stopRoom = (rid: string) => {
delete this.roomInstances[rid];
};
// When a new participant join and request a new room encryption key
provideRoomKeyToUser = async (keyId: string, rid: string) => {
// If the client is not ready
@ -220,6 +225,19 @@ class Encryption {
return roomE2E;
};
evaluateSuggestedKey = async (rid: string, E2ESuggestedKey: string) => {
try {
if (this.privateKey) {
const roomE2E = await this.getRoomInstance(rid);
await roomE2E.importRoomKey(E2ESuggestedKey, this.privateKey);
delete this.roomInstances[rid];
await Services.e2eAcceptSuggestedGroupKey(rid);
}
} catch (e) {
await Services.e2eRejectSuggestedGroupKey(rid);
}
};
// Logic to decrypt all pending messages/threads/threadMessages
// after initialize the encryption client
decryptPendingMessages = async (roomId?: string) => {

View File

@ -74,7 +74,10 @@ export default class EncryptionRoom {
if (E2EKey && Encryption.privateKey) {
// We're establishing a new room encryption client
this.establishing = true;
await this.importRoomKey(E2EKey, Encryption.privateKey);
const { keyID, roomKey, sessionKeyExportedString } = await this.importRoomKey(E2EKey, Encryption.privateKey);
this.keyID = keyID;
this.roomKey = roomKey;
this.sessionKeyExportedString = sessionKeyExportedString;
this.readyPromise.resolve();
return;
}
@ -96,20 +99,33 @@ export default class EncryptionRoom {
};
// Import roomKey as an AES Decrypt key
importRoomKey = async (E2EKey: string, privateKey: string) => {
importRoomKey = async (
E2EKey: string,
privateKey: string
): Promise<{ sessionKeyExportedString: string | ByteBuffer; roomKey: ArrayBuffer; keyID: string }> => {
try {
const roomE2EKey = E2EKey.slice(12);
const decryptedKey = await SimpleCrypto.RSA.decrypt(roomE2EKey, privateKey);
this.sessionKeyExportedString = toString(decryptedKey);
const sessionKeyExportedString = toString(decryptedKey);
this.keyID = Base64.encode(this.sessionKeyExportedString as string).slice(0, 12);
const keyID = Base64.encode(sessionKeyExportedString as string).slice(0, 12);
// Extract K from Web Crypto Secret Key
// K is a base64URL encoded array of bytes
// Web Crypto API uses this as a private key to decrypt/encrypt things
// Reference: https://www.javadoc.io/doc/com.nimbusds/nimbus-jose-jwt/5.1/com/nimbusds/jose/jwk/OctetSequenceKey.html
const { k } = EJSON.parse(this.sessionKeyExportedString as string);
this.roomKey = b64ToBuffer(k);
const { k } = EJSON.parse(sessionKeyExportedString as string);
const roomKey = b64ToBuffer(k);
return {
sessionKeyExportedString,
roomKey,
keyID
};
} catch (e: any) {
throw new Error(e);
}
};
// Create a key to a room

14
app/lib/hooks/useSnaps.ts Normal file
View File

@ -0,0 +1,14 @@
import { useDimensions } from '@react-native-community/hooks';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
// Not sure if it's worth adding this here in the context of the actionSheet
/**
* Return the snaps based on the size you pass (aka: Size of action sheet)
* @param {Number[]} snaps Sizes you want to pass, pass only one if you want the action sheet to start at a specific size
*/
export const useSnaps = (snaps: number[]): string[] => {
const insets = useSafeAreaInsets();
const { screen } = useDimensions();
const percentage = insets.bottom + insets.top > 75 ? 110 : 100;
return snaps.map(snap => `${((percentage * snap) / (screen.height * screen.scale)).toFixed(2)}%`);
};

View File

@ -0,0 +1,27 @@
import { useCallback } from 'react';
import { TActionSheetOptionsItem, useActionSheet } from '../../containers/ActionSheet';
import i18n from '../../i18n';
import { videoConfJoin } from '../methods/videoConf';
export const useVideoConf = (): { joinCall: (blockId: string) => void } => {
const { showActionSheet } = useActionSheet();
const joinCall = useCallback(blockId => {
const options: TActionSheetOptionsItem[] = [
{
title: i18n.t('Video_call'),
icon: 'camera',
onPress: () => videoConfJoin(blockId, true)
},
{
title: i18n.t('Voice_call'),
icon: 'microphone',
onPress: () => videoConfJoin(blockId, false)
}
];
showActionSheet({ options });
}, []);
return { joinCall };
};

View File

@ -4,12 +4,17 @@ import { sanitizeLikeString } from '../database/utils';
import { store } from '../store/auxStore';
import log from './helpers/log';
const DEFAULT_EXTENSION = 'mp3';
const sanitizeString = (value: string) => sanitizeLikeString(value.substring(value.lastIndexOf('/') + 1));
const parseFilename = (value: string) => {
const extension = value.substring(value.lastIndexOf('.') + 1);
const filename = sanitizeString(value.substring(value.lastIndexOf('/') + 1).split('.')[0]);
return `${filename}.${extension}`;
const getExtension = (value: string) => {
let extension = DEFAULT_EXTENSION;
const filename = value.split('/').pop();
if (filename?.includes('.')) {
extension = value.substring(value.lastIndexOf('.') + 1);
}
return extension;
};
const ensureDirAsync = async (dir: string, intermediates = true): Promise<void> => {
@ -27,7 +32,7 @@ export const downloadAudioFile = async (url: string, fileUrl: string, messageId:
const serverUrl = store.getState().server.server;
const serverUrlParsed = sanitizeString(serverUrl);
const folderPath = `${FileSystem.documentDirectory}audios/${serverUrlParsed}`;
const filename = `${messageId}_${parseFilename(fileUrl)}`;
const filename = `${messageId}.${getExtension(fileUrl)}`;
const filePath = `${folderPath}/${filename}`;
await ensureDirAsync(folderPath);
const file = await FileSystem.getInfoAsync(filePath);

View File

@ -1,16 +1,14 @@
import { SubscriptionType, TAnyMessageModel } from '../../../definitions';
import { loadNextMessages, loadMessagesForRoom } from '../../../lib/methods';
import { MessageTypeLoad } from '../../../lib/constants';
import { SubscriptionType, TAnyMessageModel } from '../../definitions';
import { loadNextMessages, loadMessagesForRoom } from '.';
import { MessageTypeLoad } from '../constants';
const getMoreMessages = ({
rid,
t,
tmid,
loaderItem
}: {
rid: string;
t: SubscriptionType;
tmid?: string;
loaderItem: TAnyMessageModel;
}): Promise<void> => {
if ([MessageTypeLoad.MORE, MessageTypeLoad.PREVIOUS_CHUNK].includes(loaderItem.t as MessageTypeLoad)) {
@ -25,7 +23,6 @@ const getMoreMessages = ({
if (loaderItem.t === MessageTypeLoad.NEXT_CHUNK) {
return loadNextMessages({
rid,
tmid,
ts: loaderItem.ts as Date,
loaderItem
});

View File

@ -57,7 +57,8 @@ export const SUPPORTED_PERMISSIONS = [
'edit-omnichannel-contact',
'edit-livechat-room-customfields',
'view-canned-responses',
'mobile-upload-file'
'mobile-upload-file',
'delete-own-message'
] as const;
export async function setPermissions(): Promise<void> {

View File

@ -1,4 +1,5 @@
import { ChatsStackParamList } from '../../../stacks/types';
import { CommonActions } from '@react-navigation/native';
import Navigation from '../../navigation/appNavigation';
import { IOmnichannelRoom, SubscriptionType, IVisitor, TSubscriptionModel, ISubscription } from '../../../definitions';
import { getRoomTitle, getUidDirectMessage } from './helpers';
@ -19,19 +20,14 @@ export type TGoRoomItem = IGoRoomItem | TSubscriptionModel | ISubscription | IOm
const navigate = ({
item,
isMasterDetail,
popToRoot,
...props
}: {
item: TGoRoomItem;
isMasterDetail: boolean;
navigationMethod?: () => ChatsStackParamList;
popToRoot: boolean;
}) => {
let navigationMethod = props.navigationMethod ?? Navigation.navigate;
if (isMasterDetail) {
navigationMethod = Navigation.replace;
}
navigationMethod('RoomView', {
const routeParams = {
rid: item.rid,
name: getRoomTitle(item),
t: item.t,
@ -40,6 +36,44 @@ const navigate = ({
visitor: item.visitor,
roomUserId: getUidDirectMessage(item),
...props
};
if (isMasterDetail) {
if (popToRoot) {
Navigation.navigate('DrawerNavigator');
}
return Navigation.dispatch((state: any) => {
const routesRoomView = state.routes.filter((r: any) => r.name !== 'RoomView');
return CommonActions.reset({
...state,
routes: [
...routesRoomView,
{
name: 'RoomView',
params: routeParams
}
],
index: routesRoomView.length
});
});
}
if (popToRoot) {
Navigation.navigate('RoomsListView');
}
return Navigation.dispatch((state: any) => {
const routesRoomsListView = state.routes.filter((r: any) => r.name === 'RoomsListView');
return CommonActions.reset({
...state,
routes: [
...routesRoomsListView,
{
name: 'RoomView',
params: routeParams
}
],
index: routesRoomsListView.length
});
});
};
@ -51,13 +85,14 @@ interface IOmnichannelRoomVisitor extends IOmnichannelRoom {
export const goRoom = async ({
item,
isMasterDetail = false,
popToRoot = false,
...props
}: {
item: TGoRoomItem;
isMasterDetail: boolean;
navigationMethod?: any;
jumpToMessageId?: string;
usedCannedResponse?: string;
popToRoot?: boolean;
}): Promise<void> => {
if (!('id' in item) && item.t === SubscriptionType.DIRECT && item?.search) {
// if user is using the search we need first to join/create room
@ -72,6 +107,7 @@ export const goRoom = async ({
t: SubscriptionType.DIRECT
},
isMasterDetail,
popToRoot,
...props
});
}
@ -80,5 +116,5 @@ export const goRoom = async ({
}
}
return navigate({ item, isMasterDetail, ...props });
return navigate({ item, isMasterDetail, popToRoot, ...props });
};

View File

@ -10,6 +10,7 @@ export * from './animations';
export const defaultHeader = {
headerBackTitleVisible: false,
headerBackTestID: 'header-back',
cardOverlayEnabled: true,
cardStyle: { backgroundColor: 'transparent' }
};

View File

@ -33,6 +33,9 @@ const RCSSLPinning = Platform.select({
type: ['com.rsa.pkcs-12']
});
const { uri, name } = res;
if (!name) {
return reject();
}
Alert.prompt(
I18n.t('Certificate_password'),
I18n.t('Whats_the_password_for_your_certificate'),

View File

@ -16,6 +16,7 @@ export * from './getSingleMessage';
export * from './getSlashCommands';
export * from './getThreadName';
export * from './getUsersPresence';
export * from './getMoreMessages';
export * from './loadMessagesForRoom';
export * from './loadMissedMessages';
export * from './loadNextMessages';

View File

@ -15,7 +15,6 @@ const COUNT = 50;
interface ILoadNextMessages {
rid: string;
ts: Date;
tmid?: string;
loaderItem: TMessageModel;
}
@ -32,7 +31,6 @@ export function loadNextMessages(args: ILoadNextMessages): Promise<void> {
const loadMoreItem = {
_id: generateLoadMoreId(lastMessage._id),
rid: lastMessage.rid,
tmid: args.tmid,
ts: moment(lastMessage.ts).add(1, 'millisecond'),
t: MessageTypeLoad.NEXT_CHUNK
};

View File

@ -19,9 +19,6 @@ export function loadSurroundingMessages({ messageId, rid }: { messageId: string;
let messages: IMessage[] = EJSON.fromJSONValue(data?.messages);
messages = orderBy(messages, 'ts');
const message = messages.find(m => m._id === messageId);
const tmid = message?.tmid;
if (messages?.length) {
if (data?.moreBefore) {
const firstMessage = messages[0];
@ -30,7 +27,6 @@ export function loadSurroundingMessages({ messageId, rid }: { messageId: string;
const loadMoreItem = {
_id: generateLoadMoreId(firstMessage._id),
rid: firstMessage.rid,
tmid,
ts: moment(firstMessage.ts).subtract(1, 'millisecond').toDate(),
t: MessageTypeLoad.PREVIOUS_CHUNK,
msg: firstMessage.msg
@ -46,7 +42,6 @@ export function loadSurroundingMessages({ messageId, rid }: { messageId: string;
const loadMoreItem = {
_id: generateLoadMoreId(lastMessage._id),
rid: lastMessage.rid,
tmid,
ts: moment(lastMessage.ts).add(1, 'millisecond').toDate(),
t: MessageTypeLoad.NEXT_CHUNK,
msg: lastMessage.msg

View File

@ -38,7 +38,8 @@ export const localSearchSubscription = async ({ text = '', filterUsers = true, f
t: item.t,
encrypted: item.encrypted,
lastMessage: item.lastMessage,
status: item.status
status: item.status,
teamMain: item.teamMain
})) as ISearchLocal[];
return search;

View File

@ -1,27 +1,35 @@
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import { settings as RocketChatSettings } from '@rocket.chat/sdk';
import { FetchBlobResponse, StatefulPromise } from 'rn-fetch-blob';
import isEmpty from 'lodash/isEmpty';
import { FetchBlobResponse, StatefulPromise } from 'rn-fetch-blob';
import { Alert } from 'react-native';
import FileUpload from './helpers/fileUpload';
import database from '../database';
import log from './helpers/log';
import { IUpload, IUser, TUploadModel } from '../../definitions';
import i18n from '../../i18n';
import database from '../database';
import FileUpload from './helpers/fileUpload';
import { IFileUpload } from './helpers/fileUpload/interfaces';
import log from './helpers/log';
const uploadQueue: { [index: string]: StatefulPromise<FetchBlobResponse> } = {};
export function isUploadActive(path: string): boolean {
return !!uploadQueue[path];
const getUploadPath = (path: string, rid: string) => `${path}-${rid}`;
export function isUploadActive(path: string, rid: string): boolean {
return !!uploadQueue[getUploadPath(path, rid)];
}
export async function cancelUpload(item: TUploadModel): Promise<void> {
if (!isEmpty(uploadQueue[item.path])) {
export async function cancelUpload(item: TUploadModel, rid: string): Promise<void> {
const uploadPath = getUploadPath(item.path, rid);
if (!isEmpty(uploadQueue[uploadPath])) {
try {
await uploadQueue[item.path].cancel();
await uploadQueue[uploadPath].cancel();
} catch {
// Do nothing
}
delete uploadQueue[uploadPath];
}
if (item.id) {
try {
const db = database.active;
await db.write(async () => {
@ -30,7 +38,6 @@ export async function cancelUpload(item: TUploadModel): Promise<void> {
} catch (e) {
log(e);
}
delete uploadQueue[item.path];
}
}
@ -39,7 +46,8 @@ export function sendFileMessage(
fileInfo: IUpload,
tmid: string | undefined,
server: string,
user: Partial<Pick<IUser, 'id' | 'token'>>
user: Partial<Pick<IUser, 'id' | 'token'>>,
isForceTryAgain?: boolean
): Promise<FetchBlobResponse | void> {
return new Promise(async (resolve, reject) => {
try {
@ -51,15 +59,22 @@ export function sendFileMessage(
const db = database.active;
const uploadsCollection = db.get('uploads');
const uploadPath = getUploadPath(fileInfo.path, rid);
let uploadRecord: TUploadModel;
try {
uploadRecord = await uploadsCollection.find(fileInfo.path);
uploadRecord = await uploadsCollection.find(uploadPath);
if (uploadRecord.id && !isForceTryAgain) {
return Alert.alert(i18n.t('FileUpload_Error'), i18n.t('Upload_in_progress'));
}
} catch (error) {
try {
await db.write(async () => {
uploadRecord = await uploadsCollection.create(u => {
u._raw = sanitizedRaw({ id: fileInfo.path }, uploadsCollection.schema);
u._raw = sanitizedRaw({ id: uploadPath }, uploadsCollection.schema);
Object.assign(u, fileInfo);
if (tmid) {
u.tmid = tmid;
}
if (u.subscription) {
u.subscription.id = rid;
}
@ -85,6 +100,13 @@ export function sendFileMessage(
});
}
if (fileInfo.msg) {
formData.push({
name: 'msg',
data: fileInfo.msg
});
}
if (tmid) {
formData.push({
name: 'tmid',
@ -99,9 +121,9 @@ export function sendFileMessage(
'X-User-Id': id
};
uploadQueue[fileInfo.path] = FileUpload.fetch('POST', uploadUrl, headers, formData);
uploadQueue[uploadPath] = FileUpload.fetch('POST', uploadUrl, headers, formData);
uploadQueue[fileInfo.path].uploadProgress(async (loaded: number, total: number) => {
uploadQueue[uploadPath].uploadProgress(async (loaded: number, total: number) => {
try {
await db.write(async () => {
await uploadRecord.update(u => {
@ -113,7 +135,7 @@ export function sendFileMessage(
}
});
uploadQueue[fileInfo.path].then(async response => {
uploadQueue[uploadPath].then(async response => {
if (response.respInfo.status >= 200 && response.respInfo.status < 400) {
// If response is all good...
try {
@ -142,7 +164,7 @@ export function sendFileMessage(
}
});
uploadQueue[fileInfo.path].catch(async error => {
uploadQueue[uploadPath].catch(async error => {
try {
await db.write(async () => {
await uploadRecord.update(u => {

Some files were not shown because too many files have changed in this diff Show More