Compare commits

..

2 Commits

Author SHA1 Message Date
Дамян Минков
f515617140 debug: Fix lint errors. 2021-09-23 22:04:38 -05:00
Дамян Минков
b66468b8c4 debug: Do not merge, debugging raise hand. 2021-09-23 21:53:47 -05:00
44 changed files with 490 additions and 1018 deletions

View File

@@ -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() {

View File

@@ -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,

View File

@@ -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 {

View File

@@ -75,7 +75,3 @@
top: 0;
height: 48px;
}
.shift-right .details-container {
margin-left: calc(#{$sidebarWidth} / 2);
}

View File

@@ -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

View File

@@ -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": "فعِّل غرفة الانتظار"
}
}

View File

@@ -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",

View File

@@ -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": "Lautorizacion 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": "Lautorizacion 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 lenregistrament, la transcripcion, la difusion RTMP e encara mai, devètz passar al nivèl superior.",
"muteParticipantsVideoDialog": "Volètz vertadièrament copar la camèra daqueste 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 decran e clicar lopcion «partejar làudio».",
"shareScreenWarningD2": "devètz arrestar lo partiment àudio, aviar lo partiment decran e clicar lopcion «partejar làudio».",
"localUserControls": "Contraròtles de lutilizaire 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 daquesta 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 ladreç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 despèra es actualament activada. Aquesta foncionalitat assegura que los participants indesirables poiràn pas participar. La volètz desactivar?",
"enableDialogText": "La sala despèra vos permet de protegir vòstras conferéncias en autorizant las personas de dintrar quaprèp laprobacion formala dun 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ò, daquel 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ò, daquel 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 lecran.",
"screenShareNoAudio": " La casa Partejar làudio es pas estada marcada a lecran 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, quavètz donadas las autorizacions al navegador pel microfòn e la camèra e que lo navigador es a jorn. Se tenètz daver de dificultats per sonar, devètz contactar lo desvolopaire de laplicacion Web.",
"videoLowQuality": "Presumissèm que vòstra vidèo aurà una baissa qualitat tocant la frequéncia dimatge e la resolucion.",
"videoTearing": "Presumissèm que vòstra vidèo serà pixelizada e aurà dartefacts 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 duna 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 destat: {{status}}",
"errorNoPermissions": "Devètz activar laccè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 duna demanda fòrta vòstre enregistrament serà limitat a {{limit}} min. Per denregistraments sens limit ensajatz <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
"limitNotificationDescriptionNative": "A causa duna demanda fòrta vòstre enregistrament serà limitat a {{limit}} min. Per denregistraments sens limit ensajatz <3>{{app}}</3>.",
"errorFetchingLink": "Error en recuperant lo ligam de lenregistrament."
"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 dimatge 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 dimatge mai nauta pel partiment burèu pòt afectar la benda passanta. Devètz reaviar lo partiment decran per aplicar los paramètres novèls.",
"desktopShareWarning": "Devètz reaviar lo partiment decran 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 daprobacion",
"reactionSilence": "Enviar una reaccion de silenci",
"reactionSurprised": "Enviar una reaccion de suspresa",
"reactionClap": "Enviar una reaccion daplaudiment"
"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 longlet dels sondatge per votar"
"title": "Un sondatge novèl es estat apondut a la conferéncia"
}
},
"volumeSlider": "Nivèl de volum"
}
}

View File

@@ -801,14 +801,13 @@
"moreOptions": "Больше настроек",
"mute": "Микрофон (вкл./выкл.)",
"muteEveryone": "Выкл. микрофон у всех",
"muteEveryonesVideo": "Выкл. Камеру у всех",
"noAudioSignalTitle": "От вашего микрофона не идет звуковой сигнал!",
"noAudioSignalDesc": "Если вы специально не отключали микрофон в системных настройках, подумайте о том, чтобы поменять его.",
"noAudioSignalDescSuggestion": "Если вы специально не отключали микрофон в системных настройках, вы можете попробовать использовать следующее устройство:",
"noAudioSignalDialInDesc": "Вы можете также дозвониться используя:",
"noAudioSignalDialInLinkDesc": "Номера для дозвона",
"noisyAudioInputTitle": "Похоже, ваш микрофон создает шум!",
"noAudioSignalTitle": "От вашего микрофона не идет звуковой сигнал!",
"noisyAudioInputDesc": "Возможно, ваш микрофон создает шум. Вы можете выключить его или смените устройство.",
"noisyAudioInputTitle": "Похоже, ваш микрофон создает шум!",
"openChat": "Открыть чат",
"participants": "Участники",
"pip": "Вкл режим Картинка-в-картинке",

View File

@@ -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';

View File

@@ -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',

View File

@@ -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) {

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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)}`;

View File

@@ -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);
});

View File

@@ -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
};
});
}

View File

@@ -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,

View File

@@ -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);

View File

@@ -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),

View File

@@ -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

View File

@@ -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

View File

@@ -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)
};
}

View File

@@ -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>&nbsp;({ youText })</span> : null }
</ParticipantNameContainer>
{isModerator && !disableModeratorIndicator && <ModeratorLabel>
{isModerator && <ModeratorLabel>
{t('videothumbnail.moderator')}
</ModeratorLabel>}
</ParticipantDetailsContainer>

View File

@@ -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,

View File

@@ -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

View File

@@ -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)
};
}

View File

@@ -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;
}
}
}
}

View File

@@ -18,7 +18,7 @@ import {
const INITIAL_STATE = {
stats: {},
pendingReorder: true,
criteria: null
criteria: ''
};
ReducerRegistry.register('features/speaker-stats', (state = _getInitialState(), action) => {

View File

@@ -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.
*

View File

@@ -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
};
}

View File

@@ -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
};
}

View File

@@ -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
};
}

View File

@@ -29,5 +29,3 @@ export const THRESHOLDS = [
];
export const NOT_APPLICABLE = 'N/A';
export const TOOLBAR_TIMEOUT = 4000;

View File

@@ -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);
}
/**

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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) => {

View File

@@ -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);

View File

@@ -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 }

View File

@@ -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'
}
];

View File

@@ -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 = {};

View File

@@ -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