mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-09-09 19:58:40 +00:00
Compare commits
2 Commits
5383
...
reactions-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f515617140 | ||
|
|
b66468b8c4 |
@@ -296,6 +296,7 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
config = in.readBundle();
|
||||
featureFlags = in.readBundle();
|
||||
userInfo = new JitsiMeetUserInfo(in.readBundle());
|
||||
byte tmpAudioMuted = in.readByte();
|
||||
}
|
||||
|
||||
Bundle asProps() {
|
||||
|
||||
17
config.js
17
config.js
@@ -545,19 +545,6 @@ var config = {
|
||||
// '__end'
|
||||
// ],
|
||||
|
||||
// Holds values related to toolbar visibility control.
|
||||
// toolbarConfig: {
|
||||
// // Moved from interfaceConfig.INITIAL_TOOLBAR_TIMEOUT
|
||||
// // The initial numer of miliseconds for the toolbar buttons to be visible on screen.
|
||||
// initialTimeout: 20000,
|
||||
// // Moved from interfaceConfig.TOOLBAR_TIMEOUT
|
||||
// // Number of miliseconds for the toolbar buttons to be visible on screen.
|
||||
// timeout: 4000,
|
||||
// // Moved from interfaceConfig.TOOLBAR_ALWAYS_VISIBLE
|
||||
// // Whether toolbar should be always visible or should hide after x miliseconds.
|
||||
// alwaysVisible: false
|
||||
// },
|
||||
|
||||
// Toolbar buttons which have their click event exposed through the API on
|
||||
// `toolbarButtonClicked` event instead of executing the normal click routine.
|
||||
// buttonsWithNotifyClick: [
|
||||
@@ -909,10 +896,6 @@ var config = {
|
||||
*/
|
||||
// dynamicBrandingUrl: '',
|
||||
|
||||
// When true the user cannot add more images to be used as virtual background.
|
||||
// Only the default ones from will be available.
|
||||
// disableAddingBackgroundImages: false,
|
||||
|
||||
// Sets the background transparency level. '0' is fully transparent, '1' is opaque.
|
||||
// backgroundAlpha: 1,
|
||||
|
||||
|
||||
@@ -213,7 +213,6 @@
|
||||
}
|
||||
|
||||
#usermsg {
|
||||
-ms-overflow-style: none;
|
||||
border: 0px none;
|
||||
border-radius:0;
|
||||
box-shadow: none;
|
||||
@@ -222,13 +221,8 @@
|
||||
padding: 10px;
|
||||
overflow-y: auto;
|
||||
resize: none;
|
||||
scrollbar-width: none;
|
||||
width: 100%;
|
||||
word-break: break-word;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#usermsg:hover {
|
||||
|
||||
@@ -75,7 +75,3 @@
|
||||
top: 0;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.shift-right .details-container {
|
||||
margin-left: calc(#{$sidebarWidth} / 2);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,11 @@ var interfaceConfig = {
|
||||
|
||||
CLOSE_PAGE_GUEST_HINT: false, // A html text to be shown to guests on the close page, false disables it
|
||||
|
||||
// Connection indicators (
|
||||
// CONNECTION_INDICATOR_AUTO_HIDE_ENABLED,
|
||||
// CONNECTION_INDICATOR_AUTO_HIDE_TIMEOUT,
|
||||
// CONNECTION_INDICATOR_DISABLED) got moved to config.js.
|
||||
|
||||
DEFAULT_BACKGROUND: '#474747',
|
||||
DEFAULT_LOCAL_DISPLAY_NAME: 'me',
|
||||
DEFAULT_LOGO_URL: 'images/watermark.svg',
|
||||
@@ -34,6 +39,9 @@ var interfaceConfig = {
|
||||
|
||||
DISABLE_DOMINANT_SPEAKER_INDICATOR: false,
|
||||
|
||||
// Deprecated. Please use disableModeratorIndicator from config.js
|
||||
// DISABLE_FOCUS_INDICATOR: false,
|
||||
|
||||
/**
|
||||
* If true, notifications regarding joining/leaving are no longer displayed.
|
||||
*/
|
||||
@@ -89,6 +97,7 @@ var interfaceConfig = {
|
||||
*/
|
||||
HIDE_INVITE_MORE_HEADER: false,
|
||||
|
||||
INITIAL_TOOLBAR_TIMEOUT: 20000,
|
||||
JITSI_WATERMARK_LINK: 'https://jitsi.org',
|
||||
|
||||
LANG_DETECTION: true, // Allow i18n to detect the system language
|
||||
@@ -174,6 +183,16 @@ var interfaceConfig = {
|
||||
*/
|
||||
SUPPORT_URL: 'https://community.jitsi.org/',
|
||||
|
||||
TOOLBAR_ALWAYS_VISIBLE: false,
|
||||
|
||||
/**
|
||||
* DEPRECATED!
|
||||
* This config was moved to config.js as `toolbarButtons`.
|
||||
*/
|
||||
// TOOLBAR_BUTTONS: [],
|
||||
|
||||
TOOLBAR_TIMEOUT: 4000,
|
||||
|
||||
// Browsers, in addition to those which do not fully support WebRTC, that
|
||||
// are not supported and should show the unsupported browser page.
|
||||
UNSUPPORTED_BROWSERS: [],
|
||||
@@ -238,28 +257,6 @@ var interfaceConfig = {
|
||||
PHONE_NUMBER_REGEX
|
||||
*/
|
||||
|
||||
// -----------------DEPRECATED CONFIGS BELOW THIS LINE-----------------------------
|
||||
|
||||
// Connection indicators (
|
||||
// CONNECTION_INDICATOR_AUTO_HIDE_ENABLED,
|
||||
// CONNECTION_INDICATOR_AUTO_HIDE_TIMEOUT,
|
||||
// CONNECTION_INDICATOR_DISABLED) got moved to config.js.
|
||||
|
||||
// Please use disableModeratorIndicator from config.js
|
||||
// DISABLE_FOCUS_INDICATOR: false,
|
||||
|
||||
// Moved to config.js as `toolbarConfig.initialTimeout`.
|
||||
// INITIAL_TOOLBAR_TIMEOUT: 20000,
|
||||
|
||||
// Moved to config.js as `toolbarConfig.alwaysVisible`.
|
||||
// TOOLBAR_ALWAYS_VISIBLE: false,
|
||||
|
||||
// This config was moved to config.js as `toolbarButtons`.
|
||||
// TOOLBAR_BUTTONS: [],
|
||||
|
||||
// Moved to config.js as `toolbarConfig.timeout`.
|
||||
// TOOLBAR_TIMEOUT: 4000,
|
||||
|
||||
// Allow all above example options to include a trailing comma and
|
||||
// prevent fear when commenting out the last value.
|
||||
// eslint-disable-next-line sort-keys
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
"copyInvite": "انسخ دعوةً للاجتماع",
|
||||
"copyLink": "انسخ رابط الاجتماع",
|
||||
"copyStream": "انسخ رابط البث المباشر",
|
||||
"contacts": "contacts",
|
||||
"countryNotSupported": "لا ندعم هذه الوجهة حاليًا.",
|
||||
"countryReminder": "أتريد الاتصال بمن هو خارج الولايات المتحدة؟ تأكد من الابتداء برمز الدولة أولًا!",
|
||||
"defaultEmail": "بريدك الافتراضي",
|
||||
@@ -17,14 +16,18 @@
|
||||
"inviteMoreMailSubject": "ضم {{appName}} للاجتماع",
|
||||
"inviteMorePrompt": "ادعُ أشخاصًا آخرين",
|
||||
"linkCopied": "نُسِخ الرابط",
|
||||
"loading": "البحث عن أشخاص وأرقام هواتف",
|
||||
"loadingNumber": "التحقق من رقم الهاتف",
|
||||
"loadingPeople": "البحث عن أشخاص لإضافتهم",
|
||||
"noResults": "لم يُعثَر على أي نتيجة بحث متطابقة",
|
||||
"noValidNumbers": "ادخل رقم هاتف، رجاءً",
|
||||
"outlookEmail": "بريد مايكروسوفت",
|
||||
"phoneNumbers": "أضف ارقام هواتف",
|
||||
"search": "ابحث عن أشخاص",
|
||||
"searchNumbers": "أضف ارقام هواتف",
|
||||
"searchPeople": "ابحث عن أشخاص",
|
||||
"searchPeopleAndNumbers": "ابحث عن أشخاص أو أضف أرقام هواتفهم",
|
||||
"shareInvite": "شارك دعوةً للاجتماع",
|
||||
"shareLink": "شارك رابط الاجتماع لدعوة الأخرين",
|
||||
"shareStream": "شارك رابط البث المباشر للاجتماع",
|
||||
"sipAddresses": "sip عنوان",
|
||||
"telephone": "رقم الهاتف: {{number}}",
|
||||
"title": "ادعُ أحدًا لهذا الاجتماع",
|
||||
"yahooEmail": "بريد ياهوو"
|
||||
@@ -58,7 +61,6 @@
|
||||
"today": "اليوم"
|
||||
},
|
||||
"chat": {
|
||||
"enter": "أدخل الغرفة",
|
||||
"error": "خطأ: لم تُرسَل رسالتك. السبب: {{error}}",
|
||||
"fieldPlaceHolder": "اكتب رسالتك هنا",
|
||||
"messagebox": "اكتب رسالة",
|
||||
@@ -70,14 +72,6 @@
|
||||
"titleWithPolls": "اكتب لقبًا لاعتماده في المحادثة"
|
||||
},
|
||||
"privateNotice": "أرسل رسالة خاصة إلى {{recipient}}",
|
||||
"message": "رسالة",
|
||||
"messageAccessibleTitle": "{{user}} مقولة:",
|
||||
"messageAccessibleTitleMe": "أنا أقول:",
|
||||
"smileysPanel": "واجهة الإيموجي",
|
||||
"tabs": {
|
||||
"chat": "دردشة",
|
||||
"polls": "تصويت"
|
||||
},
|
||||
"title": "محادثة",
|
||||
"titleWithPolls": "محادثة",
|
||||
"you": "أنت"
|
||||
@@ -85,8 +79,7 @@
|
||||
"chromeExtensionBanner": {
|
||||
"installExtensionText": "نزِّل الإضافة للدمج مع رزنامة غوغل ورزنامة أوفيس 365",
|
||||
"buttonText": "نزِّل إضافة متصفح كروم",
|
||||
"dontShowAgain": "لا ترني هذه مرة أخرى",
|
||||
"close": "إغلق"
|
||||
"dontShowAgain": "لا ترني هذه مرة أخرى"
|
||||
},
|
||||
"connectingOverlay": {
|
||||
"joiningRoom": "قيد وصلك بالإجتماع..."
|
||||
@@ -108,7 +101,6 @@
|
||||
},
|
||||
"connectionindicator": {
|
||||
"address": "العنوان:",
|
||||
"audio_ssrc": "مصدر الصوت:",
|
||||
"bandwidth": "مُعدَّل البيانات المتبادلة:",
|
||||
"bitrate": "مُعدَّل البث:",
|
||||
"bridgeCount": "عدد المخدِّمات: ",
|
||||
@@ -136,12 +128,9 @@
|
||||
"remoteport": "المنفذ البعيد:",
|
||||
"remoteport_plural": "المنافذ البعيدة:",
|
||||
"resolution": "الدقة:",
|
||||
"savelogs": "خزن الوقوعات",
|
||||
"participant_id": "معرّف المشترك:",
|
||||
"status": "الاتصال:",
|
||||
"transport": "النقل:",
|
||||
"transport_plural": "النواقل:",
|
||||
"video_ssrc": "مصدر الصورة:"
|
||||
"transport_plural": "النواقل:"
|
||||
},
|
||||
"dateUtils": {
|
||||
"earlier": "سابقًا",
|
||||
@@ -183,7 +172,6 @@
|
||||
"alreadySharedVideoMsg": "يشارك أحد الحضور الفيديو حاليًا، ولا يسمح هذا الإجتماع سوى بمشاركة فيديو واحد في آن واحد",
|
||||
"alreadySharedVideoTitle": "لا يُسمَح سوى بفيديو مشارك واحد على الأكثر في آن واحد.",
|
||||
"applicationWindow": "نافذة التطبيق",
|
||||
"authenticationRequired": "يستلزم التوثيق",
|
||||
"Back": "عد للخلف",
|
||||
"cameraConstraintFailedError": "لا تتوافق كاميرتك مع بعض الشروط المطلوبة.",
|
||||
"cameraNotFoundError": "لم يُعثَر على اي كاميرا",
|
||||
@@ -191,7 +179,6 @@
|
||||
"cameraNotSendingDataTitle": "يصعب الوصول الوصول إلى كاميرتك",
|
||||
"cameraPermissionDeniedError": "لم تُمنَح إذن استعمال الكاميرا. لا يزال بإمكانك حضور المؤتمر ولكن لن يراك الآخرون. استعمال زر الكاميرا في شريط العنوان لمحاول إصلاح المشكلة.",
|
||||
"cameraUnknownError": "يصعب استعمال الكاميرا لأسباب مجهولة.",
|
||||
"cameraTimeoutError": "تعذر بدء مصدر الفيديو. انتهت المهلة!",
|
||||
"cameraUnsupportedResolutionError": "لا تدعم كاميرتك دقة الفيديو المطلوبة.",
|
||||
"Cancel": "ِألغ",
|
||||
"close": "أغلق",
|
||||
@@ -209,20 +196,16 @@
|
||||
"copied": "نُسِخ!",
|
||||
"copy": "انسخ",
|
||||
"dismiss": "تجاهل",
|
||||
"displayNameRequired": "السلام عليكم! ما اسمك؟",
|
||||
"displayNameRequired": "السلام عليك! ما اسمك؟",
|
||||
"done": "اُنجِز",
|
||||
"e2eeDescription": "<p>عملية التعمية من طرف لطرف <strong>قيد التجريب</strong> حاليًا. زر رجاءً <a href='https://jitsi.org/blog/e2ee/' target='_blank'>هذا المنشور</a> لمزيد من التفاصيل.</p><br/><p>ضع في ذهنك أن تشغيل عملية التعمية من طرف لطرف ستعطل عمل بعض الخدمات التي يقدمها المُخدِّم مثل: التسجيل، والبث الحي، الاشتراك عبر الهاتف. أضف إلى ذلك أن الاجتماع سيعمل مع الأشخاص المنضمين من المتصفح التي تدعم قابلية الدخول إلى البث.</p>",
|
||||
"e2eeLabel": "المفتاح",
|
||||
"e2eeDisabledDueToMaxModeDescription": "لا يمكن تمكين التشفير من طرف إلى طرف بسبب العدد الكبير من المشاركين في المؤتمر.",
|
||||
"e2eeWarning": "تحذير: لا يبدو أن جميع المشاركين في هذا الاجتماع لديهم دعم للتشفير من طرف إلى طرف. إذا قمت بتمكينه فلن يتمكنوا من رؤيتك أو سماعك.",
|
||||
"e2eeWillDisableDueToMaxModeDescription": "تحذير: سيتم تعطيل التشفير من طرف إلى طرف تلقائيًا إذا انضم المزيد من المشاركين إلى المؤتمر.",
|
||||
"e2eeWarning": "<br /><p><strong>تحذير:</strong>لا يبدو أن جميع الحاضرين في هذا الاجتماع يدعمون عملية التعمية طرف لطرف. إن فعَّلت العملية، فلن يتمكنوا من رؤيتك ولا سماعك.</p>",
|
||||
"enterDisplayName": "أدخل اسمك هنا، رجاءً",
|
||||
"embedMeeting": "تضمين الاجتماع",
|
||||
"error": "خطأ",
|
||||
"gracefulShutdown": "خدمتنا متوقفة حاليًا لعمليات الصيانة. جرب مرة أخرى في وقت لاحق.",
|
||||
"grantModeratorDialog": "أما زلت راغب بجعل هذا المشارك رئيس الجلسة؟",
|
||||
"grantModeratorTitle": "اجعله رئيس الجلسة",
|
||||
"hideShareAudioHelper": "لا تظهر هذا الحوار مرة أخرى",
|
||||
"IamHost": "أنا المضيف",
|
||||
"incorrectRoomLockPassword": "كلمة مرور خطأ",
|
||||
"incorrectPassword": "اسم المستخدم أو كلمة المرور خطأ",
|
||||
@@ -232,15 +215,15 @@
|
||||
"kickParticipantButton": "اطرد",
|
||||
"kickParticipantDialog": "أمتأكد من طرد هذا المشارك؟",
|
||||
"kickParticipantTitle": "أتريد طرد هذا المشارك؟",
|
||||
"kickTitle": "عذرًا! تم طردك {{participantDisplayName}} من الاجتماع",
|
||||
"kickTitle": "عذرًا! طردك {{participantDisplayName}} من الاجتماع",
|
||||
"liveStreaming": "البث المباشر الحي",
|
||||
"liveStreamingDisabledBecauseOfActiveRecordingTooltip": "غير ممكن أثناء التسجيل",
|
||||
"liveStreamingDisabledForGuestTooltip": "لا يمكن للضيوف بدء بث حي مباشر.",
|
||||
"liveStreamingDisabledTooltip": "بدء بثٍ حيٍّ مُعطَّل",
|
||||
"lockMessage": "فشل جعل المؤتمر مغلقًا.",
|
||||
"lockRoom": "أضف الاجتماع $t(lockRoomPasswordUppercase)",
|
||||
"lockTitle": "فشلت عملية القفل والإغلاق",
|
||||
"logoutQuestion": "أمتأكد من رغبتك في الخروج وإيقاف المؤتمر؟",
|
||||
"login": "تسجيل الدخول",
|
||||
"logoutTitle": "الخروج",
|
||||
"maxUsersLimitReached": "وصل عدد المشاركين إلى الحد الأقصى. المؤتمر مكتمل الحضور. اتصل رجاءً براعي المؤتمر أو جرب مرة أخرى لاحقًا.",
|
||||
"maxUsersLimitReachedTitle": "وصل عدد المشاركين إلى الحد الأقصى",
|
||||
@@ -249,46 +232,28 @@
|
||||
"micNotSendingData": "اذهب إلى إعدادات حاسوبك لإلغاء كتم المجهار (المايكروفون) وضبط مستواه",
|
||||
"micNotSendingDataTitle": "كتم ضبط الإعدادات لديك المجهار (المايكروفون)",
|
||||
"micPermissionDeniedError": "لم تعط إذن استعمال المجهار (المايكروفون) الخاص بك. ما يزال بإمكانك الإنضمام إلى المؤتمر ولكن دون أن يسمعك الآخرون. جرب استعمال زر الكاميرا في شريط العنوان لإصلاح هذه المشكلة.",
|
||||
"micTimeoutError": "Could not start audio source. Timeout occured!",
|
||||
"micUnknownError": "Cannot use microphone for an unknown reason.",
|
||||
"moderationAudioLabel": "Allow attendees to unmute themselves",
|
||||
"moderationVideoLabel": "Allow attendees to start their video",
|
||||
"micUnknownError": "يصعب استعمال المجهار (المايكروفون) الخاص بك لأسباب مجهولة.",
|
||||
"muteEveryoneElseDialog": "بمجرد أن تكتمهم، لن تتمكن نم إلغاء الكتم هذا، ولكن يمكنهم إلغاء كتم أنفسهم في أي وقت.",
|
||||
"muteEveryoneElseTitle": "أتريد كتم الجميع باستثناء {{whom}}?",
|
||||
"muteEveryoneDialog": "أمتأكد من رغبتك بكتم الجميع؟ لن تتمكن من إلغاء الكتم ولكن يمكنهم إلغاء كتم أنفسهم في أي وقت.",
|
||||
"muteEveryoneDialogModerationOn": "The participants can send a request to speak at any time.",
|
||||
"muteEveryoneTitle": "أتريد كتم الجميع؟",
|
||||
"muteEveryoneElsesVideoDialog": "Once the camera is disabled, you won't be able to turn it back on, but they can turn it back on at any time.",
|
||||
"muteEveryoneElsesVideoTitle": "Stop everyone's video except {{whom}}?",
|
||||
"muteEveryonesVideoDialog": "The participants can turn on their video at any time.",
|
||||
"muteEveryonesVideoDialogModerationOn": "The participants can send a request to turn on their video at any time.",
|
||||
"muteEveryonesVideoDialogOk": "Disable",
|
||||
"muteEveryonesVideoTitle": "Stop everyone's video?",
|
||||
"muteEveryoneSelf": "اكتم نفسك أيضًا",
|
||||
"muteEveryoneStartMuted": "البدء مع كتم الجميع من الآن فصاعدًا",
|
||||
"muteParticipantBody": "لن تتكمن من إلغاء كتمهم، ولكن يمكنهم إلغاء كتم أنفسهم في أي وقت.",
|
||||
"muteParticipantButton": "كتم",
|
||||
"muteParticipantDialog": "أمتأكد من رغبتك لهذا المشارك؟ لن تتمكن من إلغاء الكتم ولكن يمكنه إلغاء كتم أنفسهم في أي وقت.",
|
||||
"muteParticipantsVideoDialog": "هل أنت متأكد أنك تريد إيقاف تشغيل كاميرا هذا المشارك؟ لن تتمكن من إعادة تشغيل الكاميرا ، ولكن يمكنهم إعادة تشغيلها في أي وقت.",
|
||||
"muteParticipantTitle": "أتريد كتم هذا المشارك؟",
|
||||
"muteParticipantsVideoButton": "وقف الكاميرا",
|
||||
"muteParticipantsVideoTitle": "تعطيل الكاميرا لهذا المشارك؟",
|
||||
"muteParticipantsVideoBody": "لن تتمكن من إعادة تشغيل الكاميرا ، ولكن يمكنهم إعادة تشغيلها في أي وقت.",
|
||||
"noDropboxToken": "لا يوجد رمز مميز صالح لـ Dropbox",
|
||||
"Ok": "تمام",
|
||||
"password": "Password",
|
||||
"passwordLabel": "جعل عضو ما هذا الاجتماع مغلقًا. أدخل رجاءً $t(lockRoomPassword) للإنضمام.",
|
||||
"passwordNotSupported": "ضبط اجتماع $t(lockRoomPassword) غير مدعوم.",
|
||||
"passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) غير مدعوم",
|
||||
"passwordRequired": "يُطلَب $t(lockRoomPasswordUppercase)",
|
||||
"permissionErrorTitle": "الإذن مطلوب",
|
||||
"permissionCameraRequiredError": "مطلوب إذن الكاميرا للمشاركة في المؤتمرات بالفيديو. يرجى منحه في الإعدادات",
|
||||
"permissionMicRequiredError": "مطلوب إذن الميكروفون للمشاركة في المؤتمرات مع الصوت. يرجى منحه في الإعدادات",
|
||||
"popupError": "يمنع متصفحك النوافذ المنبثقة من هذا الموقع. فعِّل رجاءً النوافذ المنبثقة في المتصفح من إعدادات الحماية وحاول مرة أخرى.",
|
||||
"popupErrorTitle": "النوافذ المنبثقة محجوبة.",
|
||||
"readMore": "أكثر",
|
||||
"readMore": "more",
|
||||
"recording": "قيد التسجيل",
|
||||
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "ليس بالإمكان ما دمت قيد البث المباشر",
|
||||
"recordingDisabledForGuestTooltip": "لا يمكن للضيوف بدء عملية التسجيل.",
|
||||
"recordingDisabledTooltip": "عملية التسجيل معطلة.",
|
||||
"rejoinNow": "انضم مجدَّدًا الآن",
|
||||
"remoteControlAllowedMessage": "قبل {{user}} طلب إمكانية التحكم البعيد!",
|
||||
@@ -315,27 +280,15 @@
|
||||
"sendPrivateMessageTitle": "أترسل ردًا خاصًا؟",
|
||||
"serviceUnavailable": "الخدمة غير متاحة",
|
||||
"sessTerminated": "أُنهيَت المكالمة",
|
||||
"sessionRestarted": "إعادة الاتصال من الجسر",
|
||||
"Share": "شارك",
|
||||
"shareAudio": "استمر",
|
||||
"shareAudioTitle" : "How to share audio",
|
||||
"shareAudioWarningTitle": "You need to stop screen sharing before sharing audio",
|
||||
"shareAudioWarningH1": "If you want to share just audio:",
|
||||
"shareAudioWarningD1": "you need to stop screen sharing before sharing your audio.",
|
||||
"shareAudioWarningD2": "you need to restart your screen sharing and check the \"share audio\" option.",
|
||||
"shareMediaWarningGenericH2": "If you want to share your screen and audio",
|
||||
"shareVideoLinkError": "زودنا برابط يوتيوب صحيح",
|
||||
"shareVideoTitle": "شارك فيديو",
|
||||
"shareYourScreen": "شارك شاشتك",
|
||||
"shareYourScreenDisabled": "مشاركة الشاشة مُعطَّلة",
|
||||
"shareYourScreenDisabledForGuest": "لا يمكن للضيوف مشاركة شاشتهم",
|
||||
"startLiveStreaming": "ابدأ بثًا حيًا",
|
||||
"startRecording": "ابدأ التسجيل",
|
||||
"startRemoteControlErrorMessage": "حصل خطأٌ أثناء محاولة بدء جلسة تحكم بعيد!",
|
||||
"shareScreenWarningTitle": "تحتاج إلى إيقاف مشاركة الصوت قبل مشاركة شاشتك",
|
||||
"shareScreenWarningH1": "إذا كنت تريد مشاركة شاشتك فقط:",
|
||||
"shareScreenWarningD1": "تحتاج إلى إيقاف مشاركة الصوت قبل مشاركة شاشتك.",
|
||||
"shareScreenWarningD2": "you need to stop audio sharing, start screen sharing and check the \"share audio\" option.",
|
||||
"sharedVideoLinkPlaceholder": "رابط اليوتيوب او رابط الفيديو المباشر",
|
||||
"stopLiveStreaming": "أوقف البث الحي",
|
||||
"stopRecording": "أوقف التسجيل",
|
||||
"stopRecordingWarning": "أمتأكد من رغبتك بإيقاف التسجيل؟",
|
||||
@@ -348,20 +301,12 @@
|
||||
"tokenAuthFailedTitle": "فشلت عملية الاستيثاق",
|
||||
"transcribing": "يذاع",
|
||||
"unlockRoom": "إزل الاجتماع $t(lockRoomPassword)",
|
||||
"user": "مستخدم",
|
||||
"userIdentifier": "معرف المستخدم",
|
||||
"userPassword": "كلمة مرور المستخدم",
|
||||
"videoLink": "رابط الفيديو",
|
||||
"viewUpgradeOptions": "عرض خيارات الترقية",
|
||||
"viewUpgradeOptionsContent": "للحصول على وصول غير محدود إلى الميزات المتميزة مثل التسجيل والنسخ وتدفق RTMP والمزيد ، ستحتاج إلى ترقية خطتك.",
|
||||
"viewUpgradeOptionsTitle": "لقد اكتشفت ميزة مميزة!",
|
||||
"WaitForHostMsg": "لم يبدأ المؤتمر <b>{{room}}</b> بعد. إن كنت المضيف والراعي، فنرجو تأكيد ذلك عبر الاستيثاق أو انتظر وصول المضيف رجاءً. ",
|
||||
"WaitForHostMsgWOk": "لم يبدأ المؤتمر <b>{{room}}</b> بعد. إن كنت المضيف والراعي، فاضغط على «تمام» للاستيثاق أو انتظر وصول المضيف رجاءً.",
|
||||
"WaitingForHost": "ننتظر وصول المضيف ...",
|
||||
"Yes": "نعم",
|
||||
"yourEntireScreen": "شاشتك كاملةً",
|
||||
"remoteUserControls": "ضوابط المستخدم عن بعد لـ {{username}}",
|
||||
"localUserControls": "ضوابط المستخدم المحلي"
|
||||
"yourEntireScreen": "شاشتك كاملةً"
|
||||
},
|
||||
"dialOut": {
|
||||
"statusMessage": "{{status}} الآن"
|
||||
@@ -375,29 +320,6 @@
|
||||
"embedMeeting": {
|
||||
"title": "ضمِّن هذا الاجتماع"
|
||||
},
|
||||
"virtualBackground": {
|
||||
"apply": "تطبيق",
|
||||
"title": "خلفيات افتراضية",
|
||||
"blur": "ضبابية",
|
||||
"slightBlur": "طمس طفيف",
|
||||
"removeBackground": "إزالة خلفية",
|
||||
"addBackground": "أضف خلفية",
|
||||
"pleaseWait": "يرجى الانتظار...",
|
||||
"none": "بدون",
|
||||
"uploadedImage": "تحميل الصورة {{index}}",
|
||||
"deleteImage": "حذف صورة",
|
||||
"image1" : "Beach",
|
||||
"image2" : "جدار أبيض محايد",
|
||||
"image3" : "غرفة بيضاء فارغة",
|
||||
"image4" : "مصباح أرضي أسود",
|
||||
"image5" : "جبل",
|
||||
"image6" : "غابة",
|
||||
"image7" : "شروق الشمس",
|
||||
"desktopShareError": "تعذر إنشاء مشاركة سطح المكتب",
|
||||
"desktopShare":"مشاركة سطح المكتب",
|
||||
"webAssemblyWarning": "WebAssembly غير مدعوم",
|
||||
"backgroundEffectError": "فشل تطبيق تأثير الخلفية."
|
||||
},
|
||||
"feedback": {
|
||||
"average": "المتوسط",
|
||||
"bad": "سيئة",
|
||||
@@ -405,8 +327,7 @@
|
||||
"good": "جيدة",
|
||||
"rateExperience": "قيِّم تجربتك مع هذا الإجتماع",
|
||||
"veryBad": "سيئة للغاية",
|
||||
"veryGood": "ممتازة",
|
||||
"star": "نجمة"
|
||||
"veryGood": "ممتازة"
|
||||
},
|
||||
"incomingCall": {
|
||||
"answer": "أجب",
|
||||
@@ -423,7 +344,6 @@
|
||||
"country": "البلد",
|
||||
"dialANumber": "إن أردت الإنضمام إلى الإجتماع، اتصل بأحد الأرقام التالية ثم أدخل رمز المرور",
|
||||
"dialInConferenceID": "رمز المرور (PIN):",
|
||||
"copyNumber":"إنسخ الرقم",
|
||||
"dialInNotSupported": "عذرًا، الاتصال غير مدعوم حاليًا.",
|
||||
"dialInNumber": "اتصل:",
|
||||
"dialInSummaryError": "خطأ في تحصيل معلومات الاتصال. جرب مرة أخرى لاحقًا.",
|
||||
@@ -432,11 +352,6 @@
|
||||
"inviteLiveStream": "لمشاهدة البث الحي لهذا الإجتماع، اضط على هذا الرابط: {{url}}",
|
||||
"invitePhone": "للإنضمام من الهاتف، استعمل: {{number}},,{{conferenceID}}#\n",
|
||||
"invitePhoneAlternatives": "أتبحث عن رقم اتصال مختلف؟\nأنظر أرقام الوصول إلى هذا الإجتماع: {{url}}\n\n\nإن كنت أيضًا تتصل عبر غرفة اتصال (room phone)، انضم دون الاتصال بالصوت: {{silentUrl}}",
|
||||
"inviteSipEndpoint": "To join using the SIP address, enter this: {{sipUri}}",
|
||||
"inviteTextiOSPersonal": "{{name}} is inviting you to a meeting.",
|
||||
"inviteTextiOSJoinSilent": "If you are dialing-in through a room phone, use this link to join without connecting to audio: {{silentUrl}}.",
|
||||
"inviteTextiOSInviteUrl": "Click the following link to join: {{inviteUrl}}.",
|
||||
"inviteTextiOSPhone": "To join via phone, use this number: {{number}},,{{conferenceID}}#. If you are looking for a different number, this is the full list: {{didUrl}}.",
|
||||
"inviteURLFirstPartGeneral": "دُعيِت للإنضمام إلى اجتماع",
|
||||
"inviteURLFirstPartPersonal": "دعاك {{name}} لاجتماع.\n",
|
||||
"inviteURLSecondPart": "\nانضم للاجتماع:\n{{url}}\n",
|
||||
@@ -447,7 +362,6 @@
|
||||
"noRoom": "لم تُخصَّص غرفة للاتصال إليها",
|
||||
"numbers": "أرقام الاتصال",
|
||||
"password": "$t(lockRoomPasswordUppercase):",
|
||||
"sip": "SIP عنوان",
|
||||
"title": "شارك",
|
||||
"tooltip": "شارك رابط وتفاصيل الاتصال لهذا الاجتماع",
|
||||
"label": "تفاصيل الاجتماع"
|
||||
@@ -466,7 +380,6 @@
|
||||
"support": "الدعم",
|
||||
"supportMsg": "إن تكرَّر حصول هذا، تواصل مع"
|
||||
},
|
||||
"jitsiHome": "{{logo}} Logo, links to Homepage",
|
||||
"keyboardShortcuts": {
|
||||
"focusLocal": "ركز على الفيديو الخاص بك",
|
||||
"focusRemote": "ركز على فيديو مشارك آخر",
|
||||
@@ -479,10 +392,10 @@
|
||||
"showSpeakerStats": "اظهر حالة مكبر الصوت",
|
||||
"toggleChat": "افتح/أغلق المحادثة",
|
||||
"toggleFilmstrip": "أظهِر/اخفِ إطار الفيديو المُصغَّر",
|
||||
"toggleParticipantsPane": "إظهار أو إخفاء قائمة المشاركين",
|
||||
"toggleScreensharing": "بدِّل بين الكاميرا ومشاركة الشاشة",
|
||||
"toggleShortcuts": "أظهِر/اخفِ اختصارات لوحة المفاتيح",
|
||||
"videoMute": "ابدأ/أوقف كاميرتك"
|
||||
"videoMute": "ابدأ/أوقف كاميرتك",
|
||||
"videoQuality": "اضبط جودة المكالمة"
|
||||
},
|
||||
"liveStreaming": {
|
||||
"limitNotificationDescriptionWeb": "نظرًا للضغط الكبير، سيقيَّد البث إلى {{limit}} د، ولكن إن أردت إجراء عملية بث غير محدودة، جرِّب <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
|
||||
@@ -551,15 +464,13 @@
|
||||
"lockRoomPasswordUppercase": "كلمة المرور",
|
||||
"me": "أنا",
|
||||
"notify": {
|
||||
"allowAction": "Allow",
|
||||
"allowedUnmute": "You can unmute your microphone, start your camera or share your screen.",
|
||||
"connectedOneMember": "انضم {{name}} للاجتماع",
|
||||
"connectedThreePlusMembers": "انضم {{name}} وعدد {{count}} غيره إلى الاجتماع",
|
||||
"connectedTwoMembers": "انضم {{first}} و {{second}} إلى الاجتماع",
|
||||
"disconnected": "انقطع الاتصال",
|
||||
"focus": "التركيز على المؤتمر",
|
||||
"focusFail": "إنَّ {{component}} غير متاح. ستعاد المحاولة مرة أخرى خلال {{ms}} ثانية.",
|
||||
"hostAskedUnmute": "The moderator would like you to speak",
|
||||
"grantedTo": "مُنِحت صلاحية رئيس الجلسة إلى {{to}}!",
|
||||
"invitedOneMember": "دُعِي {{name}}",
|
||||
"invitedThreePlusMembers": "دُعِي {{name}} وعدد {{count}} آخرين",
|
||||
"invitedTwoMembers": "دُعِي {{first}} و {{second}}",
|
||||
@@ -573,10 +484,6 @@
|
||||
"passwordRemovedRemotely": "أزال أحد المشاركين {{participantDisplayName}}",
|
||||
"passwordSetRemotely": "ضبط أحد المشاركين $t(lockRoomPasswordUppercase)",
|
||||
"raisedHand": "يريد {{name}} التحدث",
|
||||
"screenShareNoAudio": " Share audio box was not checked in the window selection screen.",
|
||||
"screenShareNoAudioTitle": "Couldn't share system audio!",
|
||||
"screenShareNoAudio": " Share audio box was not checked in the window selection screen.",
|
||||
"screenShareNoAudioTitle": "Couldn't share system audio!",
|
||||
"somebody": "شخص ما",
|
||||
"startSilentTitle": "انضممت دون مخرج للصوت!",
|
||||
"startSilentDescription": "أنضم مجدَّدًا للاجتماع لتفعيل الصوت",
|
||||
@@ -589,76 +496,10 @@
|
||||
"OldElectronAPPTitle": "ثغرة أمنية!",
|
||||
"oldElectronClientDescription1": "يبدو أنَّك تستعمل إصدارًا قديمًا من جيتسي يحوي ثغرة أمنية. تأكد رجاءً من أنَّك حدَّثته إلى ",
|
||||
"oldElectronClientDescription2": "أحدث إصدار",
|
||||
"oldElectronClientDescription3": " الآن!",
|
||||
"moderationInEffectDescription": "Please raise hand if you want to speak.",
|
||||
"moderationInEffectCSDescription": "Please raise hand if you want to share your screen.",
|
||||
"moderationInEffectVideoDescription": "Please raise your hand if you want to start your camera.",
|
||||
"moderationInEffectTitle": "Your microphone is muted by the moderator",
|
||||
"moderationInEffectCSTitle": "Screen sharing is blocked by the moderator",
|
||||
"moderationInEffectVideoTitle": "Your camera is blocked by the moderator",
|
||||
"moderationRequestFromModerator": "The host would like you to unmute",
|
||||
"moderationRequestFromParticipant": "Wants to speak",
|
||||
"moderationStartedTitle": "Moderation started",
|
||||
"moderationStoppedTitle": "Moderation stopped",
|
||||
"moderationToggleDescription": "by {{participantDisplayName}}",
|
||||
"raiseHandAction": "Raise hand",
|
||||
"reactionSounds": "Disable sounds",
|
||||
"groupTitle": "Notifications"
|
||||
},
|
||||
"participantsPane": {
|
||||
"close": "Close",
|
||||
"header": "Participants",
|
||||
"headings": {
|
||||
"lobby": "Lobby ({{count}})",
|
||||
"participantsList": "المشاركون في الاجتماع({{count}})",
|
||||
"waitingLobby": "Waiting in lobby ({{count}})"
|
||||
},
|
||||
"actions": {
|
||||
"allow": "السماح للحاضرين بـ:",
|
||||
"allowVideo": "السماح بالفيديو",
|
||||
"audioModeration": "إعادة صوت أنفسهم",
|
||||
"blockEveryoneMicCamera": "حظر ميكروفون وكاميرا الجميع",
|
||||
"invite": "قم بدعوة شخص ما",
|
||||
"askUnmute": "اطلب إعادة الصوت",
|
||||
"mute": "كتم الصوت",
|
||||
"muteAll": "كتم الكل",
|
||||
"muteEveryoneElse": "كتم صوت الآخرين",
|
||||
"stopEveryonesVideo": "أوقف فيديو الجميع",
|
||||
"stopVideo": "أوقف الفيديو",
|
||||
"unblockEveryoneMicCamera": "قم بإلغاء حظر ميكروفون وكاميرا الجميع",
|
||||
"videoModeration": "ابدأ الفيديو الخاص بهم"
|
||||
}
|
||||
"oldElectronClientDescription3": " الآن!"
|
||||
},
|
||||
"passwordSetRemotely": "ضبطها مشارك آخر",
|
||||
"passwordDigitsOnly": "حتى {{number}} عدد",
|
||||
"polls": {
|
||||
"create": {
|
||||
"addOption": "Add option",
|
||||
"answerPlaceholder": "Option {{index}}",
|
||||
"create": "Create a poll",
|
||||
"cancel": "Cancel",
|
||||
"pollOption" : "Poll option {{index}}",
|
||||
"pollQuestion" : "Poll Question",
|
||||
"questionPlaceholder": "Ask a question",
|
||||
"removeOption": "Remove option",
|
||||
"send": "Send"
|
||||
},
|
||||
"answer": {
|
||||
"skip": "Skip",
|
||||
"submit": "Submit"
|
||||
},
|
||||
"results": {
|
||||
"vote": "Vote",
|
||||
"changeVote": "Change vote",
|
||||
"empty": "There are no polls in the meeting yet. Start a poll here!",
|
||||
"hideDetailedResults": "Hide details",
|
||||
"showDetailedResults": "Show details"
|
||||
},
|
||||
"notification": {
|
||||
"title": "A new poll was added to this meeting",
|
||||
"description": "Open polls tab to vote"
|
||||
}
|
||||
},
|
||||
"poweredby": "مُشغَّل بوساطة",
|
||||
"prejoin": {
|
||||
"audioAndVideoError": "خطأ في الصوت أو الفيديو:",
|
||||
@@ -699,7 +540,6 @@
|
||||
"errorDialOutFailed": "فشلت عملية الاتصال، للأسف.",
|
||||
"errorDialOutStatus": "خطأ في معرفة حالة الاتصال",
|
||||
"errorMissingName": "أدخل اسمك للدخول للاجتماع",
|
||||
"errorNoPermissions": "You need to enable microphone and camera access",
|
||||
"errorStatusCode": "فشل الاتصال برمز خطأ: {{status}}",
|
||||
"errorValidation": "فشل التحقق من الرقم",
|
||||
"iWantToDialIn": "أريد الاتصال",
|
||||
@@ -713,7 +553,6 @@
|
||||
"premeeting": "ما قبل الاجتماع",
|
||||
"showScreen": "تفعيل واجهة ما قبل الاجتماع",
|
||||
"startWithPhone": "البدء مع جهاز الصوت من الجوال",
|
||||
"keyboardShortcuts" : "تفعيل اختصارات لوحة المفاتيح",
|
||||
"screenSharingError": "خطأ في مشاركة الشاشة:",
|
||||
"videoOnlyError": "خطأ في الفيديو:",
|
||||
"videoTrackError": "لم نتمكن من إنشاء ملف الفيديو",
|
||||
@@ -734,7 +573,6 @@
|
||||
"ringing": "يرن..."
|
||||
},
|
||||
"profile": {
|
||||
"avatar": "avatar",
|
||||
"setDisplayNameLabel": "حدد اسمك المراد عرضه",
|
||||
"setEmailInput": "أدخل بريدك الإلكتروني",
|
||||
"setEmailLabel": "حدد الصورة الرمزية (gravatar) لبريدك الإلكتروني",
|
||||
@@ -749,15 +587,12 @@
|
||||
"beta": "تجريبي",
|
||||
"busy": "نعمل على تحرير موارد التسجيل. حاول مرة أخرى بعد بضعة دقائق.",
|
||||
"busyTitle": "جميع المسجلين مشغولون حاليًا",
|
||||
"copyLink": "Copy Link",
|
||||
"error": "فشل التسجيل. حاول مرة أخرى.",
|
||||
"errorFetchingLink": "Error fetching recording link.",
|
||||
"expandedOff": "أوقٍف التسجيل",
|
||||
"expandedOn": "يُسجَّل الاجتماع الآن",
|
||||
"expandedPending": "بدء التسجيل...",
|
||||
"failedToStart": "فشل بدء التسجيل",
|
||||
"fileSharingdescription": "شارك التسجيل مع المشاركين للاجتماع",
|
||||
"linkGenerated": "We have generated a link to your recording.",
|
||||
"live": "مباشر",
|
||||
"loggedIn": "مُسجَّل باسم {{userName}}",
|
||||
"off": "أوقِف التسجيل",
|
||||
@@ -767,13 +602,11 @@
|
||||
"pending": "التحضير لتسجيل الاجتماع...",
|
||||
"rec": "تسجيل",
|
||||
"serviceDescription": "ستحفظ خدمة التسجيل الفيديو المستجل",
|
||||
"serviceDescriptionCloud": "Cloud recording",
|
||||
"serviceName": "خدمة التسجيل",
|
||||
"signIn": "دخول",
|
||||
"signOut": "خروج",
|
||||
"unavailable": "عجبًا! {{serviceName}} غير متاحة حاليًا. نعمل على حل المشكلة. حاول مرة أخرى لاحقًا.",
|
||||
"unavailableTitle": "التسجيل غير متاح",
|
||||
"uploadToCloud": "Upload to the cloud"
|
||||
"unavailableTitle": "التسجيل غير متاح"
|
||||
},
|
||||
"sectionList": {
|
||||
"pullToRefresh": "اسحب للتحديث"
|
||||
@@ -792,13 +625,8 @@
|
||||
"signedIn": "يجري الوصول إلى أحداث الرزنامة حاليًا التي تخص {{email}}. اضغط على زر قطع الاتصال بالأسفل لإيقاف الوصول إلى أحداث المسجلة في الرزنامة.",
|
||||
"title": "الرزنامة"
|
||||
},
|
||||
"desktopShareFramerate": "معدل إطارات مشاركة سطح المكتب",
|
||||
"desktopShareWarning": "تحتاج إلى إعادة تشغيل مشاركة الشاشة حتى تدخل الإعدادات الجديدة حيز التنفيذ. تحتاج إلى إعادة تشغيل مشاركة الشاشة حتى يتم تفعيل الإعدادات الجديدة.",
|
||||
"desktopShareHighFpsWarning": "قد يؤثر معدل الإطارات الأعلى لمشاركة سطح المكتب على النطاق الترددي الخاص بك. تحتاج إلى إعادة تشغيل مشاركة الشاشة حتى تدخل الإعدادات الجديدة حيز التنفيذ.",
|
||||
"devices": "الأجهزة",
|
||||
"followMe": "كل من يتابعني",
|
||||
"framesPerSecond": "إطار في الثانية",
|
||||
"incomingMessage": "رسالة واردة",
|
||||
"language": "اللغة",
|
||||
"loggedIn": "الدخول باسم {{name}}",
|
||||
"microphones": "المجهار (المايكروفون)",
|
||||
@@ -806,19 +634,12 @@
|
||||
"more": "المزيد",
|
||||
"name": "الاسم",
|
||||
"noDevice": "لا يوجد",
|
||||
"participantJoined": "انضم مشارك",
|
||||
"participantLeft": "غادر المشارك",
|
||||
"playSounds": "تشغيل الصوت عند:",
|
||||
"reactions": "ردود فعل الاجتماع",
|
||||
"sameAsSystem": "مثل النظام ({{label}})",
|
||||
"selectAudioOutput": "خرج الصوت",
|
||||
"selectCamera": "الكاميرا",
|
||||
"selectMic": "المجهار (المايكروفون)",
|
||||
"sounds": "اصوات",
|
||||
"speakers": "المذياع (مكبر الصوت)",
|
||||
"startAudioMuted": "بدء الجميع مكتومي الصوت",
|
||||
"startVideoMuted": "بدء الجميع دون فيديو",
|
||||
"talkWhileMuted": "تحدث أثناء كتم الصوت",
|
||||
"title": "الإعدادات"
|
||||
},
|
||||
"settingsView": {
|
||||
@@ -849,7 +670,6 @@
|
||||
},
|
||||
"speaker": "المتحدث",
|
||||
"speakerStats": {
|
||||
"search": "Search",
|
||||
"hours": "{{count}}س",
|
||||
"minutes": "{{count}}د",
|
||||
"name": "الاسم",
|
||||
@@ -859,7 +679,6 @@
|
||||
},
|
||||
"startupoverlay": {
|
||||
"policyText": " ",
|
||||
"genericTitle": "The meeting needs to use your microphone and camera.",
|
||||
"title": "يريد {{app}} استعمال المجهار والكاميرا خاصَّتك."
|
||||
},
|
||||
"suspendedoverlay": {
|
||||
@@ -871,11 +690,9 @@
|
||||
"accessibilityLabel": {
|
||||
"audioOnly": "بدل الصوت فقط",
|
||||
"audioRoute": "اختر جهاز الصوت",
|
||||
"boo": "Boo",
|
||||
"callQuality": "اضبط دقة الفيديو",
|
||||
"cc": "اظهِر/اخفِ الترجمة",
|
||||
"chat": "اظهِر/اخفِ نافذة الدردشة",
|
||||
"clap": "Clap",
|
||||
"document": "اظهِر/اخفِ الملف المشارك",
|
||||
"download": "نزِّل التطبيق",
|
||||
"embedMeeting": "ضمِّن الاجتماع",
|
||||
@@ -886,8 +703,6 @@
|
||||
"help": "مساعدة",
|
||||
"invite": "ادعُ آخرين",
|
||||
"kick": "اطرد مشاركًا",
|
||||
"laugh": "Laugh",
|
||||
"like": "Thumbs Up",
|
||||
"lobbyButton": "فعِّل/عطِّل وضع غرفة الانتظار",
|
||||
"localRecording": "اظهِر/اخفِ التحكم بالتسجيل المحلي",
|
||||
"lockRoom": "اظهِر/اخفِ كلمة مرور الاجتماع",
|
||||
@@ -896,52 +711,34 @@
|
||||
"moreOptions": "اظهر المزيد من الخيارت",
|
||||
"mute": "اظهِر/اخفِ كتم الصوت",
|
||||
"muteEveryone": "كتم الجميع",
|
||||
"muteEveryoneElse": "كتم صوت الآخرين",
|
||||
"muteEveryonesVideo": "تعطيل كاميرا الجميع",
|
||||
"muteEveryoneElsesVideo": "تعطيل كاميرا الآخرين",
|
||||
"participants": "مشاركون",
|
||||
"pip": "استعمل/اخرج من وضع صورة-في-صورة",
|
||||
"privateMessage": "أرسل رسالة خاصة",
|
||||
"profile": "عدِّل ملفك الشخصي",
|
||||
"raiseHand": "اظهِر/اخفِ رفع اليد",
|
||||
"reactionsMenu": "فتح / إغلاق قائمة التفاعلات",
|
||||
"recording": "اظهِر/اخفِ التسجيل",
|
||||
"remoteMute": "اكتم مشاركًا",
|
||||
"remoteVideoMute": "تعطيل كاميرا المشارك",
|
||||
"security": "خيارات الحماية",
|
||||
"Settings": "اظهِر/اخفِ الإعدادات",
|
||||
"shareaudio": "مشاركة الصوت",
|
||||
"sharedvideo": "اظهِر/اخفِ مشاركة فيديو يوتيوب",
|
||||
"shareRoom": "ادعُ أحدًا",
|
||||
"shareYourScreen": "بدِّل وضع مشاركة الشاشة",
|
||||
"shortcuts": "اظهِر/اخفِ الاختصارات",
|
||||
"show": "اظهر على المنصة",
|
||||
"silence": "Silence",
|
||||
"speakerStats": "اظهِر/اخفِ حالة المتحدث",
|
||||
"surprised": "Surprised",
|
||||
"tileView": "اظهِر/اخفِ عرض العنوان",
|
||||
"toggleCamera": "بدِّل الكاميرا",
|
||||
"toggleFilmstrip": "بدِّل وضع الشريط السينمائي (filmstrip)",
|
||||
"videomute": "بدِّل وضع اخفاء الفيديو",
|
||||
"videoblur": "استعمل/اخرج من وضع تغبيش خلفية الفيديو",
|
||||
"selectBackground": "اختر الخلفية",
|
||||
"expand": "وسّع",
|
||||
"collapse": "قلّص"
|
||||
"videoblur": "استعمل/اخرج من وضع تغبيش خلفية الفيديو"
|
||||
},
|
||||
"addPeople": "اضف أحدًا إلى المكالمة",
|
||||
"audioSettings": "إعدادات الصوت",
|
||||
"videoSettings": "اعدادات الفيديو",
|
||||
"audioOnlyOff": "عطِّل وضع معدَّل نقل البيانات المنخفص",
|
||||
"audioOnlyOn": "فعِّل وضع نقل البيانات المنخفض",
|
||||
"audioRoute": "اختر جهاز الصوت",
|
||||
"authenticate": "اَجرِ عملية الاستيثاق",
|
||||
"boo": "Boo",
|
||||
"callQuality": "اضبط دقة الفيديو",
|
||||
"chat": "افتح / أغلق الشاشة",
|
||||
"clap": "Clap",
|
||||
"closeChat": "أغلق الدردشة",
|
||||
"closeReactionsMenu": "إغلاق قائمة ردود الفعل",
|
||||
"disableReactionSounds": "يمكنك تعطيل أصوات ردود الفعل لهذا الاجتماع",
|
||||
"documentClose": "أغلق الملف المشارك",
|
||||
"documentOpen": "افتح الملف المشارك",
|
||||
"download": "نزِّل التطبيق",
|
||||
@@ -955,8 +752,6 @@
|
||||
"hangup": "غادر",
|
||||
"help": "مساعدة",
|
||||
"invite": "ادعُ أحدًا",
|
||||
"laugh": "Laugh",
|
||||
"like": "Thumbs Up",
|
||||
"lobbyButtonDisable": "عطِّل وضع غرفة الانتظار",
|
||||
"lobbyButtonEnable": "فعِّل وضع غرفة الانتظار",
|
||||
"login": "ادخل",
|
||||
@@ -966,7 +761,6 @@
|
||||
"moreOptions": "المزيد من الخيارات",
|
||||
"mute": "اكتم / ألغ الكتم",
|
||||
"muteEveryone": "اكتم الجميع",
|
||||
"muteEveryonesVideo": "تعطيل كاميرا الجميع",
|
||||
"noAudioSignalTitle": "لا يصلنا شيئًا من المجهار (المايكروفون) الخاص بك",
|
||||
"noAudioSignalDesc": "إن لم تكتمه عمدًا من إعدادات النظام لديك أو من الجهاز نفسه، بدِّله إلى جهاز آخر.",
|
||||
"noAudioSignalDescSuggestion": "إن لم تكتمه عمدًا من إعدادات النظام لديك أو من الجهاز نفسه، بدِّله إلى الجهاز الموصى به.",
|
||||
@@ -975,39 +769,28 @@
|
||||
"noisyAudioInputTitle": "يبدو أنَّ المجهار (المايكروفون) لديك يصدر الكثير من الضجة!",
|
||||
"noisyAudioInputDesc": "يبدو أنَّ المجهار (المايكروفون) لديك يصدر الكثير من الضجة، اكتمه رجاءً أو استعمل مجهارًا آخر.",
|
||||
"openChat": "فتح الدرشة",
|
||||
"openReactionsMenu": "افتح قائمة التفاعلات",
|
||||
"participants": "مشاركون",
|
||||
"pip": "استعمل وضع صورة-في-صورة",
|
||||
"privateMessage": "أرسل رسالة خاصة",
|
||||
"profile": "عدِّل ملفك الشخصي",
|
||||
"raiseHand": "ارفع / اخفض يدك",
|
||||
"raiseYourHand": "ارفع يدك",
|
||||
"reactionBoo": "Send boo reaction",
|
||||
"reactionClap": "Send clap reaction",
|
||||
"reactionLaugh": "Send laugh reaction",
|
||||
"reactionLike": "Send thumbs up reaction",
|
||||
"reactionSilence": "Send silence reaction",
|
||||
"reactionSurprised": "Send surprised reaction",
|
||||
"security": "خيارات الحماية",
|
||||
"Settings": "الإعدادات",
|
||||
"shareaudio": "مشاركة الصوت",
|
||||
"sharedvideo": "شارك فيديو يوتيوب",
|
||||
"shareRoom": "ادعُ أحدًا",
|
||||
"shortcuts": "اعرض الاختصارات",
|
||||
"silence": "Silence",
|
||||
"speakerStats": "حالة المتحدث",
|
||||
"startScreenSharing": "ابدأ بمشاركة الشاشة",
|
||||
"startSubtitles": "أظهر الترجمة",
|
||||
"stopAudioSharing": "وقف مشاركة الصوت",
|
||||
"stopScreenSharing": "أوقف مشاركة الشاشة",
|
||||
"stopSubtitles": "أخفِ الترجمة",
|
||||
"stopSharedVideo": "أوقف فيديو يوتيوب المشارك",
|
||||
"surprised": "Surprised",
|
||||
"talkWhileMutedPopup": "أتحاول التحدث؟ المجهار مكتوم لديك.",
|
||||
"tileViewToggle": "بدِّل عنوان العرض",
|
||||
"toggleCamera": "بدِّل الكاميرا",
|
||||
"videomute": "استعمل / أوقف الكاميرا",
|
||||
"selectBackground": "اختر الخلفية"
|
||||
"startvideoblur": "غبِّش الخلفية",
|
||||
"stopvideoblur": "توقف عن تغبيش الخلفية"
|
||||
},
|
||||
"transcribing": {
|
||||
"ccButtonTooltip": "أظهر / أخف الترجمة",
|
||||
@@ -1033,7 +816,6 @@
|
||||
"react-nativeGrantPermissions": "اختر <b><i>السماح</i></b> عندما يطلب المتصفح الأذونات.",
|
||||
"safariGrantPermissions": "اختر <b><i>تمام</i></b> عندما يطلب المتصفح الأذونات."
|
||||
},
|
||||
"volumeSlider": "Volume slider",
|
||||
"videoSIPGW": {
|
||||
"busy": "نعمل على تحرير الموارد. حاول مرة أخرى لاحقًا بعد بضعة دقائق.",
|
||||
"busyTitle": "خدمة الغرفة مشغولة حاليًا",
|
||||
@@ -1056,29 +838,26 @@
|
||||
"ld": "دقة منخفضة",
|
||||
"ldTooltip": "عرض فيديو بدقة منخفضة",
|
||||
"lowDefinition": "دقة منخفضة",
|
||||
"onlyAudioAvailable": "الصوت متاح فقط",
|
||||
"onlyAudioSupported": "الصوت مدعوم فقط في هذا المتصفح",
|
||||
"sd": "دقة قياسية",
|
||||
"sdTooltip": "عرض فيديو بدقة قياسية",
|
||||
"standardDefinition": "دقة قياسية"
|
||||
},
|
||||
"videothumbnail": {
|
||||
"connectionInfo": "معلومات الاتصال",
|
||||
"domute": "كتم",
|
||||
"domuteVideo": "تعطيل الكاميرا",
|
||||
"domuteOthers": "كتم الجميع",
|
||||
"domuteVideoOfOthers": "تعطيل الكاميرا للآخرين",
|
||||
"flip": "قلب",
|
||||
"grantModerator": "امنح صلاحيات رئيس الجلسة",
|
||||
"kick": "طرد خارجًا",
|
||||
"moderator": "رئيس الجلسة",
|
||||
"mute": "المشارك مكتوم الصوت",
|
||||
"muted": "مكتوم",
|
||||
"videoMuted": "الكاميرا معطلة",
|
||||
"remoteControl": "بدء / إيقاف التحكم البعيد",
|
||||
"show": "أظهر على المنصة",
|
||||
"videomute": "أوقف المشارك الكاميرا"
|
||||
},
|
||||
"welcomepage": {
|
||||
"addMeetingName": "Add Meeting name",
|
||||
"accessibilityLabel": {
|
||||
"join": "انقر للمشاركة",
|
||||
"roomname": "أدخل اسم الغرفة"
|
||||
@@ -1095,14 +874,8 @@
|
||||
"getHelp": "أريد مساعدة",
|
||||
"go": "ابدأ",
|
||||
"goSmall": "ابدأ",
|
||||
"headerTitle": "Jitsi Meet",
|
||||
"headerSubtitle": "Secure and high quality meetings",
|
||||
"info": "معلومات",
|
||||
"join": "أنشئ / انضم",
|
||||
"jitsiOnMobile": "Jitsi on mobile – download our apps and start a meeting from anywhere",
|
||||
"mobileDownLoadLinkIos": "Download mobile app for iOS",
|
||||
"mobileDownLoadLinkAndroid": "Download mobile app for Android",
|
||||
"mobileDownLoadLinkFDroid": "Download mobile app for F-Droid",
|
||||
"moderatedMessage": "أو a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">احجز رابط لاجتماع</a> إن كنت رئيس الجلسة الوحيد فقط.",
|
||||
"privacy": "الخصوصية",
|
||||
"recentList": "الجديد",
|
||||
@@ -1113,17 +886,8 @@
|
||||
"roomname": "أدخل اسم الغرفة",
|
||||
"roomnameHint": "أدخل اسم أو رابط الغرفة التي تريد الانضمام إليها. يمكنك إنشاء اسم جديد لترسله إلى من تريد أن تجتمع معهم.",
|
||||
"sendFeedback": "أبدِ رأيك",
|
||||
"startMeeting": "Start meeting",
|
||||
"terms": "الشروط",
|
||||
"title": "منصة عقد مؤتمرات واجتماعات آمنة وكاملة المزايا ومجانية بالمطلق",
|
||||
"logo":{
|
||||
"calendar":"Calendar logo",
|
||||
"microsoftLogo":"Microsoft logo",
|
||||
"logoDeepLinking":"Jitsi meet logo",
|
||||
"desktopPreviewThumbnail":"Desktop preview thumbnail",
|
||||
"googleLogo":"Google Logo",
|
||||
"policyLogo":"Policy logo"
|
||||
}
|
||||
"title": "منصة عقد مؤتمرات واجتماعات آمنة وكاملة المزايا ومجانية بالمطلق"
|
||||
},
|
||||
"lonelyMeetingExperience": {
|
||||
"button": "ادعُ آخرين",
|
||||
@@ -1133,8 +897,6 @@
|
||||
"header": "مركز المساعدة"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Admit",
|
||||
"admitAll": "Admit all",
|
||||
"knockingParticipantList": "تنبيه قائمة المشاركين",
|
||||
"allow": "اسمح",
|
||||
"backToKnockModeButton": "لا يوجد كلمة مرور، اطلب الإذن بالدخول بدلًا من ذلك.",
|
||||
@@ -1147,7 +909,6 @@
|
||||
"enableDialogText": "يحمي وضع الانتظار غرفة الاجتماع عبر منح رئيس الجلسة إمكانية الموافقة على انضمام المشاركين.",
|
||||
"enterPasswordButton": "أدخل كلمة المرور لهذا الاجتماع",
|
||||
"enterPasswordTitle": "أدخل كلمة المرور للدخول للاجتماع",
|
||||
"errorMissingPassword": "Please enter the meeting password",
|
||||
"invalidPassword": "كلمة مرور غير صحيحة",
|
||||
"joiningMessage": "ستتمكن من الانضمام للاجتماع بعد الموافقة على طلبك",
|
||||
"joinWithPasswordMessage": "الرجاء الانتظار أثناء محاولة الدخول دون كلمة مرور...",
|
||||
@@ -1166,7 +927,6 @@
|
||||
"passwordField": "أدخل كلمة الدخول إلى الاجتماع",
|
||||
"passwordJoinButton": "انضم",
|
||||
"reject": "رفض",
|
||||
"rejectAll": "Reject all",
|
||||
"toggleLabel": "فعِّل غرفة الانتظار"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,8 +248,6 @@
|
||||
"micPermissionDeniedError": "Vous n'avez pas autorisé l'utilisation de votre microphone. Vous pouvez toujours participer à la conférence, mais les autres ne vont pas vous entendre. Utilisez le bouton du microphone dans la barre d'adresse pour résoudre ce problème.",
|
||||
"micTimeoutError": "Impossible de démarrer la source audio. Délai dépassé!",
|
||||
"micUnknownError": "Vous ne pouvez pas utiliser le microphone pour une raison inconnue.",
|
||||
"moderationAudioLabel": "Autoriser les participants à réactiver leur micro",
|
||||
"moderationVideoLabel": "Autoriser les participants à démarrer leur vidéo",
|
||||
"muteEveryoneElseDialog": "Une fois leur micro coupé, vous ne pourrez plus le réactiver, mais ils pourront l'activer par eux-mêmes à tout moment.",
|
||||
"muteEveryoneElseTitle": "Couper le micro de tout le monde sauf de {{whom}} ?",
|
||||
"muteEveryoneDialog": "Êtes-vous sûr de vouloir couper les micros de tout le monde ? Vous ne pourrez plus réactiver leur micro, mais ils pourront l'activer par eux-mêmes à tout moment.",
|
||||
@@ -565,21 +563,12 @@
|
||||
"close": "Fermer",
|
||||
"headings": {
|
||||
"lobby": "Salle d'attente ({{count}})",
|
||||
"participantsList": "Participants de la réunion ({{count}})",
|
||||
"waitingLobby": "Dans la salle d'attente ({{count}})"
|
||||
"participantsList": "Participants de la réunion ({{count}})"
|
||||
},
|
||||
"actions": {
|
||||
"allow": "Autoriser les participant à:",
|
||||
"blockEveryoneMicCamera": "Bloquer tous les micros et caméras",
|
||||
"invite": "Inviter quelqu'un",
|
||||
"askUnmute": "Demander de réactiver le micro",
|
||||
"mute": "Couper le micro",
|
||||
"muteAll": "Couper le micro de tout le monde",
|
||||
"muteEveryoneElse": "Couper le micro de tous les autres",
|
||||
"startModeration": "Réactiver son micro ou démarrer sa vidéo",
|
||||
"stopEveryonesVideo": "Couper toutes les caméras",
|
||||
"stopVideo": "Couper la vidéo",
|
||||
"unblockEveryoneMicCamera": "Débloquer tous les micros et caméras"
|
||||
"muteAll": "couper le micro de tout le monde",
|
||||
"stopVideo": "couper la vidéo"
|
||||
}
|
||||
},
|
||||
"passwordSetRemotely": "défini par un autre participant",
|
||||
|
||||
@@ -258,7 +258,7 @@
|
||||
"passwordNotSupported": "Ajustar un $t(lockRoomPassword) a una conferéncia es pas suportat.",
|
||||
"passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) pas suportat",
|
||||
"passwordRequired": "$t(lockRoomPasswordUppercase) requesit",
|
||||
"popupError": "Vòstre navigador bloca las fenèstras que sorgisson a partir d'aqueste site. Mercés d'activar aquelas fenèstras dins los paramètres de vòstre navigador e de tornar ensajar.",
|
||||
"popupError": "Vòstre navigator bloca las fenèstras que sorgisson a partir d'aqueste site. Mercés d'activar aquelas fenèstras dins los paramètres de vòstre navigator e de tornar ensajar.",
|
||||
"popupErrorTitle": "Fenèstra que sorgís blocada",
|
||||
"readMore": "mai",
|
||||
"recording": "Enregistrament",
|
||||
@@ -353,15 +353,7 @@
|
||||
"viewUpgradeOptionsTitle": "Avètz descobèrt una foncionalitat premium !",
|
||||
"e2eeDisabledDueToMaxModeDescription": "Impossible d'activar lo chiframent del cap a la fin a causa d'un nombre tròp bèl de participants dins la conferéncia.",
|
||||
"remoteUserControls": "Contraròtle a distància de l'utilizaire {{username}}",
|
||||
"e2eeWillDisableDueToMaxModeDescription": "AVERTIMENT : lo chiframent del cap a la fin serà automaticament desactivat se mai de participants rejonhent la conferéncia.",
|
||||
"permissionCameraRequiredError": "L’autorizacion de la camèra es requerida per participar a la conferéncia amb la vidèo. Mercés de la donar als Paramètres",
|
||||
"permissionMicRequiredError": "L’autorizacion del microfòn es requerida per participar a la conferéncia amb l’àudio. Mercés de la donar als Paramètres",
|
||||
"viewUpgradeOptionsContent": "Per obténer un accès sens limit a las foncionalitats premium coma l’enregistrament, la transcripcion, la difusion RTMP e encara mai, devètz passar al nivèl superior.",
|
||||
"muteParticipantsVideoDialog": "Volètz vertadièrament copar la camèra d’aqueste participant ? Poiretz pas li activar de nòu, mas poiriá se la restablir quand vòlga.",
|
||||
"muteParticipantsVideoBody": "Poiretz pas lor activar la camèra de nòu, mas poirián se la restablir quand vòlga.",
|
||||
"shareAudioWarningD2": "devètz reaviar lo partiment d’ecran e clicar l’opcion « partejar l’àudio ».",
|
||||
"shareScreenWarningD2": "devètz arrestar lo partiment àudio, aviar lo partiment d’ecran e clicar l’opcion « partejar l’àudio ».",
|
||||
"localUserControls": "Contraròtles de l’utilizaire local"
|
||||
"e2eeWillDisableDueToMaxModeDescription": "AVERTIMENT : lo chiframent del cap a la fin serà automaticament desactivat se mai de participants rejonhent la conferéncia."
|
||||
},
|
||||
"dialOut": {
|
||||
"statusMessage": "ara es {{status}}"
|
||||
@@ -426,11 +418,7 @@
|
||||
"tooltip": "Partejar lo ligam e las informacions d’aquesta conferéncia",
|
||||
"copyNumber": "Copiar lo numèro",
|
||||
"inviteTextiOSPersonal": "{{name}} vos convida a la conferéncia.",
|
||||
"sip": "adreça SIP",
|
||||
"inviteSipEndpoint": "Per rejónher en utilizant l’adreça SIP, picatz aquò : {{sipUri}}",
|
||||
"inviteTextiOSPhone": "Per participar per telefòn, utilizatz aqueste numèro : {{number}},,{{conferenceID}}#. Se cercatz un numèro diferent, vaquí la lista complèta : {{didUrl}}.",
|
||||
"inviteTextiOSInviteUrl": "Clicatz lo ligam seguent per rejónher : {{inviteUrl}}.",
|
||||
"inviteTextiOSJoinSilent": "Se sonatz via un aparelh de sala de reünion, utilizatz aqueste ligam per rejónher sens connectar l’àudio : {{silentUrl}}."
|
||||
"sip": "adreça SIP"
|
||||
},
|
||||
"inlineDialogFailure": {
|
||||
"msg": "Avèm un pauc patit a manténer la connexion.",
|
||||
@@ -530,9 +518,7 @@
|
||||
"admit": "Acceptar",
|
||||
"admitAll": "Tot acceptar",
|
||||
"rejectAll": "Tot regetar",
|
||||
"errorMissingPassword": "Mercés de picar lo senhal de la conferéncia",
|
||||
"disableDialogContent": "La sala d’espèra es actualament activada. Aquesta foncionalitat assegura que los participants indesirables poiràn pas participar. La volètz desactivar ?",
|
||||
"enableDialogText": "La sala d’espèra vos permet de protegir vòstras conferéncias en autorizant las personas de dintrar qu’aprèp l’aprobacion formala d’un moderator."
|
||||
"errorMissingPassword": "Mercés de picar lo senhal de la conferéncia"
|
||||
},
|
||||
"localRecording": {
|
||||
"clientState": {
|
||||
@@ -582,10 +568,10 @@
|
||||
"invitedTwoMembers": "{{first}} e {{second}} son estats convidats",
|
||||
"kickParticipant": "{{kicked}} es estat expulsat per {{kicker}}",
|
||||
"me": "Ieu",
|
||||
"moderator": "Sètz ara moderator",
|
||||
"moderator": "Dreches moderator acordats !",
|
||||
"muted": "Avètz començat la conversacion en mut.",
|
||||
"mutedRemotelyDescription": "Podètz totjorn activar vòstre microfòn per prendre la paraula. Desactivatz lo microfò quand terminetz per evitar los bruches parasits.",
|
||||
"mutedRemotelyTitle": "{{participantDisplayName}} vos a mes en silenci",
|
||||
"mutedRemotelyTitle": "{{participantDisplayName}} vos a mes en silenci !",
|
||||
"mutedTitle": "Sètz en mut !",
|
||||
"newDeviceAction": "Utilizar",
|
||||
"newDeviceAudioTitle": "Nòu periferic àudio detectat",
|
||||
@@ -600,8 +586,8 @@
|
||||
"somebody": "Qualqu'un",
|
||||
"startSilentDescription": "Rejónher la conferéncia per activar l’àudio",
|
||||
"startSilentTitle": "Avètz jonch sens cap de sortida àudio !",
|
||||
"suboptimalBrowserWarning": "Planhèm que vòstra experiéncia de la conferéncia siá pas de las bonas. Sèm a cercar de solucions per melhorar aquò, d’aquel temps, ensajatz un dels <a href='{{recommendedBrowserPageLink}}' target='_blank'>navigadors compatibles</a>.",
|
||||
"suboptimalExperienceTitle": "Avertiment del navigador",
|
||||
"suboptimalBrowserWarning": "Planhèm que vòstra experiéncia de la conferéncia siá pas de las bonas. Sèm a cercar de solucions per melhorar aquò, d’aquel temps, ensajatz un dels <a href='{{recommendedBrowserPageLink}}' target='_blank'>navegators compatibles</a>.",
|
||||
"suboptimalExperienceTitle": "Avertiment del navegador",
|
||||
"unmute": "Restablir lo son",
|
||||
"allowAction": "Autorizar",
|
||||
"videoMutedRemotelyTitle": "{{moderator}} a copat vòstra camèra",
|
||||
@@ -617,13 +603,7 @@
|
||||
"raiseHandAction": "Levar la man",
|
||||
"reactionSounds": "Desactivar los sons",
|
||||
"groupTitle": "Notificacions",
|
||||
"hostAskedUnmute": "Lo moderator vos que parletz",
|
||||
"moderationInEffectDescription": "Volgatz levar la man se volètz parlar.",
|
||||
"screenShareNoAudioTitle": "Partiment impossible de l’àudio del sistèma !",
|
||||
"allowedUnmute": "Vos podètz restablir lo son del microfòn, aviar la camèra e partejar l’ecran.",
|
||||
"screenShareNoAudio": " La casa Partejar l’àudio es pas estada marcada a l’ecran de seleccion de la fenèstra.",
|
||||
"moderationInEffectCSDescription": "Volgatz levar la man se volètz partejar vòstre ecran.",
|
||||
"moderationInEffectVideoDescription": "Volgatz levar la man se volètz aviar vòstra camèra."
|
||||
"hostAskedUnmute": "Lo moderator vos que parletz"
|
||||
},
|
||||
"passwordDigitsOnly": "Fins a {{number}} chifras",
|
||||
"passwordSetRemotely": "causit per qualqu'un mai",
|
||||
@@ -652,10 +632,7 @@
|
||||
"noVideo": "Nos esperam a trobat vòstra qualitat vidèo òrra.",
|
||||
"veryPoorConnection": "Nos esperam a trobar vòstra qualitat vidèo plan òrra.",
|
||||
"videoFreezing": "Nos esperam a veire vòstra vidèo se gelar, venir negra e se pixelizar.",
|
||||
"videoHighQuality": "Nos esperam a trobar vòstra qualitat vidèo de bona qualitat.",
|
||||
"undetectable": "Se capitatz totjorn pas de sonar del navigador estant, vos recomandam de vos assegurar que vos naut-parlaires, lo microfòn e la camèra son corrèctament configurats, qu’avètz donadas las autorizacions al navegador pel microfòn e la camèra e que lo navigador es a jorn. Se tenètz d’aver de dificultats per sonar, devètz contactar lo desvolopaire de l’aplicacion Web.",
|
||||
"videoLowQuality": "Presumissèm que vòstra vidèo aurà una baissa qualitat tocant la frequéncia d’imatge e la resolucion.",
|
||||
"videoTearing": "Presumissèm que vòstra vidèo serà pixelizada e aurà d’artefacts visuals."
|
||||
"videoHighQuality": "Nos esperam a trobar vòstra qualitat vidèo de bona qualitat."
|
||||
},
|
||||
"copyAndShare": "Copiar e partejar lo ligam de conferéncia",
|
||||
"dialing": "A sonar",
|
||||
@@ -681,11 +658,7 @@
|
||||
"startWithPhone": "Començar amb l’àudio del telefòn",
|
||||
"videoOnlyError": "Error vidèo :",
|
||||
"videoTrackError": "Creacion d’una pista àudio impossibla.",
|
||||
"viewAllNumbers": "veire totes los numèros",
|
||||
"errorValidation": "Validacion del numèro fracassada",
|
||||
"keyboardShortcuts": "Activar los acorchis de clavièr",
|
||||
"errorStatusCode": "Error de numerotacion, còdi d’estat : {{status}}",
|
||||
"errorNoPermissions": "Devètz activar l’accès al microfòn e a la camèra"
|
||||
"viewAllNumbers": "veire totes los numèros"
|
||||
},
|
||||
"presenceStatus": {
|
||||
"busy": "Ocupat",
|
||||
@@ -705,8 +678,7 @@
|
||||
"setDisplayNameLabel": "Causissètz vòstre escais",
|
||||
"setEmailInput": "Picatz lo corrièl",
|
||||
"setEmailLabel": "Definissètz vòstre corrièl per Gravatar",
|
||||
"title": "Perfil",
|
||||
"avatar": "avatar"
|
||||
"title": "Perfil"
|
||||
},
|
||||
"raisedHand": "Volriá charrar",
|
||||
"recording": {
|
||||
@@ -735,13 +707,7 @@
|
||||
"signIn": "Connexion",
|
||||
"signOut": "Se desconnectar",
|
||||
"unavailable": "Ops ! Lo {{serviceName}} es pas disponible pel moment. Sèm a reglar aqueste problèma. Mercés de tornar ensajar mai tard.",
|
||||
"unavailableTitle": "Enregistrament indisponible",
|
||||
"copyLink": "Copiar lo ligam",
|
||||
"linkGenerated": "Avèm generat un ligam pel vòstre enregistrament.",
|
||||
"uploadToCloud": "Enviar al cloud",
|
||||
"limitNotificationDescriptionWeb": "A causa d’una demanda fòrta vòstre enregistrament serà limitat a {{limit}} min. Per d’enregistraments sens limit ensajatz <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
|
||||
"limitNotificationDescriptionNative": "A causa d’una demanda fòrta vòstre enregistrament serà limitat a {{limit}} min. Per d’enregistraments sens limit ensajatz <3>{{app}}</3>.",
|
||||
"errorFetchingLink": "Error en recuperant lo ligam de l’enregistrament."
|
||||
"unavailableTitle": "Enregistrament indisponible"
|
||||
},
|
||||
"sectionList": {
|
||||
"pullToRefresh": "Tirar per actualizar"
|
||||
@@ -775,19 +741,7 @@
|
||||
"speakers": "Naut-parlairs",
|
||||
"startAudioMuted": "Començan totes sens son",
|
||||
"startVideoMuted": "Començan totes sens vidèo",
|
||||
"title": "Paramètres",
|
||||
"desktopShareFramerate": "Frequéncia d’imatge del partiment de burèu",
|
||||
"framesPerSecond": "frames-per-second",
|
||||
"participantJoined": "Lo participant a rejonch",
|
||||
"participantLeft": "Lo participant es partit",
|
||||
"playSounds": "Lectura dels sons activa",
|
||||
"reactions": "Reaccions de la conferéncia",
|
||||
"sameAsSystem": "Coma pel sistèma ({{label}})",
|
||||
"sounds": "Sons",
|
||||
"talkWhileMuted": "Parlar en mut",
|
||||
"desktopShareHighFpsWarning": "Una frequéncia d’imatge mai nauta pel partiment burèu pòt afectar la benda passanta. Devètz reaviar lo partiment d’ecran per aplicar los paramètres novèls.",
|
||||
"desktopShareWarning": "Devètz reaviar lo partiment d’ecran per prendre en compte las modificacions.",
|
||||
"incomingMessage": "Messatge dintrant"
|
||||
"title": "Paramètres"
|
||||
},
|
||||
"settingsView": {
|
||||
"advanced": "Avançat",
|
||||
@@ -822,8 +776,7 @@
|
||||
"name": "Escais",
|
||||
"seconds": "{{count}} segondas",
|
||||
"speakerStats": "Estatisticas orator",
|
||||
"speakerTime": "Temps de paraula",
|
||||
"search": "Recercar"
|
||||
"speakerTime": "Temps de paraula"
|
||||
},
|
||||
"startupoverlay": {
|
||||
"genericTitle": "Aquesta reünion requerís l'utilizacion del microfòn e de la camèra.",
|
||||
@@ -878,23 +831,7 @@
|
||||
"toggleCamera": "Passar a la camèra",
|
||||
"toggleFilmstrip": "Bascular mòde benda film",
|
||||
"videoblur": "Enfoscar o non la vidèo",
|
||||
"videomute": "Silenciar la vidèo",
|
||||
"boo": "Aücar",
|
||||
"clap": "Picar de las mans",
|
||||
"laugh": "Rire",
|
||||
"like": "Levar lo det gròs",
|
||||
"muteEveryonesVideo": "Copar la camèra dels autres",
|
||||
"muteEveryoneElsesVideo": "Copar la camèra de los demai",
|
||||
"participants": "Participants",
|
||||
"remoteVideoMute": "Copar la camèra del participant",
|
||||
"shareaudio": "Partejar l’àudio",
|
||||
"silence": "Amudir",
|
||||
"surprised": "Suspresa",
|
||||
"selectBackground": "Seleccionar un rèireplan",
|
||||
"expand": "Espandir",
|
||||
"collapse": "Plegar",
|
||||
"muteEveryoneElse": "Copar lo microfòn dels autres",
|
||||
"reactionsMenu": "Dobrir / Tampar lo menú de reaccions"
|
||||
"videomute": "Silenciar la vidèo"
|
||||
},
|
||||
"addPeople": "Ajustar de monde a vòstra sonada",
|
||||
"audioOnlyOff": "Desactivar lo mòde connexion febla",
|
||||
@@ -965,17 +902,7 @@
|
||||
"laugh": "Rire",
|
||||
"muteEveryonesVideo": "Desactivar la camèra de tot lo monde",
|
||||
"stopAudioSharing": "Arrestar lo partiment àudio",
|
||||
"closeReactionsMenu": "Tampar lo menú de reaccions",
|
||||
"boo": "Aücar",
|
||||
"clap": "Picar de las mans",
|
||||
"disableReactionSounds": "Podètz desactivar los sons de las reaccions per aquesta conferéncia",
|
||||
"like": "Levar lo det gròs",
|
||||
"reactionBoo": "Enviar una reaccion de bramada",
|
||||
"reactionLaugh": "Enviar una reaccion de rire",
|
||||
"reactionLike": "Enviar una reaccion d’aprobacion",
|
||||
"reactionSilence": "Enviar una reaccion de silenci",
|
||||
"reactionSurprised": "Enviar una reaccion de suspresa",
|
||||
"reactionClap": "Enviar una reaccion d’aplaudiment"
|
||||
"closeReactionsMenu": "Tampar lo menú de reaccions"
|
||||
},
|
||||
"transcribing": {
|
||||
"ccButtonTooltip": "Aviar / Arrestat los sostítols",
|
||||
@@ -1135,8 +1062,7 @@
|
||||
"stopEveryonesVideo": "Arrestar la vidèo de tot lo monde",
|
||||
"stopVideo": "Arrestar la vidèo",
|
||||
"unblockEveryoneMicCamera": "Desblocar lo microfòn e la camèra de tot lo monde",
|
||||
"videoModeration": "Aviar lor vidèo",
|
||||
"allowVideo": "Autorizar la vidèo"
|
||||
"videoModeration": "Aviar lor vidèo"
|
||||
}
|
||||
},
|
||||
"jitsiHome": "{{logo}} Logotipe, mena a la pagina d'acuèlh",
|
||||
@@ -1160,13 +1086,10 @@
|
||||
"vote": "Votar",
|
||||
"changeVote": "Cambiar lo vote",
|
||||
"hideDetailedResults": "Rescondre los detalhs",
|
||||
"showDetailedResults": "Mostrar los detalhs",
|
||||
"empty": "I a pas cap de sondatge dins la conferéncia pel moment. Començatz-ne un aicí !"
|
||||
"showDetailedResults": "Mostrar los detalhs"
|
||||
},
|
||||
"notification": {
|
||||
"title": "Un sondatge novèl es estat apondut a la conferéncia",
|
||||
"description": "Dobrissètz l’onglet dels sondatge per votar"
|
||||
"title": "Un sondatge novèl es estat apondut a la conferéncia"
|
||||
}
|
||||
},
|
||||
"volumeSlider": "Nivèl de volum"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -801,14 +801,13 @@
|
||||
"moreOptions": "Больше настроек",
|
||||
"mute": "Микрофон (вкл./выкл.)",
|
||||
"muteEveryone": "Выкл. микрофон у всех",
|
||||
"muteEveryonesVideo": "Выкл. Камеру у всех",
|
||||
"noAudioSignalTitle": "От вашего микрофона не идет звуковой сигнал!",
|
||||
"noAudioSignalDesc": "Если вы специально не отключали микрофон в системных настройках, подумайте о том, чтобы поменять его.",
|
||||
"noAudioSignalDescSuggestion": "Если вы специально не отключали микрофон в системных настройках, вы можете попробовать использовать следующее устройство:",
|
||||
"noAudioSignalDialInDesc": "Вы можете также дозвониться используя:",
|
||||
"noAudioSignalDialInLinkDesc": "Номера для дозвона",
|
||||
"noisyAudioInputTitle": "Похоже, ваш микрофон создает шум!",
|
||||
"noAudioSignalTitle": "От вашего микрофона не идет звуковой сигнал!",
|
||||
"noisyAudioInputDesc": "Возможно, ваш микрофон создает шум. Вы можете выключить его или смените устройство.",
|
||||
"noisyAudioInputTitle": "Похоже, ваш микрофон создает шум!",
|
||||
"openChat": "Открыть чат",
|
||||
"participants": "Участники",
|
||||
"pip": "Вкл режим Картинка-в-картинке",
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import '../authentication/middleware';
|
||||
import '../base/devices/middleware';
|
||||
import '../dynamic-branding/middleware';
|
||||
import '../e2ee/middleware';
|
||||
import '../external-api/middleware';
|
||||
import '../keyboard-shortcuts/middleware';
|
||||
|
||||
@@ -84,7 +84,6 @@ export default [
|
||||
'disableAEC',
|
||||
'disableAGC',
|
||||
'disableAP',
|
||||
'disableAddingBackgroundImages',
|
||||
'disableAudioLevels',
|
||||
'disableChatSmileys',
|
||||
'disableDeepLinking',
|
||||
@@ -189,7 +188,6 @@ export default [
|
||||
'subject',
|
||||
'testing',
|
||||
'toolbarButtons',
|
||||
'toolbarConfig',
|
||||
'useHostPageLocalStorage',
|
||||
'useTurnUdp',
|
||||
'videoQuality.persist',
|
||||
|
||||
@@ -227,28 +227,6 @@ function _translateLegacyConfig(oldValue: Object) {
|
||||
newValue.toolbarButtons = interfaceConfig.TOOLBAR_BUTTONS;
|
||||
}
|
||||
|
||||
if (!oldValue.toolbarConfig) {
|
||||
oldValue.toolbarConfig = {};
|
||||
}
|
||||
|
||||
if (typeof oldValue.toolbarConfig.alwaysVisible !== 'boolean'
|
||||
&& typeof interfaceConfig === 'object'
|
||||
&& typeof interfaceConfig.TOOLBAR_ALWAYS_VISIBLE === 'boolean') {
|
||||
newValue.toolbarConfig.alwaysVisible = interfaceConfig.TOOLBAR_ALWAYS_VISIBLE;
|
||||
}
|
||||
|
||||
if (typeof oldValue.toolbarConfig.initialTimeout !== 'number'
|
||||
&& typeof interfaceConfig === 'object'
|
||||
&& typeof interfaceConfig.INITIAL_TOOLBAR_TIMEOUT === 'number') {
|
||||
newValue.toolbarConfig.initialTimeout = interfaceConfig.INITIAL_TOOLBAR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (typeof oldValue.toolbarConfig.timeout !== 'number'
|
||||
&& typeof interfaceConfig === 'object'
|
||||
&& typeof interfaceConfig.TOOLBAR_TIMEOUT === 'number') {
|
||||
newValue.toolbarConfig.timeout = interfaceConfig.TOOLBAR_TIMEOUT;
|
||||
}
|
||||
|
||||
const filteredConferenceInfo = Object.keys(CONFERENCE_HEADER_MAPPING).filter(key => oldValue[key]);
|
||||
|
||||
if (filteredConferenceInfo.length) {
|
||||
|
||||
@@ -58,6 +58,7 @@ import {
|
||||
getRemoteParticipants,
|
||||
isLocalParticipantModerator
|
||||
} from './functions';
|
||||
import logger from './logger';
|
||||
import { PARTICIPANT_JOINED_FILE, PARTICIPANT_LEFT_FILE } from './sounds';
|
||||
|
||||
declare var APP: Object;
|
||||
@@ -462,6 +463,10 @@ function _participantJoinedOrUpdated(store, next, action) {
|
||||
|
||||
// Send raisedHand signalling only if there is a change
|
||||
if (conference && raisedHand !== getLocalParticipant(getState()).raisedHand) {
|
||||
const e = new Error();
|
||||
|
||||
logger.info(`Will send raised hand ${e.stack}`);
|
||||
|
||||
conference.setLocalParticipantProperty('raisedHand', raisedHand);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,12 +55,9 @@ export function createDesiredLocalTracks(...desiredTypes) {
|
||||
dispatch(destroyLocalDesktopTrackIfExists());
|
||||
|
||||
if (desiredTypes.length === 0) {
|
||||
const { video } = state['features/base/media'];
|
||||
const { audio, video } = state['features/base/media'];
|
||||
|
||||
// XXX: Always create the audio track early, even if it will be muted.
|
||||
// This fixes a timing issue when adding the track to the conference which
|
||||
// manifests primarily on iOS 15.
|
||||
desiredTypes.push(MEDIA_TYPE.AUDIO);
|
||||
audio.muted || desiredTypes.push(MEDIA_TYPE.AUDIO);
|
||||
|
||||
// XXX When the app is coming into the foreground from the
|
||||
// background in order to handle a URL, it may realize the new
|
||||
|
||||
@@ -179,7 +179,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
} else if (jitsiTrack.isLocal() && !(jitsiTrack.videoType === VIDEO_TYPE.DESKTOP)) {
|
||||
APP.conference.setVideoMuteStatus();
|
||||
} else if (jitsiTrack.isLocal() && muted && jitsiTrack.videoType === VIDEO_TYPE.DESKTOP) {
|
||||
store.dispatch(toggleScreensharing(false, false, true));
|
||||
store.dispatch(toggleScreensharing(false));
|
||||
} else {
|
||||
APP.UI.setVideoMuted(participantID);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
* @param {string} path - The URL path.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function extractFqnFromPath() {
|
||||
const parts = window.location.pathname.split('/');
|
||||
export function extractFqnFromPath(path: string) {
|
||||
const parts = path.split('/');
|
||||
const len = parts.length;
|
||||
|
||||
return parts.length > 2 ? `${parts[len - 2]}/${parts[len - 1]}` : '';
|
||||
@@ -28,7 +28,7 @@ export function getDynamicBrandingUrl(state: Object) {
|
||||
}
|
||||
|
||||
const baseUrl = state['features/base/config'].brandingDataUrl;
|
||||
const fqn = extractFqnFromPath();
|
||||
const fqn = extractFqnFromPath(state['features/base/connection'].locationURL.pathname);
|
||||
|
||||
if (baseUrl && fqn) {
|
||||
return `${baseUrl}?conferenceFqn=${encodeURIComponent(fqn)}`;
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import { APP_WILL_MOUNT } from '../base/app';
|
||||
import { MiddlewareRegistry } from '../base/redux';
|
||||
|
||||
import { fetchCustomBrandingData } from './actions';
|
||||
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
switch (action.type) {
|
||||
case APP_WILL_MOUNT: {
|
||||
|
||||
store.dispatch(fetchCustomBrandingData());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return next(action);
|
||||
});
|
||||
@@ -1,7 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import { ReducerRegistry } from '../base/redux';
|
||||
import { type Image } from '../virtual-background/constants';
|
||||
|
||||
import {
|
||||
SET_DYNAMIC_BRANDING_DATA,
|
||||
@@ -114,15 +113,7 @@ const DEFAULT_STATE = {
|
||||
* @public
|
||||
* @type {boolean}
|
||||
*/
|
||||
useDynamicBrandingData: false,
|
||||
|
||||
/**
|
||||
* An array of images to be used as virtual backgrounds instead of the default ones.
|
||||
*
|
||||
* @public
|
||||
* @type {Array<Object>}
|
||||
*/
|
||||
virtualBackgrounds: []
|
||||
useDynamicBrandingData: false
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -140,8 +131,7 @@ ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
|
||||
inviteDomain,
|
||||
logoClickUrl,
|
||||
logoImageUrl,
|
||||
premeetingBackground,
|
||||
virtualBackgrounds
|
||||
premeetingBackground
|
||||
} = action.value;
|
||||
|
||||
return {
|
||||
@@ -156,8 +146,7 @@ ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
|
||||
premeetingBackground,
|
||||
customizationFailed: false,
|
||||
customizationReady: true,
|
||||
useDynamicBrandingData: true,
|
||||
virtualBackgrounds: formatImages(virtualBackgrounds || [])
|
||||
useDynamicBrandingData: true
|
||||
};
|
||||
}
|
||||
case SET_DYNAMIC_BRANDING_FAILED: {
|
||||
@@ -177,30 +166,3 @@ ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
|
||||
|
||||
return state;
|
||||
});
|
||||
|
||||
/**
|
||||
* Transforms the branding images into an array of Images objects ready
|
||||
* to be used as virtual backgrounds.
|
||||
*
|
||||
* @param {Array<string>} images -
|
||||
* @private
|
||||
* @returns {{Props}}
|
||||
*/
|
||||
function formatImages(images: Array<string> | Array<Object>): Array<Image> {
|
||||
return images.map((img, i) => {
|
||||
let src;
|
||||
let tooltip;
|
||||
|
||||
if (typeof img === 'object') {
|
||||
({ src, tooltip } = img);
|
||||
} else {
|
||||
src = img;
|
||||
}
|
||||
|
||||
return {
|
||||
id: `branding-${i}`,
|
||||
src,
|
||||
tooltip
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ export function sendJaasFeedbackMetadata(conference: Object, feedback: Object) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const meetingFqn = extractFqnFromPath();
|
||||
const meetingFqn = extractFqnFromPath(state['features/base/connection'].locationURL.pathname);
|
||||
const feedbackData = {
|
||||
...feedback,
|
||||
sessionId: conference.sessionId,
|
||||
|
||||
@@ -5,6 +5,7 @@ import React, { Component } from 'react';
|
||||
import { Watermarks } from '../../base/react';
|
||||
import { connect } from '../../base/redux';
|
||||
import { setColorAlpha } from '../../base/util';
|
||||
import { fetchCustomBrandingData } from '../../dynamic-branding';
|
||||
import { SharedVideo } from '../../shared-video/components/web';
|
||||
import { Captions } from '../../subtitles/';
|
||||
|
||||
@@ -27,6 +28,11 @@ type Props = {
|
||||
*/
|
||||
_customBackgroundImageUrl: string,
|
||||
|
||||
/**
|
||||
* Fetches the branding data.
|
||||
*/
|
||||
_fetchCustomBrandingData: Function,
|
||||
|
||||
/**
|
||||
* Prop that indicates whether the chat is open.
|
||||
*/
|
||||
@@ -46,6 +52,14 @@ type Props = {
|
||||
* @extends Component
|
||||
*/
|
||||
class LargeVideo extends Component<Props> {
|
||||
/**
|
||||
* Implements React's {@link Component#componentDidMount}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.props._fetchCustomBrandingData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
@@ -153,4 +167,8 @@ function _mapStateToProps(state) {
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(LargeVideo);
|
||||
const _mapDispatchToProps = {
|
||||
_fetchCustomBrandingData: fetchCustomBrandingData
|
||||
};
|
||||
|
||||
export default connect(_mapStateToProps, _mapDispatchToProps)(LargeVideo);
|
||||
|
||||
@@ -28,11 +28,6 @@ type Props = {
|
||||
*/
|
||||
_audioMediaState: MediaState,
|
||||
|
||||
/**
|
||||
* Whether or not to disable the moderator indicator.
|
||||
*/
|
||||
_disableModeratorIndicator: boolean,
|
||||
|
||||
/**
|
||||
* The display name of the participant.
|
||||
*/
|
||||
@@ -136,7 +131,6 @@ class MeetingParticipantItem extends PureComponent<Props> {
|
||||
render() {
|
||||
const {
|
||||
_audioMediaState,
|
||||
_disableModeratorIndicator,
|
||||
_displayName,
|
||||
_isModerator,
|
||||
_local,
|
||||
@@ -148,7 +142,6 @@ class MeetingParticipantItem extends PureComponent<Props> {
|
||||
return (
|
||||
<ParticipantItem
|
||||
audioMediaState = { _audioMediaState }
|
||||
disableModeratorIndicator = { _disableModeratorIndicator }
|
||||
displayName = { _displayName }
|
||||
isKnockingParticipant = { false }
|
||||
isModerator = { _isModerator }
|
||||
@@ -178,11 +171,9 @@ function mapStateToProps(state, ownProps): Object {
|
||||
const _isVideoMuted = isParticipantVideoMuted(participant, state);
|
||||
const audioMediaState = getParticipantAudioMediaState(participant, _isAudioMuted, state);
|
||||
const videoMediaState = getParticipantVideoMediaState(participant, _isVideoMuted, state);
|
||||
const { disableModeratorIndicator } = state['features/base/config'];
|
||||
|
||||
return {
|
||||
_audioMediaState: audioMediaState,
|
||||
_disableModeratorIndicator: disableModeratorIndicator,
|
||||
_displayName: getParticipantDisplayName(state, participant?.id),
|
||||
_isAudioMuted,
|
||||
_isFakeParticipant: Boolean(participant?.isFakeParticipant),
|
||||
|
||||
@@ -24,11 +24,6 @@ type Props = {
|
||||
*/
|
||||
children?: Node,
|
||||
|
||||
/**
|
||||
* Whether or not to disable the moderator indicator.
|
||||
*/
|
||||
disableModeratorIndicator?: boolean,
|
||||
|
||||
/**
|
||||
* The name of the participant. Used for showing lobby names.
|
||||
*/
|
||||
@@ -78,7 +73,6 @@ type Props = {
|
||||
function ParticipantItem({
|
||||
children,
|
||||
displayName,
|
||||
disableModeratorIndicator,
|
||||
isKnockingParticipant,
|
||||
isModerator,
|
||||
local,
|
||||
@@ -107,9 +101,7 @@ function ParticipantItem({
|
||||
</Text>
|
||||
{ local ? <Text style = { styles.isLocal }>({t('chat.you')})</Text> : null }
|
||||
</View>
|
||||
{isModerator && !disableModeratorIndicator
|
||||
&& <Text style = { styles.moderatorLabel }>{t('videothumbnail.moderator')}</Text>
|
||||
}
|
||||
{isModerator && <Text style = { styles.moderatorLabel }>{t('videothumbnail.moderator')}</Text>}
|
||||
</View>
|
||||
{
|
||||
!isKnockingParticipant
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { approveParticipant } from '../../../av-moderation/actions';
|
||||
import { Avatar } from '../../../base/avatar';
|
||||
import { isToolbarButtonEnabled } from '../../../base/config/functions.web';
|
||||
import { openDialog } from '../../../base/dialog';
|
||||
@@ -13,12 +12,10 @@ import {
|
||||
IconCrown,
|
||||
IconMessage,
|
||||
IconMicDisabled,
|
||||
IconMicrophone,
|
||||
IconMuteEveryoneElse,
|
||||
IconShareVideo,
|
||||
IconVideoOff
|
||||
} from '../../../base/icons';
|
||||
import { MEDIA_TYPE } from '../../../base/media';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
getParticipantByIdOrUndefined,
|
||||
@@ -34,7 +31,7 @@ import { Drawer, DrawerPortal } from '../../../toolbox/components/web';
|
||||
import { GrantModeratorDialog, KickRemoteParticipantDialog, MuteEveryoneDialog } from '../../../video-menu';
|
||||
import { VolumeSlider } from '../../../video-menu/components/web';
|
||||
import MuteRemoteParticipantsVideoDialog from '../../../video-menu/components/web/MuteRemoteParticipantsVideoDialog';
|
||||
import { getComputedOuterHeight, isForceMuted } from '../../functions';
|
||||
import { getComputedOuterHeight } from '../../functions';
|
||||
|
||||
import {
|
||||
ContextMenu,
|
||||
@@ -46,11 +43,6 @@ import {
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Whether or not the participant is audio force muted.
|
||||
*/
|
||||
_isAudioForceMuted: boolean,
|
||||
|
||||
/**
|
||||
* True if the local participant is moderator and false otherwise.
|
||||
*/
|
||||
@@ -76,11 +68,6 @@ type Props = {
|
||||
*/
|
||||
_isParticipantAudioMuted: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the participant is video force muted.
|
||||
*/
|
||||
_isVideoForceMuted: boolean,
|
||||
|
||||
/**
|
||||
* Shared video local participant owner.
|
||||
*/
|
||||
@@ -219,7 +206,6 @@ class MeetingParticipantContextMenu extends Component<Props, State> {
|
||||
this._onSendPrivateMessage = this._onSendPrivateMessage.bind(this);
|
||||
this._position = this._position.bind(this);
|
||||
this._onVolumeChange = this._onVolumeChange.bind(this);
|
||||
this._onAskToUnmute = this._onAskToUnmute.bind(this);
|
||||
}
|
||||
|
||||
_getCurrentParticipantId: () => string;
|
||||
@@ -358,20 +344,6 @@ class MeetingParticipantContextMenu extends Component<Props, State> {
|
||||
dispatch(setVolume(id, value));
|
||||
}
|
||||
|
||||
_onAskToUnmute: () => void;
|
||||
|
||||
/**
|
||||
* Handles click on ask to unmute.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onAskToUnmute() {
|
||||
const { _participant, dispatch } = this.props;
|
||||
const { id } = _participant;
|
||||
|
||||
dispatch(approveParticipant(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React Component's componentDidMount.
|
||||
*
|
||||
@@ -401,13 +373,11 @@ class MeetingParticipantContextMenu extends Component<Props, State> {
|
||||
*/
|
||||
render() {
|
||||
const {
|
||||
_isAudioForceMuted,
|
||||
_isLocalModerator,
|
||||
_isChatButtonEnabled,
|
||||
_isParticipantModerator,
|
||||
_isParticipantVideoMuted,
|
||||
_isParticipantAudioMuted,
|
||||
_isVideoForceMuted,
|
||||
_localVideoOwner,
|
||||
_participant,
|
||||
_volume = 1,
|
||||
@@ -446,16 +416,6 @@ class MeetingParticipantContextMenu extends Component<Props, State> {
|
||||
{_isLocalModerator && (
|
||||
<ContextMenuItemGroup>
|
||||
<>
|
||||
{overflowDrawer && (_isAudioForceMuted || _isVideoForceMuted)
|
||||
&& <ContextMenuItem onClick = { this._onAskToUnmute }>
|
||||
<ContextMenuIcon src = { IconMicrophone } />
|
||||
<span>
|
||||
{t(_isAudioForceMuted
|
||||
? 'participantsPane.actions.askUnmute'
|
||||
: 'participantsPane.actions.allowVideo')}
|
||||
</span>
|
||||
</ContextMenuItem>
|
||||
}
|
||||
{
|
||||
!_isParticipantAudioMuted && overflowDrawer
|
||||
&& <ContextMenuItem onClick = { muteAudio(_participant) }>
|
||||
@@ -582,13 +542,11 @@ function _mapStateToProps(state, ownProps): Object {
|
||||
const isLocal = participant?.local ?? true;
|
||||
|
||||
return {
|
||||
_isAudioForceMuted: isForceMuted(participant, MEDIA_TYPE.AUDIO, state),
|
||||
_isLocalModerator,
|
||||
_isChatButtonEnabled,
|
||||
_isParticipantModerator,
|
||||
_isParticipantVideoMuted,
|
||||
_isParticipantAudioMuted,
|
||||
_isVideoForceMuted: isForceMuted(participant, MEDIA_TYPE.VIDEO, state),
|
||||
_localVideoOwner: Boolean(ownerId === localParticipantId),
|
||||
_participant: participant,
|
||||
_volume: isLocal ? undefined : id ? participantsVolume[id] : undefined
|
||||
|
||||
@@ -42,9 +42,10 @@ type Props = {
|
||||
_audioTrack: ?Object,
|
||||
|
||||
/**
|
||||
* Whether or not to disable the moderator indicator.
|
||||
* Media state for video.
|
||||
*/
|
||||
_disableModeratorIndicator: boolean,
|
||||
_videoMediaState: MediaState,
|
||||
|
||||
|
||||
/**
|
||||
* The display name of the participant.
|
||||
@@ -84,11 +85,6 @@ type Props = {
|
||||
*/
|
||||
_raisedHand: boolean,
|
||||
|
||||
/**
|
||||
* Media state for video.
|
||||
*/
|
||||
_videoMediaState: MediaState,
|
||||
|
||||
/**
|
||||
* The translated ask unmute text for the qiuck action buttons.
|
||||
*/
|
||||
@@ -160,7 +156,7 @@ type Props = {
|
||||
function MeetingParticipantItem({
|
||||
_audioMediaState,
|
||||
_audioTrack,
|
||||
_disableModeratorIndicator,
|
||||
_videoMediaState,
|
||||
_displayName,
|
||||
_local,
|
||||
_localVideoOwner,
|
||||
@@ -168,7 +164,6 @@ function MeetingParticipantItem({
|
||||
_participantID,
|
||||
_quickActionButtonType,
|
||||
_raisedHand,
|
||||
_videoMediaState,
|
||||
askUnmuteText,
|
||||
isHighlighted,
|
||||
muteAudio,
|
||||
@@ -224,7 +219,6 @@ function MeetingParticipantItem({
|
||||
<ParticipantItem
|
||||
actionsTrigger = { ACTION_TRIGGER.HOVER }
|
||||
audioMediaState = { audioMediaState }
|
||||
disableModeratorIndicator = { _disableModeratorIndicator }
|
||||
displayName = { _displayName }
|
||||
isHighlighted = { isHighlighted }
|
||||
isModerator = { isParticipantModerator(_participant) }
|
||||
@@ -285,20 +279,17 @@ function _mapStateToProps(state, ownProps): Object {
|
||||
const _audioTrack = participantID === localParticipantId
|
||||
? getLocalAudioTrack(tracks) : getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.AUDIO, participantID);
|
||||
|
||||
const { disableModeratorIndicator } = state['features/base/config'];
|
||||
|
||||
return {
|
||||
_audioMediaState,
|
||||
_audioTrack,
|
||||
_disableModeratorIndicator: disableModeratorIndicator,
|
||||
_videoMediaState,
|
||||
_displayName: getParticipantDisplayName(state, participant?.id),
|
||||
_local: Boolean(participant?.local),
|
||||
_localVideoOwner: Boolean(ownerId === localParticipantId),
|
||||
_participant: participant,
|
||||
_participantID: participant?.id,
|
||||
_quickActionButtonType,
|
||||
_raisedHand: Boolean(participant?.raisedHand),
|
||||
_videoMediaState
|
||||
_raisedHand: Boolean(participant?.raisedHand)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -51,11 +51,6 @@ type Props = {
|
||||
*/
|
||||
children: Node,
|
||||
|
||||
/**
|
||||
* Whether or not to disable the moderator indicator.
|
||||
*/
|
||||
disableModeratorIndicator: boolean,
|
||||
|
||||
/**
|
||||
* The name of the participant. Used for showing lobby names.
|
||||
*/
|
||||
@@ -124,21 +119,20 @@ type Props = {
|
||||
* @returns {ReactNode}
|
||||
*/
|
||||
function ParticipantItem({
|
||||
actionsTrigger = ACTION_TRIGGER.HOVER,
|
||||
audioMediaState = MEDIA_STATE.NONE,
|
||||
children,
|
||||
disableModeratorIndicator,
|
||||
displayName,
|
||||
isHighlighted,
|
||||
isModerator,
|
||||
local,
|
||||
onLeave,
|
||||
actionsTrigger = ACTION_TRIGGER.HOVER,
|
||||
audioMediaState = MEDIA_STATE.NONE,
|
||||
videoMediaState = MEDIA_STATE.NONE,
|
||||
displayName,
|
||||
participantID,
|
||||
local,
|
||||
openDrawerForParticipant,
|
||||
overflowDrawer,
|
||||
participantID,
|
||||
raisedHand,
|
||||
t,
|
||||
videoMediaState = MEDIA_STATE.NONE,
|
||||
youText
|
||||
}: Props) {
|
||||
const ParticipantActions = Actions[actionsTrigger];
|
||||
@@ -168,7 +162,7 @@ function ParticipantItem({
|
||||
</ParticipantName>
|
||||
{ local ? <span> ({ youText })</span> : null }
|
||||
</ParticipantNameContainer>
|
||||
{isModerator && !disableModeratorIndicator && <ModeratorLabel>
|
||||
{isModerator && <ModeratorLabel>
|
||||
{t('videothumbnail.moderator')}
|
||||
</ModeratorLabel>}
|
||||
</ParticipantDetailsContainer>
|
||||
|
||||
@@ -55,6 +55,7 @@ export async function sendReactionsWebhook(state: Object, reactions: Array<?stri
|
||||
const { webhookProxyUrl: url } = state['features/base/config'];
|
||||
const { conference } = state['features/base/conference'];
|
||||
const { jwt } = state['features/base/jwt'];
|
||||
const { locationURL } = state['features/base/connection'];
|
||||
const localParticipant = getLocalParticipant(state);
|
||||
|
||||
const headers = {
|
||||
@@ -64,7 +65,7 @@ export async function sendReactionsWebhook(state: Object, reactions: Array<?stri
|
||||
|
||||
|
||||
const reqBody = {
|
||||
meetingFqn: extractFqnFromPath(),
|
||||
meetingFqn: extractFqnFromPath(locationURL.pathname),
|
||||
sessionId: conference.sessionId,
|
||||
submitted: Date.now(),
|
||||
reactions,
|
||||
|
||||
@@ -10,10 +10,10 @@ import {
|
||||
/**
|
||||
* Starts a search by criteria.
|
||||
*
|
||||
* @param {string | null} criteria - The search criteria.
|
||||
* @param {string} criteria - The search criteria.
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function initSearch(criteria: string | null) {
|
||||
export function initSearch(criteria: string) {
|
||||
return {
|
||||
type: INIT_SEARCH,
|
||||
criteria
|
||||
|
||||
@@ -10,7 +10,7 @@ import { connect } from '../../base/redux';
|
||||
import { escapeRegexp } from '../../base/util';
|
||||
import { initUpdateStats, initSearch } from '../actions';
|
||||
import { SPEAKER_STATS_RELOAD_INTERVAL } from '../constants';
|
||||
import { getSpeakerStats } from '../functions';
|
||||
import { getSpeakerStats, getSearchCriteria } from '../functions';
|
||||
|
||||
import SpeakerStatsItem from './SpeakerStatsItem';
|
||||
import SpeakerStatsLabels from './SpeakerStatsLabels';
|
||||
@@ -36,7 +36,7 @@ type Props = {
|
||||
/**
|
||||
* The search criteria.
|
||||
*/
|
||||
_criteria: string | null,
|
||||
_criteria: string,
|
||||
|
||||
/**
|
||||
* The JitsiConference from which stats will be pulled.
|
||||
@@ -140,9 +140,24 @@ class SpeakerStats extends Component<Props> {
|
||||
const dominantSpeakerTime = statsModel.getTotalDominantSpeakerTime();
|
||||
const hasLeft = statsModel.hasLeft();
|
||||
|
||||
let displayName;
|
||||
|
||||
if (statsModel.isLocalStats()) {
|
||||
const { t } = this.props;
|
||||
const meString = t('me');
|
||||
|
||||
displayName = this.props._localDisplayName;
|
||||
displayName
|
||||
= displayName ? `${displayName} (${meString})` : meString;
|
||||
} else {
|
||||
displayName
|
||||
= this.props._stats[userId].getDisplayName()
|
||||
|| interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME;
|
||||
}
|
||||
|
||||
return (
|
||||
<SpeakerStatsItem
|
||||
displayName = { statsModel.getDisplayName() }
|
||||
displayName = { displayName }
|
||||
dominantSpeakerTime = { dominantSpeakerTime }
|
||||
hasLeft = { hasLeft }
|
||||
isDominantSpeaker = { isDominantSpeaker }
|
||||
@@ -172,40 +187,7 @@ class SpeakerStats extends Component<Props> {
|
||||
* @private
|
||||
*/
|
||||
_updateStats() {
|
||||
this.props.dispatch(initUpdateStats(() => this._getSpeakerStats()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the internal state with the latest speaker stats.
|
||||
*
|
||||
* @returns {Object}
|
||||
* @private
|
||||
*/
|
||||
_getSpeakerStats() {
|
||||
const stats = { ...this.props.conference.getSpeakerStats() };
|
||||
|
||||
for (const userId in stats) {
|
||||
if (stats[userId]) {
|
||||
if (stats[userId].isLocalStats()) {
|
||||
const { t } = this.props;
|
||||
const meString = t('me');
|
||||
|
||||
stats[userId].setDisplayName(
|
||||
this.props._localDisplayName
|
||||
? `${this.props._localDisplayName} (${meString})`
|
||||
: meString
|
||||
);
|
||||
}
|
||||
|
||||
if (!stats[userId].getDisplayName()) {
|
||||
stats[userId].setDisplayName(
|
||||
interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return stats;
|
||||
this.props.dispatch(initUpdateStats(() => this.props.conference.getSpeakerStats()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,7 +198,8 @@ class SpeakerStats extends Component<Props> {
|
||||
* @private
|
||||
* @returns {{
|
||||
* _localDisplayName: ?string,
|
||||
* _stats: Object
|
||||
* _stats: Object,
|
||||
* _criteria: string,
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
@@ -230,7 +213,8 @@ function _mapStateToProps(state) {
|
||||
* @type {string|undefined}
|
||||
*/
|
||||
_localDisplayName: localParticipant && localParticipant.name,
|
||||
_stats: getSpeakerStats(state)
|
||||
_stats: getSpeakerStats(state),
|
||||
_criteria: getSearchCriteria(state)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -43,10 +43,10 @@ export function getSpeakerStats(state: Object) {
|
||||
* Gets speaker stats search criteria.
|
||||
*
|
||||
* @param {*} state - The redux state.
|
||||
* @returns {string | null} - The search criteria.
|
||||
* @returns {string} - The search criteria.
|
||||
*/
|
||||
export function getSearchCriteria(state: Object) {
|
||||
return state['features/speaker-stats']?.criteria;
|
||||
return state['features/speaker-stats']?.criteria ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,14 +161,16 @@ export function filterBySearchCriteria(state: Object, stats: ?Object) {
|
||||
const filteredStats = _.cloneDeep(stats ?? getSpeakerStats(state));
|
||||
const criteria = getSearchCriteria(state);
|
||||
|
||||
if (criteria !== null) {
|
||||
if (criteria) {
|
||||
const searchRegex = new RegExp(criteria, 'gi');
|
||||
|
||||
for (const id in filteredStats) {
|
||||
if (filteredStats[id].hasOwnProperty('_isLocalStats')) {
|
||||
const name = filteredStats[id].getDisplayName();
|
||||
|
||||
filteredStats[id].hidden = !name || !name.match(searchRegex);
|
||||
if (!name || !name.match(searchRegex)) {
|
||||
filteredStats[id].hidden = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
const INITIAL_STATE = {
|
||||
stats: {},
|
||||
pendingReorder: true,
|
||||
criteria: null
|
||||
criteria: ''
|
||||
};
|
||||
|
||||
ReducerRegistry.register('features/speaker-stats', (state = _getInitialState(), action) => {
|
||||
|
||||
@@ -55,6 +55,16 @@ export const SET_OVERFLOW_MENU_VISIBLE = 'SET_OVERFLOW_MENU_VISIBLE';
|
||||
*/
|
||||
export const SET_TOOLBAR_HOVERED = 'SET_TOOLBAR_HOVERED';
|
||||
|
||||
/**
|
||||
* The type of the action which sets the permanent visibility of the Toolbox.
|
||||
*
|
||||
* {
|
||||
* type: SET_TOOLBOX_ALWAYS_VISIBLE,
|
||||
* alwaysVisible: boolean
|
||||
* }
|
||||
*/
|
||||
export const SET_TOOLBOX_ALWAYS_VISIBLE = 'SET_TOOLBOX_ALWAYS_VISIBLE';
|
||||
|
||||
/**
|
||||
* The type of the (redux) action which enables/disables the Toolbox.
|
||||
*
|
||||
@@ -77,6 +87,17 @@ export const SET_TOOLBOX_ENABLED = 'SET_TOOLBOX_ENABLED';
|
||||
*/
|
||||
export const SET_TOOLBOX_TIMEOUT = 'SET_TOOLBOX_TIMEOUT';
|
||||
|
||||
/**
|
||||
* The type of the action which sets the delay in milliseconds after which
|
||||
* the Toolbox visibility is to be changed.
|
||||
*
|
||||
* {
|
||||
* type: SET_TOOLBOX_TIMEOUT_MS,
|
||||
* timeoutMS: number
|
||||
* }
|
||||
*/
|
||||
export const SET_TOOLBOX_TIMEOUT_MS = 'SET_TOOLBOX_TIMEOUT_MS';
|
||||
|
||||
/**
|
||||
* The type of the (redux) action which shows/hides the Toolbox.
|
||||
*
|
||||
|
||||
@@ -1,12 +1,27 @@
|
||||
// @flow
|
||||
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import {
|
||||
SET_TOOLBOX_ALWAYS_VISIBLE,
|
||||
SET_TOOLBOX_ENABLED,
|
||||
SET_TOOLBOX_VISIBLE
|
||||
} from './actionTypes';
|
||||
|
||||
/**
|
||||
* Signals that always visible toolbars value should be changed.
|
||||
*
|
||||
* @param {boolean} alwaysVisible - Value to be set in redux store.
|
||||
* @returns {{
|
||||
* type: SET_TOOLBOX_ALWAYS_VISIBLE,
|
||||
* alwaysVisible: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setToolboxAlwaysVisible(alwaysVisible: boolean): Object {
|
||||
return {
|
||||
type: SET_TOOLBOX_ALWAYS_VISIBLE,
|
||||
alwaysVisible
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables/disables the toolbox.
|
||||
*
|
||||
@@ -27,19 +42,14 @@ export function setToolboxEnabled(enabled: boolean): Object {
|
||||
* Shows/hides the toolbox.
|
||||
*
|
||||
* @param {boolean} visible - True to show the toolbox or false to hide it.
|
||||
* @returns {Function}
|
||||
* @returns {{
|
||||
* type: SET_TOOLBOX_VISIBLE,
|
||||
* visible: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setToolboxVisible(visible: boolean): Object {
|
||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const { toolbarConfig: { alwaysVisible } } = getState()['features/base/config'];
|
||||
|
||||
if (!visible && alwaysVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: SET_TOOLBOX_VISIBLE,
|
||||
visible
|
||||
});
|
||||
return {
|
||||
type: SET_TOOLBOX_VISIBLE,
|
||||
visible
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// @flow
|
||||
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { TOGGLE_TOOLBOX_VISIBLE } from './actionTypes';
|
||||
|
||||
export * from './actions.any';
|
||||
@@ -9,20 +7,12 @@ export * from './actions.any';
|
||||
/**
|
||||
* Action to toggle the toolbox visibility.
|
||||
*
|
||||
* @returns {Function}
|
||||
* @returns {{
|
||||
* type: TOGGLE_TOOLBOX_VISIBLE
|
||||
* }}
|
||||
*/
|
||||
export function toggleToolboxVisible() {
|
||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const state = getState();
|
||||
const { toolbarConfig: { alwaysVisible } } = state['features/base/config'];
|
||||
const { visible } = state['features/toolbox'];
|
||||
|
||||
if (visible && alwaysVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: TOGGLE_TOOLBOX_VISIBLE
|
||||
});
|
||||
return {
|
||||
type: TOGGLE_TOOLBOX_VISIBLE
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { overwriteConfig } from '../base/config';
|
||||
import { isLayoutTileView } from '../video-layout';
|
||||
|
||||
import {
|
||||
@@ -12,10 +11,12 @@ import {
|
||||
SET_OVERFLOW_DRAWER,
|
||||
SET_OVERFLOW_MENU_VISIBLE,
|
||||
SET_TOOLBAR_HOVERED,
|
||||
SET_TOOLBOX_TIMEOUT
|
||||
SET_TOOLBOX_TIMEOUT,
|
||||
SET_TOOLBOX_TIMEOUT_MS
|
||||
} from './actionTypes';
|
||||
import { setToolboxVisible } from './actions';
|
||||
import { getToolbarTimeout } from './functions';
|
||||
import { setToolboxVisible } from './actions.any';
|
||||
|
||||
declare var interfaceConfig: Object;
|
||||
|
||||
export * from './actions.any';
|
||||
|
||||
@@ -27,9 +28,7 @@ export * from './actions.any';
|
||||
*/
|
||||
export function dockToolbox(dock: boolean): Function {
|
||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const state = getState();
|
||||
const { visible } = state['features/toolbox'];
|
||||
const toolbarTimeout = getToolbarTimeout(state);
|
||||
const { timeoutMS, visible } = getState()['features/toolbox'];
|
||||
|
||||
if (dock) {
|
||||
// First make sure the toolbox is shown.
|
||||
@@ -40,7 +39,7 @@ export function dockToolbox(dock: boolean): Function {
|
||||
dispatch(
|
||||
setToolboxTimeout(
|
||||
() => dispatch(hideToolbox()),
|
||||
toolbarTimeout));
|
||||
timeoutMS));
|
||||
} else {
|
||||
dispatch(showToolbox());
|
||||
}
|
||||
@@ -74,9 +73,11 @@ export function fullScreenChanged(fullScreen: boolean) {
|
||||
export function hideToolbox(force: boolean = false): Function {
|
||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const state = getState();
|
||||
const { toolbarConfig: { alwaysVisible } } = state['features/base/config'];
|
||||
const { hovered } = state['features/toolbox'];
|
||||
const toolbarTimeout = getToolbarTimeout(state);
|
||||
const {
|
||||
alwaysVisible,
|
||||
hovered,
|
||||
timeoutMS
|
||||
} = state['features/toolbox'];
|
||||
|
||||
if (alwaysVisible) {
|
||||
return;
|
||||
@@ -94,7 +95,7 @@ export function hideToolbox(force: boolean = false): Function {
|
||||
dispatch(
|
||||
setToolboxTimeout(
|
||||
() => dispatch(hideToolbox()),
|
||||
toolbarTimeout));
|
||||
timeoutMS));
|
||||
} else {
|
||||
dispatch(setToolboxVisible(false));
|
||||
}
|
||||
@@ -127,13 +128,9 @@ export function showToolbox(timeout: number = 0): Object {
|
||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const state = getState();
|
||||
const {
|
||||
toolbarConfig: { initialTimeout, alwaysVisible },
|
||||
toolbarConfig
|
||||
} = state['features/base/config'];
|
||||
const toolbarTimeout = getToolbarTimeout(state);
|
||||
|
||||
const {
|
||||
alwaysVisible,
|
||||
enabled,
|
||||
timeoutMS,
|
||||
visible,
|
||||
overflowDrawer
|
||||
} = state['features/toolbox'];
|
||||
@@ -146,17 +143,11 @@ export function showToolbox(timeout: number = 0): Object {
|
||||
// If the Toolbox is always visible, there's no need for a timeout
|
||||
// to toggle its visibility.
|
||||
if (!alwaysVisible) {
|
||||
if (typeof initialTimeout === 'number') {
|
||||
// reset `initialTimeout` once it is consumed once
|
||||
dispatch(overwriteConfig({ toolbarConfig: {
|
||||
...toolbarConfig,
|
||||
initialTimeout: null
|
||||
} }));
|
||||
}
|
||||
dispatch(
|
||||
setToolboxTimeout(
|
||||
() => dispatch(hideToolbox()),
|
||||
timeout || initialTimeout || toolbarTimeout));
|
||||
timeout || timeoutMS));
|
||||
dispatch(setToolboxTimeoutMS(interfaceConfig.TOOLBAR_TIMEOUT));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -259,3 +250,18 @@ export function setToolboxTimeout(handler: Function, timeoutMS: number): Object
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an action which sets new toolbox timeout value.
|
||||
*
|
||||
* @param {number} timeoutMS - Delay.
|
||||
* @returns {{
|
||||
* type: SET_TOOLBOX_TIMEOUT_MS,
|
||||
* timeoutMS: number
|
||||
* }}
|
||||
*/
|
||||
export function setToolboxTimeoutMS(timeoutMS: number): Object {
|
||||
return {
|
||||
type: SET_TOOLBOX_TIMEOUT_MS,
|
||||
timeoutMS
|
||||
};
|
||||
}
|
||||
|
||||
@@ -29,5 +29,3 @@ export const THRESHOLDS = [
|
||||
];
|
||||
|
||||
export const NOT_APPLICABLE = 'N/A';
|
||||
|
||||
export const TOOLBAR_TIMEOUT = 4000;
|
||||
|
||||
@@ -60,14 +60,12 @@ export function getMovableButtons(width: number): Set<string> {
|
||||
*/
|
||||
export function isToolboxVisible(stateful: Object | Function) {
|
||||
const state = toState(stateful);
|
||||
const { toolbarConfig: { alwaysVisible } } = state['features/base/config'];
|
||||
const { enabled, visible } = state['features/toolbox'];
|
||||
const { alwaysVisible, enabled, visible } = state['features/toolbox'];
|
||||
const participantCount = getParticipantCountWithFake(state);
|
||||
const alwaysVisibleFlag = getFeatureFlag(state, TOOLBOX_ALWAYS_VISIBLE, false);
|
||||
const enabledFlag = getFeatureFlag(state, TOOLBOX_ENABLED, true);
|
||||
|
||||
return enabledFlag && enabled
|
||||
&& (alwaysVisible || visible || participantCount === 1 || alwaysVisibleFlag);
|
||||
return enabledFlag && enabled && (alwaysVisible || visible || participantCount === 1 || alwaysVisibleFlag);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
import { getToolbarButtons } from '../base/config';
|
||||
import { hasAvailableDevices } from '../base/devices';
|
||||
|
||||
import { TOOLBAR_TIMEOUT } from './constants';
|
||||
|
||||
/**
|
||||
* Helper for getting the height of the toolbox.
|
||||
*
|
||||
@@ -39,8 +37,9 @@ export function isButtonEnabled(name: string, state: Object) {
|
||||
* otherwise.
|
||||
*/
|
||||
export function isToolboxVisible(state: Object) {
|
||||
const { iAmSipGateway, toolbarConfig: { alwaysVisible } } = state['features/base/config'];
|
||||
const { iAmSipGateway } = state['features/base/config'];
|
||||
const {
|
||||
alwaysVisible,
|
||||
timeoutID,
|
||||
visible
|
||||
} = state['features/toolbox'];
|
||||
@@ -102,15 +101,3 @@ export function showOverflowDrawer(state: Object) {
|
||||
export function isToolboxEnabled(state: Object) {
|
||||
return state['features/toolbox'].enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the toolbar timeout from config or the default value.
|
||||
*
|
||||
* @param {Object} state - The state from the Redux store.
|
||||
* @returns {number} - Toolbar timeout in miliseconds.
|
||||
*/
|
||||
export function getToolbarTimeout(state: Object) {
|
||||
const { toolbarConfig: { timeout } } = state['features/base/config'];
|
||||
|
||||
return timeout || TOOLBAR_TIMEOUT;
|
||||
}
|
||||
|
||||
@@ -8,66 +8,121 @@ import {
|
||||
SET_OVERFLOW_DRAWER,
|
||||
SET_OVERFLOW_MENU_VISIBLE,
|
||||
SET_TOOLBAR_HOVERED,
|
||||
SET_TOOLBOX_ALWAYS_VISIBLE,
|
||||
SET_TOOLBOX_ENABLED,
|
||||
SET_TOOLBOX_TIMEOUT,
|
||||
SET_TOOLBOX_TIMEOUT_MS,
|
||||
SET_TOOLBOX_VISIBLE,
|
||||
TOGGLE_TOOLBOX_VISIBLE
|
||||
} from './actionTypes';
|
||||
|
||||
declare var interfaceConfig: Object;
|
||||
|
||||
/**
|
||||
* Initial state of toolbox's part of Redux store.
|
||||
* Returns initial state for toolbox's part of Redux store.
|
||||
*
|
||||
* @private
|
||||
* @returns {{
|
||||
* alwaysVisible: boolean,
|
||||
* enabled: boolean,
|
||||
* hovered: boolean,
|
||||
* overflowDrawer: boolean,
|
||||
* overflowMenuVisible: boolean,
|
||||
* timeoutID: number,
|
||||
* timeoutMS: number,
|
||||
* visible: boolean
|
||||
* }}
|
||||
*/
|
||||
const INITIAL_STATE = {
|
||||
function _getInitialState() {
|
||||
// Does the toolbar eventually fade out, or is it always visible?
|
||||
let alwaysVisible = false;
|
||||
|
||||
/**
|
||||
* The indicator which determines whether the Toolbox is enabled.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
enabled: true,
|
||||
// Toolbar (initial) visibility.
|
||||
let visible = false;
|
||||
|
||||
/**
|
||||
* The indicator which determines whether a Toolbar in the Toolbox is
|
||||
* hovered.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
hovered: false,
|
||||
// Default toolbox timeout for mobile app.
|
||||
let timeoutMS = 5000;
|
||||
|
||||
/**
|
||||
* The indicator which determines whether the overflow menu(s) are to be displayed as drawers.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
overflowDrawer: false,
|
||||
if (typeof interfaceConfig !== 'undefined') {
|
||||
if (interfaceConfig.INITIAL_TOOLBAR_TIMEOUT) {
|
||||
timeoutMS = interfaceConfig.INITIAL_TOOLBAR_TIMEOUT;
|
||||
}
|
||||
if (typeof interfaceConfig.TOOLBAR_ALWAYS_VISIBLE !== 'undefined') {
|
||||
alwaysVisible = interfaceConfig.TOOLBAR_ALWAYS_VISIBLE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The indicator which determines whether the OverflowMenu is visible.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
overflowMenuVisible: false,
|
||||
// When the toolbar is always visible, it must initially be visible too.
|
||||
if (alwaysVisible === true) {
|
||||
visible = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* A number, non-zero value which identifies the timer created by a call
|
||||
* to setTimeout().
|
||||
*
|
||||
* @type {number|null}
|
||||
*/
|
||||
timeoutID: null,
|
||||
return {
|
||||
/**
|
||||
* The indicator which determines whether the Toolbox should always be
|
||||
* visible. When false, the toolbar will fade out after timeoutMS.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
alwaysVisible,
|
||||
|
||||
/**
|
||||
* The indicator which determines whether the Toolbox is enabled.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
enabled: true,
|
||||
|
||||
/**
|
||||
* The indicator that determines whether the Toolbox is visible.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
visible: false
|
||||
};
|
||||
/**
|
||||
* The indicator which determines whether a Toolbar in the Toolbox is
|
||||
* hovered.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
hovered: false,
|
||||
|
||||
/**
|
||||
* The indicator which determines whether the overflow menu(s) are to be displayed as drawers.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
overflowDrawer: false,
|
||||
|
||||
/**
|
||||
* The indicator which determines whether the OverflowMenu is visible.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
overflowMenuVisible: false,
|
||||
|
||||
/**
|
||||
* A number, non-zero value which identifies the timer created by a call
|
||||
* to setTimeout() with timeoutMS.
|
||||
*
|
||||
* @type {number|null}
|
||||
*/
|
||||
timeoutID: null,
|
||||
|
||||
/**
|
||||
* The delay in milliseconds before timeoutID executes (after its
|
||||
* initialization).
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
timeoutMS,
|
||||
|
||||
/**
|
||||
* The indicator that determines whether the Toolbox is visible.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
visible
|
||||
};
|
||||
}
|
||||
|
||||
ReducerRegistry.register(
|
||||
'features/toolbox',
|
||||
(state: Object = INITIAL_STATE, action: Object) => {
|
||||
(state: Object = _getInitialState(), action: Object) => {
|
||||
switch (action.type) {
|
||||
case CLEAR_TOOLBOX_TIMEOUT:
|
||||
return {
|
||||
@@ -99,6 +154,13 @@ ReducerRegistry.register(
|
||||
hovered: action.hovered
|
||||
};
|
||||
|
||||
case SET_TOOLBOX_ALWAYS_VISIBLE:
|
||||
return {
|
||||
...state,
|
||||
alwaysVisible: action.alwaysVisible,
|
||||
visible: action.alwaysVisible === true ? true : state.visible
|
||||
};
|
||||
|
||||
case SET_TOOLBOX_ENABLED:
|
||||
return {
|
||||
...state,
|
||||
@@ -108,14 +170,21 @@ ReducerRegistry.register(
|
||||
case SET_TOOLBOX_TIMEOUT:
|
||||
return {
|
||||
...state,
|
||||
timeoutID: action.timeoutID
|
||||
timeoutID: action.timeoutID,
|
||||
timeoutMS: action.timeoutMS
|
||||
};
|
||||
|
||||
case SET_TOOLBOX_TIMEOUT_MS:
|
||||
return {
|
||||
...state,
|
||||
timeoutMS: action.timeoutMS
|
||||
};
|
||||
|
||||
case SET_TOOLBOX_VISIBLE:
|
||||
return set(state, 'visible', action.visible);
|
||||
return set(state, 'visible', state.alwaysVisible || action.visible);
|
||||
|
||||
case TOGGLE_TOOLBOX_VISIBLE:
|
||||
return set(state, 'visible', !state.visible);
|
||||
return set(state, 'visible', state.alwaysVisible || !state.visible);
|
||||
}
|
||||
|
||||
return state;
|
||||
|
||||
@@ -35,10 +35,9 @@ const logger = getLogger(__filename);
|
||||
*
|
||||
* @param {boolean} enable - Whether to mute or unmute.
|
||||
* @param {MEDIA_TYPE} mediaType - The type of the media channel to mute.
|
||||
* @param {boolean} stopScreenSharing - Whether or not to stop the screensharing.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function muteLocal(enable: boolean, mediaType: MEDIA_TYPE, stopScreenSharing: boolean = false) {
|
||||
export function muteLocal(enable: boolean, mediaType: MEDIA_TYPE) {
|
||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const isAudio = mediaType === MEDIA_TYPE.AUDIO;
|
||||
|
||||
@@ -57,7 +56,7 @@ export function muteLocal(enable: boolean, mediaType: MEDIA_TYPE, stopScreenShar
|
||||
return;
|
||||
}
|
||||
|
||||
if (enable && stopScreenSharing) {
|
||||
if (enable) {
|
||||
dispatch(toggleScreensharing(false, false, true));
|
||||
}
|
||||
|
||||
@@ -103,7 +102,7 @@ export function muteAllParticipants(exclude: Array<string>, mediaType: MEDIA_TYP
|
||||
const localId = getLocalParticipant(state).id;
|
||||
|
||||
if (!exclude.includes(localId)) {
|
||||
dispatch(muteLocal(true, mediaType, true));
|
||||
dispatch(muteLocal(true, mediaType));
|
||||
}
|
||||
|
||||
getRemoteParticipants(state).forEach((p, id) => {
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
import uuid from 'uuid';
|
||||
|
||||
import { translate } from '../../base/i18n';
|
||||
import { Icon, IconPlusCircle } from '../../base/icons';
|
||||
import { VIRTUAL_BACKGROUND_TYPE, type Image } from '../constants';
|
||||
import { resizeImage } from '../functions';
|
||||
import logger from '../logger';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Callback used to set the 'loading' state of the parent component.
|
||||
*/
|
||||
setLoading: Function,
|
||||
|
||||
/**
|
||||
* Callback used to set the options.
|
||||
*/
|
||||
setOptions: Function,
|
||||
|
||||
/**
|
||||
* Callback used to set the storedImages array.
|
||||
*/
|
||||
setStoredImages: Function,
|
||||
|
||||
/**
|
||||
* A list of images locally stored.
|
||||
*/
|
||||
storedImages: Array<Image>,
|
||||
|
||||
/**
|
||||
* If a label should be displayed alongside the button.
|
||||
*/
|
||||
showLabel: boolean,
|
||||
|
||||
/**
|
||||
* Used for translation.
|
||||
*/
|
||||
t: Function
|
||||
}
|
||||
|
||||
/**
|
||||
* Component used to upload an image.
|
||||
*
|
||||
* @param {Object} Props - The props of the component.
|
||||
* @returns {React$Node}
|
||||
*/
|
||||
function UploadImageButton({
|
||||
setLoading,
|
||||
setOptions,
|
||||
setStoredImages,
|
||||
showLabel,
|
||||
storedImages,
|
||||
t
|
||||
}: Props) {
|
||||
const uploadImageButton: Object = useRef(null);
|
||||
const uploadImageKeyPress = useCallback(e => {
|
||||
if (uploadImageButton.current && (e.key === ' ' || e.key === 'Enter')) {
|
||||
e.preventDefault();
|
||||
uploadImageButton.current.click();
|
||||
}
|
||||
}, [ uploadImageButton.current ]);
|
||||
|
||||
|
||||
const uploadImage = useCallback(async e => {
|
||||
const reader = new FileReader();
|
||||
const imageFile = e.target.files;
|
||||
|
||||
reader.readAsDataURL(imageFile[0]);
|
||||
reader.onload = async () => {
|
||||
const url = await resizeImage(reader.result);
|
||||
const uuId = uuid.v4();
|
||||
|
||||
setStoredImages([
|
||||
...storedImages,
|
||||
{
|
||||
id: uuId,
|
||||
src: url
|
||||
}
|
||||
]);
|
||||
setOptions({
|
||||
backgroundType: VIRTUAL_BACKGROUND_TYPE.IMAGE,
|
||||
enabled: true,
|
||||
url,
|
||||
selectedThumbnail: uuId
|
||||
});
|
||||
};
|
||||
logger.info('New virtual background image uploaded!');
|
||||
|
||||
reader.onerror = () => {
|
||||
setLoading(false);
|
||||
logger.error('Failed to upload virtual image!');
|
||||
};
|
||||
}, [ storedImages ]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{showLabel && <label
|
||||
aria-label = { t('virtualBackground.uploadImage') }
|
||||
className = 'file-upload-label'
|
||||
htmlFor = 'file-upload'
|
||||
onKeyPress = { uploadImageKeyPress }
|
||||
tabIndex = { 0 } >
|
||||
<Icon
|
||||
className = { 'add-background' }
|
||||
size = { 20 }
|
||||
src = { IconPlusCircle } />
|
||||
{t('virtualBackground.addBackground')}
|
||||
</label>}
|
||||
|
||||
<input
|
||||
accept = 'image/*'
|
||||
className = 'file-upload-btn'
|
||||
id = 'file-upload'
|
||||
onChange = { uploadImage }
|
||||
ref = { uploadImageButton }
|
||||
type = 'file' />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(UploadImageButton);
|
||||
@@ -3,11 +3,12 @@
|
||||
import Spinner from '@atlaskit/spinner';
|
||||
import Bourne from '@hapi/bourne';
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils/jitsi-local-storage';
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import uuid from 'uuid';
|
||||
|
||||
import { Dialog, hideDialog, openDialog } from '../../base/dialog';
|
||||
import { translate } from '../../base/i18n';
|
||||
import { Icon, IconCloseSmall, IconShareDesktop } from '../../base/icons';
|
||||
import { Icon, IconCloseSmall, IconPlusCircle, IconShareDesktop } from '../../base/icons';
|
||||
import { browser, JitsiTrackErrors } from '../../base/lib-jitsi-meet';
|
||||
import { createLocalTrack } from '../../base/lib-jitsi-meet/functions';
|
||||
import { VIDEO_TYPE } from '../../base/media';
|
||||
@@ -17,19 +18,61 @@ import { Tooltip } from '../../base/tooltip';
|
||||
import { getLocalVideoTrack } from '../../base/tracks';
|
||||
import { showErrorNotification } from '../../notifications';
|
||||
import { toggleBackgroundEffect } from '../actions';
|
||||
import { IMAGES, BACKGROUNDS_LIMIT, VIRTUAL_BACKGROUND_TYPE, type Image } from '../constants';
|
||||
import { toDataURL } from '../functions';
|
||||
import { VIRTUAL_BACKGROUND_TYPE } from '../constants';
|
||||
import { resizeImage, toDataURL } from '../functions';
|
||||
import logger from '../logger';
|
||||
|
||||
import UploadImageButton from './UploadImageButton';
|
||||
import VirtualBackgroundPreview from './VirtualBackgroundPreview';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The list of Images to choose from.
|
||||
*/
|
||||
_images: Array<Image>,
|
||||
type Image = {
|
||||
tooltip?: string,
|
||||
id: string,
|
||||
src: string
|
||||
}
|
||||
|
||||
// The limit of virtual background uploads is 24. When the number
|
||||
// of uploads is 25 we trigger the deleteStoredImage function to delete
|
||||
// the first/oldest uploaded background.
|
||||
const backgroundsLimit = 25;
|
||||
const images: Array<Image> = [
|
||||
{
|
||||
tooltip: 'image1',
|
||||
id: '1',
|
||||
src: 'images/virtual-background/background-1.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image2',
|
||||
id: '2',
|
||||
src: 'images/virtual-background/background-2.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image3',
|
||||
id: '3',
|
||||
src: 'images/virtual-background/background-3.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image4',
|
||||
id: '4',
|
||||
src: 'images/virtual-background/background-4.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image5',
|
||||
id: '5',
|
||||
src: 'images/virtual-background/background-5.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image6',
|
||||
id: '6',
|
||||
src: 'images/virtual-background/background-6.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image7',
|
||||
id: '7',
|
||||
src: 'images/virtual-background/background-7.jpg'
|
||||
}
|
||||
];
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The current local flip x status.
|
||||
@@ -46,11 +89,6 @@ type Props = {
|
||||
*/
|
||||
_selectedThumbnail: string,
|
||||
|
||||
/**
|
||||
* If the upload button should be displayed or not.
|
||||
*/
|
||||
_showUploadButton: boolean,
|
||||
|
||||
/**
|
||||
* Returns the selected virtual background object.
|
||||
*/
|
||||
@@ -90,15 +128,11 @@ const onError = event => {
|
||||
*/
|
||||
function _mapStateToProps(state): Object {
|
||||
const { localFlipX } = state['features/base/settings'];
|
||||
const dynamicBrandingImages = state['features/dynamic-branding'].virtualBackgrounds;
|
||||
const hasBrandingImages = Boolean(dynamicBrandingImages.length);
|
||||
|
||||
return {
|
||||
_localFlipX: Boolean(localFlipX),
|
||||
_images: (hasBrandingImages && dynamicBrandingImages) || IMAGES,
|
||||
_virtualBackground: state['features/virtual-background'],
|
||||
_selectedThumbnail: state['features/virtual-background'].selectedThumbnail,
|
||||
_showUploadButton: !(hasBrandingImages || state['features/base/config'].disableAddingBackgroundImages),
|
||||
_jitsiTrack: getLocalVideoTrack(state['features/base/tracks'])?.jitsiTrack
|
||||
};
|
||||
}
|
||||
@@ -111,11 +145,9 @@ const VirtualBackgroundDialog = translate(connect(_mapStateToProps)(VirtualBackg
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
function VirtualBackground({
|
||||
_images,
|
||||
_jitsiTrack,
|
||||
_localFlipX,
|
||||
_jitsiTrack,
|
||||
_selectedThumbnail,
|
||||
_showUploadButton,
|
||||
_virtualBackground,
|
||||
dispatch,
|
||||
initialOptions,
|
||||
@@ -126,7 +158,7 @@ function VirtualBackground({
|
||||
const localImages = jitsiLocalStorage.getItem('virtualBackgrounds');
|
||||
const [ storedImages, setStoredImages ] = useState<Array<Image>>((localImages && Bourne.parse(localImages)) || []);
|
||||
const [ loading, setLoading ] = useState(false);
|
||||
|
||||
const uploadImageButton: Object = useRef(null);
|
||||
const [ activeDesktopVideo ] = useState(_virtualBackground?.virtualSource?.videoType === VIDEO_TYPE.DESKTOP
|
||||
? _virtualBackground.virtualSource
|
||||
: null);
|
||||
@@ -154,7 +186,7 @@ function VirtualBackground({
|
||||
// Preventing localStorage QUOTA_EXCEEDED_ERR
|
||||
err && setStoredImages(storedImages.slice(1));
|
||||
}
|
||||
if (storedImages.length === BACKGROUNDS_LIMIT) {
|
||||
if (storedImages.length === backgroundsLimit) {
|
||||
setStoredImages(storedImages.slice(1));
|
||||
}
|
||||
}, [ storedImages ]);
|
||||
@@ -289,27 +321,61 @@ function VirtualBackground({
|
||||
|
||||
const setImageBackground = useCallback(async e => {
|
||||
const imageId = e.currentTarget.getAttribute('data-imageid');
|
||||
const image = _images.find(img => img.id === imageId);
|
||||
const image = images.find(img => img.id === imageId);
|
||||
|
||||
if (image) {
|
||||
try {
|
||||
const url = await toDataURL(image.src);
|
||||
const url = await toDataURL(image.src);
|
||||
|
||||
setOptions({
|
||||
backgroundType: 'image',
|
||||
enabled: true,
|
||||
url,
|
||||
selectedThumbnail: image.id
|
||||
});
|
||||
logger.info('Image set for virtual background preview!');
|
||||
} catch (err) {
|
||||
logger.error('Could not fetch virtual background image:', err);
|
||||
}
|
||||
setOptions({
|
||||
backgroundType: 'image',
|
||||
enabled: true,
|
||||
url,
|
||||
selectedThumbnail: image.id
|
||||
});
|
||||
logger.info('Image setted for virtual background preview!');
|
||||
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const uploadImage = useCallback(async e => {
|
||||
const reader = new FileReader();
|
||||
const imageFile = e.target.files;
|
||||
|
||||
reader.readAsDataURL(imageFile[0]);
|
||||
reader.onload = async () => {
|
||||
const url = await resizeImage(reader.result);
|
||||
const uuId = uuid.v4();
|
||||
|
||||
setStoredImages([
|
||||
...storedImages,
|
||||
{
|
||||
id: uuId,
|
||||
src: url
|
||||
}
|
||||
]);
|
||||
setOptions({
|
||||
backgroundType: VIRTUAL_BACKGROUND_TYPE.IMAGE,
|
||||
enabled: true,
|
||||
url,
|
||||
selectedThumbnail: uuId
|
||||
});
|
||||
};
|
||||
logger.info('New virtual background image uploaded!');
|
||||
|
||||
reader.onerror = () => {
|
||||
setLoading(false);
|
||||
logger.error('Failed to upload virtual image!');
|
||||
};
|
||||
}, [ dispatch, storedImages ]);
|
||||
|
||||
const uploadImageKeyPress = useCallback(e => {
|
||||
if (uploadImageButton.current && (e.key === ' ' || e.key === 'Enter')) {
|
||||
e.preventDefault();
|
||||
uploadImageButton.current.click();
|
||||
}
|
||||
}, [ uploadImageButton.current ]);
|
||||
|
||||
const setImageBackgroundKeyPress = useCallback(e => {
|
||||
if (e.key === ' ' || e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
@@ -382,13 +448,25 @@ function VirtualBackground({
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
{_showUploadButton
|
||||
&& <UploadImageButton
|
||||
setLoading = { setLoading }
|
||||
setOptions = { setOptions }
|
||||
setStoredImages = { setStoredImages }
|
||||
showLabel = { previewIsLoaded }
|
||||
storedImages = { storedImages } />}
|
||||
{previewIsLoaded && <label
|
||||
aria-label = { t('virtualBackground.uploadImage') }
|
||||
className = 'file-upload-label'
|
||||
htmlFor = 'file-upload'
|
||||
onKeyPress = { uploadImageKeyPress }
|
||||
tabIndex = { 0 } >
|
||||
<Icon
|
||||
className = { 'add-background' }
|
||||
size = { 20 }
|
||||
src = { IconPlusCircle } />
|
||||
{t('virtualBackground.addBackground')}
|
||||
</label> }
|
||||
<input
|
||||
accept = 'image/*'
|
||||
className = 'file-upload-btn'
|
||||
id = 'file-upload'
|
||||
onChange = { uploadImage }
|
||||
ref = { uploadImageButton }
|
||||
type = 'file' />
|
||||
<div
|
||||
className = 'virtual-background-dialog'
|
||||
role = 'radiogroup'
|
||||
@@ -457,7 +535,7 @@ function VirtualBackground({
|
||||
src = { IconShareDesktop } />
|
||||
</div>
|
||||
</Tooltip>
|
||||
{_images.map(image => (
|
||||
{images.map(image => (
|
||||
<Tooltip
|
||||
content = { image.tooltip && t(`virtualBackground.${image.tooltip}`) }
|
||||
key = { image.id }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
/**
|
||||
* An enumeration of the different virtual background types.
|
||||
*
|
||||
@@ -11,54 +9,3 @@ export const VIRTUAL_BACKGROUND_TYPE = {
|
||||
BLUR: 'blur',
|
||||
NONE: 'none'
|
||||
};
|
||||
|
||||
|
||||
export type Image = {
|
||||
tooltip?: string,
|
||||
id: string,
|
||||
src: string
|
||||
}
|
||||
|
||||
// The limit of virtual background uploads is 24. When the number
|
||||
// of uploads is 25 we trigger the deleteStoredImage function to delete
|
||||
// the first/oldest uploaded background.
|
||||
export const BACKGROUNDS_LIMIT = 25;
|
||||
|
||||
|
||||
export const IMAGES: Array<Image> = [
|
||||
{
|
||||
tooltip: 'image1',
|
||||
id: '1',
|
||||
src: 'images/virtual-background/background-1.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image2',
|
||||
id: '2',
|
||||
src: 'images/virtual-background/background-2.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image3',
|
||||
id: '3',
|
||||
src: 'images/virtual-background/background-3.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image4',
|
||||
id: '4',
|
||||
src: 'images/virtual-background/background-4.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image5',
|
||||
id: '5',
|
||||
src: 'images/virtual-background/background-5.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image6',
|
||||
id: '6',
|
||||
src: 'images/virtual-background/background-6.jpg'
|
||||
},
|
||||
{
|
||||
tooltip: 'image7',
|
||||
id: '7',
|
||||
src: 'images/virtual-background/background-7.jpg'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -22,7 +22,7 @@ local function get_poll_message(stanza)
|
||||
return nil;
|
||||
end
|
||||
local data = json.decode(json_data);
|
||||
if not data or (data.type ~= "new-poll" and data.type ~= "answer-poll") then
|
||||
if data.type ~= "new-poll" and data.type ~= "answer-poll" then
|
||||
return nil;
|
||||
end
|
||||
return data;
|
||||
@@ -42,7 +42,7 @@ end
|
||||
module:hook("muc-room-created", function(event)
|
||||
local room = event.room;
|
||||
if is_healthcheck_room(room.jid) then return end
|
||||
module:log("debug", "setting up polls in room %s", room.jid);
|
||||
module:log("debug", "setting up polls in room "..tostring(room));
|
||||
room.polls = {
|
||||
by_id = {};
|
||||
order = {};
|
||||
|
||||
@@ -128,9 +128,9 @@ function Util:get_public_key(keyId)
|
||||
local content = self.cache:get(keyId);
|
||||
if content == nil then
|
||||
-- If the key is not found in the cache.
|
||||
module:log("debug", "Cache miss for key: %s", keyId);
|
||||
module:log("debug", "Cache miss for key: "..keyId);
|
||||
local keyurl = path.join(self.asapKeyServer, hex.to(sha256(keyId))..'.pem');
|
||||
module:log("debug", "Fetching public key from: %s", keyurl);
|
||||
module:log("debug", "Fetching public key from: "..keyurl);
|
||||
content = http_get_with_retry(keyurl, nr_retries);
|
||||
if content ~= nil then
|
||||
self.cache:set(keyId, content);
|
||||
@@ -138,7 +138,7 @@ function Util:get_public_key(keyId)
|
||||
return content;
|
||||
else
|
||||
-- If the key is in the cache, use it.
|
||||
module:log("debug", "Cache hit for key: %s", keyId);
|
||||
module:log("debug", "Cache hit for key: "..keyId);
|
||||
return content;
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user