Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/spreed.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarco <marcoambrosini@pm.me>2022-04-29 18:00:40 +0300
committerGitHub <noreply@github.com>2022-04-29 18:00:40 +0300
commitc7494125de305571f93b185ec2ea6a1997805691 (patch)
tree4739ce643db68f830e301a49c196b3ad98d3d30a
parentace7ccbe0c9bc9649e15fc39748e37b7290ddcec (diff)
parent4e76690e99d35c37be29e5fa950dca6975290585 (diff)
Merge branch 'master' into feature/noid/improve-shareditems-componentfeature/noid/improve-shareditems-component
-rw-r--r--CHANGELOG.md16
-rw-r--r--appinfo/info.xml2
-rw-r--r--docs/capabilities.md3
-rw-r--r--docs/constants.md1
-rw-r--r--l10n/af.js8
-rw-r--r--l10n/af.json8
-rw-r--r--l10n/ar.js12
-rw-r--r--l10n/ar.json12
-rw-r--r--l10n/ast.js6
-rw-r--r--l10n/ast.json6
-rw-r--r--l10n/bg.js37
-rw-r--r--l10n/bg.json37
-rw-r--r--l10n/br.js8
-rw-r--r--l10n/br.json8
-rw-r--r--l10n/ca.js12
-rw-r--r--l10n/ca.json12
-rw-r--r--l10n/cs.js18
-rw-r--r--l10n/cs.json18
-rw-r--r--l10n/da.js12
-rw-r--r--l10n/da.json12
-rw-r--r--l10n/de.js16
-rw-r--r--l10n/de.json16
-rw-r--r--l10n/de_DE.js18
-rw-r--r--l10n/de_DE.json18
-rw-r--r--l10n/el.js12
-rw-r--r--l10n/el.json12
-rw-r--r--l10n/en_GB.js8
-rw-r--r--l10n/en_GB.json8
-rw-r--r--l10n/eo.js6
-rw-r--r--l10n/eo.json6
-rw-r--r--l10n/es.js12
-rw-r--r--l10n/es.json12
-rw-r--r--l10n/es_419.js6
-rw-r--r--l10n/es_419.json6
-rw-r--r--l10n/es_AR.js10
-rw-r--r--l10n/es_AR.json10
-rw-r--r--l10n/es_CL.js6
-rw-r--r--l10n/es_CL.json6
-rw-r--r--l10n/es_CO.js6
-rw-r--r--l10n/es_CO.json6
-rw-r--r--l10n/es_CR.js6
-rw-r--r--l10n/es_CR.json6
-rw-r--r--l10n/es_DO.js6
-rw-r--r--l10n/es_DO.json6
-rw-r--r--l10n/es_EC.js6
-rw-r--r--l10n/es_EC.json6
-rw-r--r--l10n/es_GT.js6
-rw-r--r--l10n/es_GT.json6
-rw-r--r--l10n/es_HN.js6
-rw-r--r--l10n/es_HN.json6
-rw-r--r--l10n/es_MX.js10
-rw-r--r--l10n/es_MX.json10
-rw-r--r--l10n/es_NI.js6
-rw-r--r--l10n/es_NI.json6
-rw-r--r--l10n/es_PA.js6
-rw-r--r--l10n/es_PA.json6
-rw-r--r--l10n/es_PE.js6
-rw-r--r--l10n/es_PE.json6
-rw-r--r--l10n/es_PR.js6
-rw-r--r--l10n/es_PR.json6
-rw-r--r--l10n/es_PY.js8
-rw-r--r--l10n/es_PY.json8
-rw-r--r--l10n/es_SV.js6
-rw-r--r--l10n/es_SV.json6
-rw-r--r--l10n/es_UY.js6
-rw-r--r--l10n/es_UY.json6
-rw-r--r--l10n/et_EE.js8
-rw-r--r--l10n/et_EE.json8
-rw-r--r--l10n/eu.js12
-rw-r--r--l10n/eu.json12
-rw-r--r--l10n/fa.js12
-rw-r--r--l10n/fa.json12
-rw-r--r--l10n/fi.js40
-rw-r--r--l10n/fi.json40
-rw-r--r--l10n/fr.js12
-rw-r--r--l10n/fr.json12
-rw-r--r--l10n/gl.js12
-rw-r--r--l10n/gl.json12
-rw-r--r--l10n/he.js8
-rw-r--r--l10n/he.json8
-rw-r--r--l10n/hr.js12
-rw-r--r--l10n/hr.json12
-rw-r--r--l10n/hu.js18
-rw-r--r--l10n/hu.json18
-rw-r--r--l10n/id.js8
-rw-r--r--l10n/id.json8
-rw-r--r--l10n/is.js10
-rw-r--r--l10n/is.json10
-rw-r--r--l10n/it.js12
-rw-r--r--l10n/it.json12
-rw-r--r--l10n/ja.js12
-rw-r--r--l10n/ja.json12
-rw-r--r--l10n/ka_GE.js6
-rw-r--r--l10n/ka_GE.json6
-rw-r--r--l10n/ko.js10
-rw-r--r--l10n/ko.json10
-rw-r--r--l10n/lt_LT.js10
-rw-r--r--l10n/lt_LT.json10
-rw-r--r--l10n/lv.js6
-rw-r--r--l10n/lv.json6
-rw-r--r--l10n/mk.js10
-rw-r--r--l10n/mk.json10
-rw-r--r--l10n/nb.js12
-rw-r--r--l10n/nb.json12
-rw-r--r--l10n/nl.js12
-rw-r--r--l10n/nl.json12
-rw-r--r--l10n/nn_NO.js5
-rw-r--r--l10n/nn_NO.json5
-rw-r--r--l10n/oc.js14
-rw-r--r--l10n/oc.json14
-rw-r--r--l10n/pl.js18
-rw-r--r--l10n/pl.json18
-rw-r--r--l10n/pt_BR.js16
-rw-r--r--l10n/pt_BR.json16
-rw-r--r--l10n/pt_PT.js10
-rw-r--r--l10n/pt_PT.json10
-rw-r--r--l10n/ro.js10
-rw-r--r--l10n/ro.json10
-rw-r--r--l10n/ru.js12
-rw-r--r--l10n/ru.json12
-rw-r--r--l10n/sc.js12
-rw-r--r--l10n/sc.json12
-rw-r--r--l10n/sk.js12
-rw-r--r--l10n/sk.json12
-rw-r--r--l10n/sl.js12
-rw-r--r--l10n/sl.json12
-rw-r--r--l10n/sq.js8
-rw-r--r--l10n/sq.json8
-rw-r--r--l10n/sr.js12
-rw-r--r--l10n/sr.json12
-rw-r--r--l10n/sv.js12
-rw-r--r--l10n/sv.json12
-rw-r--r--l10n/th.js8
-rw-r--r--l10n/th.json8
-rw-r--r--l10n/tr.js18
-rw-r--r--l10n/tr.json18
-rw-r--r--l10n/uk.js10
-rw-r--r--l10n/uk.json10
-rw-r--r--l10n/vi.js12
-rw-r--r--l10n/vi.json12
-rw-r--r--l10n/zh_CN.js12
-rw-r--r--l10n/zh_CN.json12
-rw-r--r--l10n/zh_HK.js18
-rw-r--r--l10n/zh_HK.json18
-rw-r--r--l10n/zh_TW.js12
-rw-r--r--l10n/zh_TW.json12
-rw-r--r--lib/Capabilities.php1
-rw-r--r--lib/Chat/SystemMessage/Listener.php480
-rw-r--r--lib/Collaboration/Collaborators/RoomPlugin.php8
-rw-r--r--lib/Command/ActiveCalls.php19
-rw-r--r--lib/Controller/ChatController.php6
-rw-r--r--lib/Controller/ReactionController.php9
-rw-r--r--lib/Events/AlreadySharedEvent.php45
-rw-r--r--lib/Exceptions/PermissionsException.php28
-rw-r--r--lib/Listener/RestrictStartingCalls.php12
-rw-r--r--lib/Middleware/InjectionMiddleware.php27
-rw-r--r--lib/Migration/Version15000Date20220427183026.php113
-rw-r--r--lib/Model/Attendee.php2
-rw-r--r--lib/Share/RoomShareProvider.php20
-rw-r--r--lib/Signaling/Listener.php511
-rw-r--r--lib/Status/Listener.php36
-rw-r--r--package-lock.json129
-rw-r--r--package.json4
-rw-r--r--src/__mocks__/attachmediastream.js17
-rw-r--r--src/components/AvatarWrapper/AvatarWrapper.vue9
-rw-r--r--src/components/CallView/CallView.vue12
-rw-r--r--src/components/CallView/Grid/Grid.vue6
-rw-r--r--src/components/CallView/shared/Video.spec.js1147
-rw-r--r--src/components/CallView/shared/Video.vue22
-rw-r--r--src/components/CallView/shared/VideoBottomBar.vue11
-rw-r--r--src/components/ConversationSettings/Matterbridge/BridgePart.vue6
-rw-r--r--src/components/ConversationSettings/Matterbridge/MatterbridgeSettings.vue6
-rw-r--r--src/components/MessagesList/MessagesGroup/Message/Message.spec.js35
-rw-r--r--src/components/MessagesList/MessagesGroup/Message/Message.vue34
-rw-r--r--src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.vue4
-rw-r--r--src/components/MessagesList/MessagesGroup/Message/MessagePart/Contact.vue6
-rw-r--r--src/components/MessagesList/MessagesGroup/Message/MessagePart/DeckCard.vue5
-rw-r--r--src/components/NewMessageForm/NewMessageForm.vue10
-rw-r--r--src/components/PermissionsEditor/PermissionsEditor.vue10
-rw-r--r--src/components/RightSidebar/SharedItems/SharedItemsBrowser/SharedItemsBrowser.vue6
-rw-r--r--src/components/SettingsDialog/SettingsDialog.vue12
-rw-r--r--src/constants.js5
-rw-r--r--src/store/guestNameStore.js8
-rw-r--r--src/utils/signaling.js5
-rw-r--r--src/utils/webrtc/RemoteVideoBlocker.js131
-rw-r--r--src/utils/webrtc/RemoteVideoBlocker.spec.js336
-rw-r--r--src/utils/webrtc/models/CallParticipantModel.js44
-rw-r--r--src/utils/webrtc/simplewebrtc/peer.js68
-rw-r--r--src/utils/webrtc/webrtc.js41
-rw-r--r--tests/integration/features/bootstrap/FeatureContext.php2
-rw-r--r--tests/integration/features/chat/delete.feature16
-rw-r--r--tests/integration/features/chat/file-share.feature29
-rw-r--r--tests/integration/features/chat/group.feature12
-rw-r--r--tests/integration/features/chat/public.feature13
-rw-r--r--tests/integration/features/chat/rich-object-share.feature25
-rw-r--r--tests/integration/features/conversation-2/set-participant-permissions.feature214
-rw-r--r--tests/integration/features/conversation-2/set-permissions.feature48
-rw-r--r--tests/integration/features/reaction/react.feature40
-rw-r--r--tests/php/CapabilitiesTest.php1
-rw-r--r--tests/php/Collaboration/Collaborators/RoomPluginTest.php18
-rw-r--r--tests/php/Listener/RestrictStartingCallsTest.php2
-rw-r--r--tests/psalm-baseline.xml36
202 files changed, 3980 insertions, 1508 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index aec23554c..2757e3f61 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,22 @@
# Changelog
All notable changes to this project will be documented in this file.
+## 14.0.0-rc.4 – 2022-04-29
+### Added
+- Add a modal to show more shared items
+
+### Fixed
+- Make reactions work for guests and handle guests without name in the summary
+ [7217](https://github.com/nextcloud/spreed/pull/7217)
+- Add a link to notification sound settings from talk settings
+ [7224](https://github.com/nextcloud/spreed/pull/7224)
+- Fix migration with Postgres and Oracle
+ [7211](https://github.com/nextcloud/spreed/pull/7211)
+- Add programmatic output options to talk:active-calls command
+ [7227](https://github.com/nextcloud/spreed/pull/7227)
+- Fix media tab and reactions summary in read-only rooms
+ [7236](https://github.com/nextcloud/spreed/pull/7236)
+
## 14.0.0-rc.3 – 2022-04-22
### Fixed
- Move message.reactions.self to message.reactionsSelf to not merge different data structures
diff --git a/appinfo/info.xml b/appinfo/info.xml
index 685656237..b5a25ea37 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -16,7 +16,7 @@ And in the works for the [coming versions](https://github.com/nextcloud/spreed/m
]]></description>
- <version>15.0.0-dev.2</version>
+ <version>15.0.0-dev.3</version>
<licence>agpl</licence>
<author>Aleksandra Lazarević</author>
diff --git a/docs/capabilities.md b/docs/capabilities.md
index f9e989427..3c029b860 100644
--- a/docs/capabilities.md
+++ b/docs/capabilities.md
@@ -92,3 +92,6 @@ title: Capabilities
* `reactions` - Api reactions to chat message
* `rich-object-list-media` - When the API to get the chat messages for shared media is available
* `rich-object-delete` - When the API allows to delete chat messages which are file or rich object shares
+
+## 15
+* `chat-permission` - When permission 128 is required to post chat messages, reaction or share items to the conversation
diff --git a/docs/constants.md b/docs/constants.md
index 108bfb668..f170118c8 100644
--- a/docs/constants.md
+++ b/docs/constants.md
@@ -70,6 +70,7 @@ title: Constants
* `16` Can publish audio stream
* `32` Can publish video stream
* `64` Can publish screen sharing stream
+* `128` Can post chat message, share items and do reactions
### Attendee permission modifications
* `set` - Setting this permission set.
diff --git a/l10n/af.js b/l10n/af.js
index d4ffaf17f..1dd6ef9f8 100644
--- a/l10n/af.js
+++ b/l10n/af.js
@@ -299,14 +299,14 @@ OC.L10N.register(
"Chat" : "Klets",
"Details" : "Besonderhede",
"Settings" : "Instellings",
- "Media" : "Media",
- "Files" : " Lêers",
- "Audio" : "Oudio",
- "Other" : "Ander",
"Privacy" : "Privaatheid",
"Keyboard shortcuts" : "Sneltoetse",
"Search" : "Soek",
"Send" : "Stuur",
+ "Media" : "Media",
+ "Files" : " Lêers",
+ "Audio" : "Oudio",
+ "Other" : "Ander",
"Do not disturb" : "Moenie pla nie",
"Away" : "Weg",
"Default" : "Verstek",
diff --git a/l10n/af.json b/l10n/af.json
index 82c3ebbb2..e9db551d5 100644
--- a/l10n/af.json
+++ b/l10n/af.json
@@ -297,14 +297,14 @@
"Chat" : "Klets",
"Details" : "Besonderhede",
"Settings" : "Instellings",
- "Media" : "Media",
- "Files" : " Lêers",
- "Audio" : "Oudio",
- "Other" : "Ander",
"Privacy" : "Privaatheid",
"Keyboard shortcuts" : "Sneltoetse",
"Search" : "Soek",
"Send" : "Stuur",
+ "Media" : "Media",
+ "Files" : " Lêers",
+ "Audio" : "Oudio",
+ "Other" : "Ander",
"Do not disturb" : "Moenie pla nie",
"Away" : "Weg",
"Default" : "Verstek",
diff --git a/l10n/ar.js b/l10n/ar.js
index f0e2d7fcc..a38273918 100644
--- a/l10n/ar.js
+++ b/l10n/ar.js
@@ -914,13 +914,8 @@ OC.L10N.register(
"Chat" : "الدردشة",
"Details" : "التفاصيل",
"Settings" : "الإعدادات",
- "Media" : "وسائط",
- "Files" : "الملفات",
- "Locations" : "المواقع",
- "Audio" : "الصوت",
- "Other" : "آخر",
- "Show all files" : "أظهر جميع الملفات",
"Projects" : "المشاريع",
+ "Show all files" : "أظهر جميع الملفات",
"Meeting ID: {meetingId}" : "معرف الاجتماع: {meetingId}",
"Your PIN: {attendeePin}" : "رقمك السري: {attendeePin}",
"Attachments folder" : "مجلد المرفقات",
@@ -984,6 +979,11 @@ OC.L10N.register(
"Error while accessing microphone" : "حدث خطأ اثناء الوصول إلى المايكرفون",
"Access to camera is only possible with HTTPS" : "الوصول للكاميرا مسموح فقط في HTTPS",
"An error occurred while fetching the participants" : "حدث خطأ اثناء اضافة مشاركيين",
+ "Media" : "وسائط",
+ "Files" : "الملفات",
+ "Locations" : "المواقع",
+ "Audio" : "الصوت",
+ "Other" : "آخر",
"Nextcloud Talk was updated, please reload the page" : "تم تحديث نكست كلاود التحدث، يرجى تحديث الصفحة.",
"Do not disturb" : "عدم الازعاج",
"Away" : "بالخارج",
diff --git a/l10n/ar.json b/l10n/ar.json
index 7384dce97..318652b09 100644
--- a/l10n/ar.json
+++ b/l10n/ar.json
@@ -912,13 +912,8 @@
"Chat" : "الدردشة",
"Details" : "التفاصيل",
"Settings" : "الإعدادات",
- "Media" : "وسائط",
- "Files" : "الملفات",
- "Locations" : "المواقع",
- "Audio" : "الصوت",
- "Other" : "آخر",
- "Show all files" : "أظهر جميع الملفات",
"Projects" : "المشاريع",
+ "Show all files" : "أظهر جميع الملفات",
"Meeting ID: {meetingId}" : "معرف الاجتماع: {meetingId}",
"Your PIN: {attendeePin}" : "رقمك السري: {attendeePin}",
"Attachments folder" : "مجلد المرفقات",
@@ -982,6 +977,11 @@
"Error while accessing microphone" : "حدث خطأ اثناء الوصول إلى المايكرفون",
"Access to camera is only possible with HTTPS" : "الوصول للكاميرا مسموح فقط في HTTPS",
"An error occurred while fetching the participants" : "حدث خطأ اثناء اضافة مشاركيين",
+ "Media" : "وسائط",
+ "Files" : "الملفات",
+ "Locations" : "المواقع",
+ "Audio" : "الصوت",
+ "Other" : "آخر",
"Nextcloud Talk was updated, please reload the page" : "تم تحديث نكست كلاود التحدث، يرجى تحديث الصفحة.",
"Do not disturb" : "عدم الازعاج",
"Away" : "بالخارج",
diff --git a/l10n/ast.js b/l10n/ast.js
index b47f4f371..699c32109 100644
--- a/l10n/ast.js
+++ b/l10n/ast.js
@@ -86,12 +86,12 @@ OC.L10N.register(
"Add users or groups" : "Add users or groups",
"Details" : "Detalles",
"Settings" : "Settings",
- "Files" : "Ficheros",
- "Audio" : "Audiu",
- "Other" : "Otru",
"Keyboard shortcuts" : "Atayos de tecláu",
"Search" : "Search",
"Send" : "Unviar",
+ "Files" : "Ficheros",
+ "Audio" : "Audiu",
+ "Other" : "Otru",
"Default" : "Predetermináu",
"Access to microphone & camera is only possible with HTTPS" : "L'accesu a la cámara y micrófonu namái ye posible con HTTPS",
"Access to microphone & camera was denied" : "Ñegóse l'accesu a la cámara y micrófonu",
diff --git a/l10n/ast.json b/l10n/ast.json
index 71e0d219a..5fa906e5a 100644
--- a/l10n/ast.json
+++ b/l10n/ast.json
@@ -84,12 +84,12 @@
"Add users or groups" : "Add users or groups",
"Details" : "Detalles",
"Settings" : "Settings",
- "Files" : "Ficheros",
- "Audio" : "Audiu",
- "Other" : "Otru",
"Keyboard shortcuts" : "Atayos de tecláu",
"Search" : "Search",
"Send" : "Unviar",
+ "Files" : "Ficheros",
+ "Audio" : "Audiu",
+ "Other" : "Otru",
"Default" : "Predetermináu",
"Access to microphone & camera is only possible with HTTPS" : "L'accesu a la cámara y micrófonu namái ye posible con HTTPS",
"Access to microphone & camera was denied" : "Ñegóse l'accesu a la cámara y micrófonu",
diff --git a/l10n/bg.js b/l10n/bg.js
index b902c8ef6..8525aea75 100644
--- a/l10n/bg.js
+++ b/l10n/bg.js
@@ -47,6 +47,7 @@ OC.L10N.register(
"The command does not exist" : "Командата не съществува",
"An error occurred while running the command. Please ask an administrator to check the logs." : "Възникна грешка при изпълнение на командата. Моля, помолете администратор да провери регистрационните /журнали/ файлове.",
"Talk updates ✅" : "Актуализации на Talk ✅",
+ "Reaction deleted by author" : "Реакцията е изтрита от автор",
"{actor} created the conversation" : "{actor} създаде разговора",
"You created the conversation" : "Вие създадохте разговор",
"An administrator created the conversation" : "Администратор създаде разговора",
@@ -148,6 +149,8 @@ OC.L10N.register(
"You stopped Matterbridge" : "Спряхте Matterbridge",
"{actor} deleted a message" : "{actor} изтри съобщение",
"You deleted a message" : "Изтрихте съобщение",
+ "{actor} deleted a reaction" : "{actor} изтри реакция",
+ "You deleted a reaction" : "Изтрихте реакция",
"{actor} cleared the history of the conversation" : "{actor} изчисти историята на разговора",
"You cleared the history of the conversation" : "Изчистихте историята на разговора",
"Message deleted by author" : "Съобщението е изтрито от автора",
@@ -207,6 +210,11 @@ OC.L10N.register(
"A deleted user replied to your message in conversation {call}" : "Изтрит потребител отговори на съобщението ви в разговор {call}",
"{guest} (guest) replied to your message in conversation {call}" : "{guest} (гост) отговори на съобщението ви в разговор {call}",
"A guest replied to your message in conversation {call}" : "Гост отговори на съобщението ви в разговор {call}",
+ "{user} reacted with {reaction} to your private message" : "{user} реагира с {reaction} на вашето лично съобщение",
+ "{user} reacted with {reaction} to your message in conversation {call}" : "{user} реагира с {reaction} на вашето съобщение в разговор {call}",
+ "A deleted user reacted with {reaction} to your message in conversation {call}" : "Изтрит потребител реагира с {reaction} на вашето съобщение в разговор {call}",
+ "{guest} (guest) reacted with {reaction} to your message in conversation {call}" : "{guest} (гост) реагира с {reaction} на вашето съобщение в разговор {call}",
+ "A guest reacted with {reaction} to your message in conversation {call}" : "Гост реагира с {reaction} на вашето съобщение в разговор {call}",
"{user} mentioned you in a private conversation" : "{user} ви спомена в личен разговор",
"{user} mentioned you in conversation {call}" : "{user} ви спомена в разговор {call}",
"A deleted user mentioned you in conversation {call}" : "Изтрит потребител ви спомена в разговор {call}",
@@ -616,6 +624,8 @@ OC.L10N.register(
"OK: Running version: {version}" : "ОК: Работна версия: {version}",
"Error: Cannot connect to server" : "Грешка: Невъзможно свързване със сървъра",
"Error: Server did not respond with proper JSON" : "Грешка: Сървърът не отговори с правилен JSON",
+ "Could not get version" : "Не можа да се получи версията",
+ "Error: Running version: {version}; Server needs to be updated to be compatible with this version of Talk" : "Грешка: Използвана версия: {version}; Сървърът трябва да бъде актуализиран, за да бъде съвместим с тази версия на приложението Talk",
"Error: Server responded with: {error}" : "Грешка: Сървърът отговори с: {error}",
"Error: Unknown error occurred" : "Грешка: Възникна неизвестна грешка",
"High-performance backend" : "Високопроизводителен сървър",
@@ -648,6 +658,7 @@ OC.L10N.register(
"Checking …" : "Проверка ...",
"Failed: WebAssembly is disabled or not supported in this browser. Please enable WebAssembly or use a browser with support for it to do the check." : "Неуспешно: WebAssembly е деактивиран или не се поддържа в този браузър. Моля, активирайте WebAssembly или използвайте браузър с поддръжка за него, за да направите проверката.",
"Failed: \".wasm\" and \".tflite\" files were not properly returned by the web server. Please check \"System requirements\" section in Talk documentation." : "Неуспех: файловете „wasm“ и „.tflite“ не са върнати правилно от уеб сървъра. Моля, проверете раздела „Системни изисквания“ в документацията на Talk.",
+ "OK: \".wasm\" and \".tflite\" files were properly returned by the web server." : "OK: файловете „wasm“ и „.tflite“ са върнати правилно от уеб сървъра.",
"{nickName} raised their hand." : "{nickName} вдигна ръка.",
"A participant raised their hand." : "Един участник вдигна ръка.",
"Previous page of videos" : "Предишна страница с видеоклипове",
@@ -718,6 +729,7 @@ OC.L10N.register(
"An error occurred while starting screensharing." : "Възникна грешка при стартиране на споделянето на екрана.",
"Back" : "Назад",
"Access to camera was denied" : "Беше отказан достъп до камера",
+ "Error while accessing camera: It is likely in use by another program" : "Грешка при достъп до камера: възможно е тя се използва от друга програма",
"Error while accessing camera" : "Грешка при достъп до камера",
"You have been muted by a moderator" : "Бяхте заглушен от модератор",
"You" : "Ти",
@@ -1025,16 +1037,19 @@ OC.L10N.register(
"Chat" : "Съобщения",
"Details" : "Подробности",
"Settings" : "Настройки",
+ "Shared items" : "Споделени елементи",
"Participants ({count})" : "Участници ({count})",
- "Media" : "Медия",
- "Files" : "Файлове",
- "Locations" : "Местоположения",
- "Audio" : "Аудио",
- "Other" : "Други",
- "Show all files" : "Показване на всички файлове",
"Projects" : "Проекти",
+ "Show all media" : "Показване на всички медии",
+ "Show all files" : "Показване на всички файлове",
+ "Show all deck cards" : "Показване на всички deck карти",
+ "Show all voice messages" : "Показване на всички гласови съобщения",
+ "Show all locations" : "Показване на всички местоположения",
+ "Show all audio" : "Показване на всички аудиозаписи",
+ "Show all other" : "Показване на всичко останало",
"Meeting ID: {meetingId}" : "Идентификатор на срещата: {meetingId}",
"Your PIN: {attendeePin}" : "Вашият ПИН: {attendeePin}",
+ "Display name: <strong>{name}</strong>" : "Име за визуализация: <strong>{name}</strong>",
"Attachments folder" : "Папка с прикачени файлове",
"Privacy" : "Поверителност",
"Share my read-status and show the read-status of others" : "Споделяне на моето състояние на четене и показване на състоянието на четене и на другите",
@@ -1048,6 +1063,7 @@ OC.L10N.register(
"Fullscreen the chat or call" : "Чат на цял екран или обаждане",
"Search" : "Търсене",
"Shortcuts while in a call" : "Преки пътища по време на обаждане",
+ "Camera on and off" : "Включване и изключване на камера",
"Microphone on and off" : "Включване и изключване на микрофон",
"Space bar" : "Интервал",
"Push to talk or push to mute" : "Натиснете за разговор или натиснете за заглушаване",
@@ -1100,6 +1116,13 @@ OC.L10N.register(
"Error while accessing microphone" : "Грешка при достъп до микрофон",
"Access to camera is only possible with HTTPS" : "Достъп до камера е възможен само с HTTPS",
"An error occurred while fetching the participants" : "Възникна грешка при извличане на участниците",
+ "Media" : "Медия",
+ "Files" : "Файлове",
+ "Deck cards" : "Deck карти",
+ "Voice messages" : "Гласови съобщения",
+ "Locations" : "Местоположения",
+ "Audio" : "Аудио",
+ "Other" : "Други",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk беше актуализиран, моля, презаредете страницата",
"Do not disturb" : "Не безпокойте",
"Away" : "Отсъстващ",
@@ -1109,6 +1132,8 @@ OC.L10N.register(
"Error while uploading file \"{fileName}\"" : "Грешка при качването на файл „{fileName}“",
"An error happened when trying to share your file" : "Възникна грешка при опит за споделяне на вашия файл",
"Could not post message: {errorMessage}" : "Не можа да се публикува съобщение: {errorMessage}",
+ "Failed to add reaction" : "Неуспешно добавяне на реакция",
+ "Failed to remove reaction" : "Неуспешно премахване на реакция",
"Failed to join the conversation. Try to reload the page." : "Присъединяването към разговора не беше успешно. Опитайте да презаредите страницата.",
"You are trying to join a conversation while having an active session in another window or device. This is currently not supported by Nextcloud Talk. What do you want to do?" : "Опитвате се да се присъедините към разговор, докато имате активна сесия в друг прозорец или устройство. Това в момента не се поддържа от Nextcloud Talk. Какво искате да направите?",
"Join here" : "Присъединяване тук",
diff --git a/l10n/bg.json b/l10n/bg.json
index 7e3d9a213..e699db0a3 100644
--- a/l10n/bg.json
+++ b/l10n/bg.json
@@ -45,6 +45,7 @@
"The command does not exist" : "Командата не съществува",
"An error occurred while running the command. Please ask an administrator to check the logs." : "Възникна грешка при изпълнение на командата. Моля, помолете администратор да провери регистрационните /журнали/ файлове.",
"Talk updates ✅" : "Актуализации на Talk ✅",
+ "Reaction deleted by author" : "Реакцията е изтрита от автор",
"{actor} created the conversation" : "{actor} създаде разговора",
"You created the conversation" : "Вие създадохте разговор",
"An administrator created the conversation" : "Администратор създаде разговора",
@@ -146,6 +147,8 @@
"You stopped Matterbridge" : "Спряхте Matterbridge",
"{actor} deleted a message" : "{actor} изтри съобщение",
"You deleted a message" : "Изтрихте съобщение",
+ "{actor} deleted a reaction" : "{actor} изтри реакция",
+ "You deleted a reaction" : "Изтрихте реакция",
"{actor} cleared the history of the conversation" : "{actor} изчисти историята на разговора",
"You cleared the history of the conversation" : "Изчистихте историята на разговора",
"Message deleted by author" : "Съобщението е изтрито от автора",
@@ -205,6 +208,11 @@
"A deleted user replied to your message in conversation {call}" : "Изтрит потребител отговори на съобщението ви в разговор {call}",
"{guest} (guest) replied to your message in conversation {call}" : "{guest} (гост) отговори на съобщението ви в разговор {call}",
"A guest replied to your message in conversation {call}" : "Гост отговори на съобщението ви в разговор {call}",
+ "{user} reacted with {reaction} to your private message" : "{user} реагира с {reaction} на вашето лично съобщение",
+ "{user} reacted with {reaction} to your message in conversation {call}" : "{user} реагира с {reaction} на вашето съобщение в разговор {call}",
+ "A deleted user reacted with {reaction} to your message in conversation {call}" : "Изтрит потребител реагира с {reaction} на вашето съобщение в разговор {call}",
+ "{guest} (guest) reacted with {reaction} to your message in conversation {call}" : "{guest} (гост) реагира с {reaction} на вашето съобщение в разговор {call}",
+ "A guest reacted with {reaction} to your message in conversation {call}" : "Гост реагира с {reaction} на вашето съобщение в разговор {call}",
"{user} mentioned you in a private conversation" : "{user} ви спомена в личен разговор",
"{user} mentioned you in conversation {call}" : "{user} ви спомена в разговор {call}",
"A deleted user mentioned you in conversation {call}" : "Изтрит потребител ви спомена в разговор {call}",
@@ -614,6 +622,8 @@
"OK: Running version: {version}" : "ОК: Работна версия: {version}",
"Error: Cannot connect to server" : "Грешка: Невъзможно свързване със сървъра",
"Error: Server did not respond with proper JSON" : "Грешка: Сървърът не отговори с правилен JSON",
+ "Could not get version" : "Не можа да се получи версията",
+ "Error: Running version: {version}; Server needs to be updated to be compatible with this version of Talk" : "Грешка: Използвана версия: {version}; Сървърът трябва да бъде актуализиран, за да бъде съвместим с тази версия на приложението Talk",
"Error: Server responded with: {error}" : "Грешка: Сървърът отговори с: {error}",
"Error: Unknown error occurred" : "Грешка: Възникна неизвестна грешка",
"High-performance backend" : "Високопроизводителен сървър",
@@ -646,6 +656,7 @@
"Checking …" : "Проверка ...",
"Failed: WebAssembly is disabled or not supported in this browser. Please enable WebAssembly or use a browser with support for it to do the check." : "Неуспешно: WebAssembly е деактивиран или не се поддържа в този браузър. Моля, активирайте WebAssembly или използвайте браузър с поддръжка за него, за да направите проверката.",
"Failed: \".wasm\" and \".tflite\" files were not properly returned by the web server. Please check \"System requirements\" section in Talk documentation." : "Неуспех: файловете „wasm“ и „.tflite“ не са върнати правилно от уеб сървъра. Моля, проверете раздела „Системни изисквания“ в документацията на Talk.",
+ "OK: \".wasm\" and \".tflite\" files were properly returned by the web server." : "OK: файловете „wasm“ и „.tflite“ са върнати правилно от уеб сървъра.",
"{nickName} raised their hand." : "{nickName} вдигна ръка.",
"A participant raised their hand." : "Един участник вдигна ръка.",
"Previous page of videos" : "Предишна страница с видеоклипове",
@@ -716,6 +727,7 @@
"An error occurred while starting screensharing." : "Възникна грешка при стартиране на споделянето на екрана.",
"Back" : "Назад",
"Access to camera was denied" : "Беше отказан достъп до камера",
+ "Error while accessing camera: It is likely in use by another program" : "Грешка при достъп до камера: възможно е тя се използва от друга програма",
"Error while accessing camera" : "Грешка при достъп до камера",
"You have been muted by a moderator" : "Бяхте заглушен от модератор",
"You" : "Ти",
@@ -1023,16 +1035,19 @@
"Chat" : "Съобщения",
"Details" : "Подробности",
"Settings" : "Настройки",
+ "Shared items" : "Споделени елементи",
"Participants ({count})" : "Участници ({count})",
- "Media" : "Медия",
- "Files" : "Файлове",
- "Locations" : "Местоположения",
- "Audio" : "Аудио",
- "Other" : "Други",
- "Show all files" : "Показване на всички файлове",
"Projects" : "Проекти",
+ "Show all media" : "Показване на всички медии",
+ "Show all files" : "Показване на всички файлове",
+ "Show all deck cards" : "Показване на всички deck карти",
+ "Show all voice messages" : "Показване на всички гласови съобщения",
+ "Show all locations" : "Показване на всички местоположения",
+ "Show all audio" : "Показване на всички аудиозаписи",
+ "Show all other" : "Показване на всичко останало",
"Meeting ID: {meetingId}" : "Идентификатор на срещата: {meetingId}",
"Your PIN: {attendeePin}" : "Вашият ПИН: {attendeePin}",
+ "Display name: <strong>{name}</strong>" : "Име за визуализация: <strong>{name}</strong>",
"Attachments folder" : "Папка с прикачени файлове",
"Privacy" : "Поверителност",
"Share my read-status and show the read-status of others" : "Споделяне на моето състояние на четене и показване на състоянието на четене и на другите",
@@ -1046,6 +1061,7 @@
"Fullscreen the chat or call" : "Чат на цял екран или обаждане",
"Search" : "Търсене",
"Shortcuts while in a call" : "Преки пътища по време на обаждане",
+ "Camera on and off" : "Включване и изключване на камера",
"Microphone on and off" : "Включване и изключване на микрофон",
"Space bar" : "Интервал",
"Push to talk or push to mute" : "Натиснете за разговор или натиснете за заглушаване",
@@ -1098,6 +1114,13 @@
"Error while accessing microphone" : "Грешка при достъп до микрофон",
"Access to camera is only possible with HTTPS" : "Достъп до камера е възможен само с HTTPS",
"An error occurred while fetching the participants" : "Възникна грешка при извличане на участниците",
+ "Media" : "Медия",
+ "Files" : "Файлове",
+ "Deck cards" : "Deck карти",
+ "Voice messages" : "Гласови съобщения",
+ "Locations" : "Местоположения",
+ "Audio" : "Аудио",
+ "Other" : "Други",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk беше актуализиран, моля, презаредете страницата",
"Do not disturb" : "Не безпокойте",
"Away" : "Отсъстващ",
@@ -1107,6 +1130,8 @@
"Error while uploading file \"{fileName}\"" : "Грешка при качването на файл „{fileName}“",
"An error happened when trying to share your file" : "Възникна грешка при опит за споделяне на вашия файл",
"Could not post message: {errorMessage}" : "Не можа да се публикува съобщение: {errorMessage}",
+ "Failed to add reaction" : "Неуспешно добавяне на реакция",
+ "Failed to remove reaction" : "Неуспешно премахване на реакция",
"Failed to join the conversation. Try to reload the page." : "Присъединяването към разговора не беше успешно. Опитайте да презаредите страницата.",
"You are trying to join a conversation while having an active session in another window or device. This is currently not supported by Nextcloud Talk. What do you want to do?" : "Опитвате се да се присъедините към разговор, докато имате активна сесия в друг прозорец или устройство. Това в момента не се поддържа от Nextcloud Talk. Какво искате да направите?",
"Join here" : "Присъединяване тук",
diff --git a/l10n/br.js b/l10n/br.js
index fe1199c86..3ce25ecb3 100644
--- a/l10n/br.js
+++ b/l10n/br.js
@@ -675,10 +675,6 @@ OC.L10N.register(
"Chat" : "Chat",
"Details" : "Munudoù",
"Settings" : "Arventennoù",
- "Media" : "Media",
- "Files" : "Restroù",
- "Audio" : "Audio",
- "Other" : "All",
"Projects" : "Raktres",
"Attachments folder" : "Teuliad stag",
"Privacy" : "Prevezder",
@@ -712,6 +708,10 @@ OC.L10N.register(
"Error while accessing microphone" : "Ur fazi a zo bet en ur tizhout ar mikro",
"Access to camera is only possible with HTTPS" : "Tizhout ar c'hamera n'eo posupl nemet gant HTTPS",
"An error occurred while fetching the participants" : "Ur fazi a zo bet en ur pakañ an tud",
+ "Media" : "Media",
+ "Files" : "Restroù",
+ "Audio" : "Audio",
+ "Other" : "All",
"Nextcloud Talk was updated, please reload the page" : "Adnevesaet eo bet Nextcloud Talk , adkargit ar bajenn",
"Do not disturb" : "Na rannit ket",
"Away" : "Pell",
diff --git a/l10n/br.json b/l10n/br.json
index 32132750d..2a0098870 100644
--- a/l10n/br.json
+++ b/l10n/br.json
@@ -673,10 +673,6 @@
"Chat" : "Chat",
"Details" : "Munudoù",
"Settings" : "Arventennoù",
- "Media" : "Media",
- "Files" : "Restroù",
- "Audio" : "Audio",
- "Other" : "All",
"Projects" : "Raktres",
"Attachments folder" : "Teuliad stag",
"Privacy" : "Prevezder",
@@ -710,6 +706,10 @@
"Error while accessing microphone" : "Ur fazi a zo bet en ur tizhout ar mikro",
"Access to camera is only possible with HTTPS" : "Tizhout ar c'hamera n'eo posupl nemet gant HTTPS",
"An error occurred while fetching the participants" : "Ur fazi a zo bet en ur pakañ an tud",
+ "Media" : "Media",
+ "Files" : "Restroù",
+ "Audio" : "Audio",
+ "Other" : "All",
"Nextcloud Talk was updated, please reload the page" : "Adnevesaet eo bet Nextcloud Talk , adkargit ar bajenn",
"Do not disturb" : "Na rannit ket",
"Away" : "Pell",
diff --git a/l10n/ca.js b/l10n/ca.js
index 29930eba0..e2933ca21 100644
--- a/l10n/ca.js
+++ b/l10n/ca.js
@@ -747,13 +747,8 @@ OC.L10N.register(
"Chat" : "Xat",
"Details" : "Detalls",
"Settings" : "Paràmetres",
- "Media" : "Multimèdia",
- "Files" : "Fitxers",
- "Locations" : "Ubicacions",
- "Audio" : "Àudio",
- "Other" : "Altres",
- "Show all files" : "Mostra tots els fitxers",
"Projects" : "Projectes",
+ "Show all files" : "Mostra tots els fitxers",
"Attachments folder" : "Carpeta fitxers adjunts",
"Privacy" : "Privadesa",
"Keyboard shortcuts" : "Dreceres de teclat",
@@ -793,6 +788,11 @@ OC.L10N.register(
"Error while accessing microphone" : "S'ha produït un error en accedir al micròfon",
"Access to camera is only possible with HTTPS" : "L'accés a la càmera només és possible amb HTTPS",
"An error occurred while fetching the participants" : "S'ha produït un error en recuperar els participants",
+ "Media" : "Multimèdia",
+ "Files" : "Fitxers",
+ "Locations" : "Ubicacions",
+ "Audio" : "Àudio",
+ "Other" : "Altres",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk s'ha actualitzat, si us plau, torneu a carregar la pàgina",
"Do not disturb" : "No molesteu",
"Away" : "Absent",
diff --git a/l10n/ca.json b/l10n/ca.json
index eba18e467..3907b982c 100644
--- a/l10n/ca.json
+++ b/l10n/ca.json
@@ -745,13 +745,8 @@
"Chat" : "Xat",
"Details" : "Detalls",
"Settings" : "Paràmetres",
- "Media" : "Multimèdia",
- "Files" : "Fitxers",
- "Locations" : "Ubicacions",
- "Audio" : "Àudio",
- "Other" : "Altres",
- "Show all files" : "Mostra tots els fitxers",
"Projects" : "Projectes",
+ "Show all files" : "Mostra tots els fitxers",
"Attachments folder" : "Carpeta fitxers adjunts",
"Privacy" : "Privadesa",
"Keyboard shortcuts" : "Dreceres de teclat",
@@ -791,6 +786,11 @@
"Error while accessing microphone" : "S'ha produït un error en accedir al micròfon",
"Access to camera is only possible with HTTPS" : "L'accés a la càmera només és possible amb HTTPS",
"An error occurred while fetching the participants" : "S'ha produït un error en recuperar els participants",
+ "Media" : "Multimèdia",
+ "Files" : "Fitxers",
+ "Locations" : "Ubicacions",
+ "Audio" : "Àudio",
+ "Other" : "Altres",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk s'ha actualitzat, si us plau, torneu a carregar la pàgina",
"Do not disturb" : "No molesteu",
"Away" : "Absent",
diff --git a/l10n/cs.js b/l10n/cs.js
index fe08edcdb..e74999a0a 100644
--- a/l10n/cs.js
+++ b/l10n/cs.js
@@ -1039,13 +1039,7 @@ OC.L10N.register(
"Settings" : "Nastavení",
"Shared items" : "Sdílené položky",
"Participants ({count})" : "Účastníků ({count})",
- "Media" : "Média",
- "Files" : "Soubory",
- "Deck cards" : "Karty aplikace Deck",
- "Voice messages" : "Hlasové zprávy",
- "Locations" : "Polohy",
- "Audio" : "Zvuk",
- "Other" : "Ostatní",
+ "Projects" : "Projekty",
"Show all media" : "Zobrazit veškerá média",
"Show all files" : "Zobrazit všechny soubory",
"Show all deck cards" : "Zobrazit všechny karty aplikace Deck",
@@ -1053,7 +1047,6 @@ OC.L10N.register(
"Show all locations" : "Zobrazit všechny polohy",
"Show all audio" : "Zobrazit všechno audio",
"Show all other" : "Zobrazit všechno ostatní",
- "Projects" : "Projekty",
"Meeting ID: {meetingId}" : "Identif. schůzky: {meetingId}",
"Your PIN: {attendeePin}" : "Váš PIN: {attendeePin}",
"Display name: <strong>{name}</strong>" : "Zobrazované jméno: <strong>{name}</strong>",
@@ -1063,6 +1056,7 @@ OC.L10N.register(
"Sounds" : "Zvuky",
"Play sounds when participants join or leave a call" : "Přehrát zvuky oznámení když se účastníci připojí nebo odpojí",
"Sounds can currently not be played in Safari browser and iPad and iPhone devices due to technical restrictions by the manufacturer." : "Z důvodu stávajících technických omezení, zavedených jeho výrobcem, nemohou být v prohlížeči Safari na zařízeních iPad a iPhone přehrávány zvuky.",
+ "Sounds for chat and call notifications can be adjusted in the personal settings." : "Zvuky pro upozorňování na chat a volání je možné si přizpůsobit v osobních nastaveních.",
"Keyboard shortcuts" : "Klávesové zkratky",
"Speed up your Talk experience with these quick shortcuts." : "Pohybujte se po Talk rychleji pomocí těchto pohotových klávesových zkratek",
"Focus the chat input" : "Zaměřit okno psaní chatu",
@@ -1123,6 +1117,13 @@ OC.L10N.register(
"Error while accessing microphone" : "Chyba při přístupu k mikrofonu",
"Access to camera is only possible with HTTPS" : "Přístup ke kameře je možný pouze přes HTTPS",
"An error occurred while fetching the participants" : "Došlo k chybě při získávání seznamu účastníků",
+ "Media" : "Média",
+ "Files" : "Soubory",
+ "Deck cards" : "Karty aplikace Deck",
+ "Voice messages" : "Hlasové zprávy",
+ "Locations" : "Polohy",
+ "Audio" : "Zvuk",
+ "Other" : "Ostatní",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk bylo aktualizováno – načtěte stránku znovu",
"Do not disturb" : "Nerušit",
"Away" : "Pryč",
@@ -1131,6 +1132,7 @@ OC.L10N.register(
"Not enough free space to upload file \"{fileName}\"" : "Nedostatek místa pro nahrání souboru „{fileName}“",
"Error while uploading file \"{fileName}\"" : "Chyba při nahrávání „{fileName}“",
"An error happened when trying to share your file" : "Při pokusu o sdílení vašeho souboru došlo k chybě",
+ "{guest} (guest)" : "{guest} (host)",
"Could not post message: {errorMessage}" : "Zprávu se nedaří odeslat: {errorMessage}",
"Failed to add reaction" : "Nepodařilo se přidat reakci",
"Failed to remove reaction" : "Nepodařilo se odebrat reakci",
diff --git a/l10n/cs.json b/l10n/cs.json
index c4295284c..1a027f955 100644
--- a/l10n/cs.json
+++ b/l10n/cs.json
@@ -1037,13 +1037,7 @@
"Settings" : "Nastavení",
"Shared items" : "Sdílené položky",
"Participants ({count})" : "Účastníků ({count})",
- "Media" : "Média",
- "Files" : "Soubory",
- "Deck cards" : "Karty aplikace Deck",
- "Voice messages" : "Hlasové zprávy",
- "Locations" : "Polohy",
- "Audio" : "Zvuk",
- "Other" : "Ostatní",
+ "Projects" : "Projekty",
"Show all media" : "Zobrazit veškerá média",
"Show all files" : "Zobrazit všechny soubory",
"Show all deck cards" : "Zobrazit všechny karty aplikace Deck",
@@ -1051,7 +1045,6 @@
"Show all locations" : "Zobrazit všechny polohy",
"Show all audio" : "Zobrazit všechno audio",
"Show all other" : "Zobrazit všechno ostatní",
- "Projects" : "Projekty",
"Meeting ID: {meetingId}" : "Identif. schůzky: {meetingId}",
"Your PIN: {attendeePin}" : "Váš PIN: {attendeePin}",
"Display name: <strong>{name}</strong>" : "Zobrazované jméno: <strong>{name}</strong>",
@@ -1061,6 +1054,7 @@
"Sounds" : "Zvuky",
"Play sounds when participants join or leave a call" : "Přehrát zvuky oznámení když se účastníci připojí nebo odpojí",
"Sounds can currently not be played in Safari browser and iPad and iPhone devices due to technical restrictions by the manufacturer." : "Z důvodu stávajících technických omezení, zavedených jeho výrobcem, nemohou být v prohlížeči Safari na zařízeních iPad a iPhone přehrávány zvuky.",
+ "Sounds for chat and call notifications can be adjusted in the personal settings." : "Zvuky pro upozorňování na chat a volání je možné si přizpůsobit v osobních nastaveních.",
"Keyboard shortcuts" : "Klávesové zkratky",
"Speed up your Talk experience with these quick shortcuts." : "Pohybujte se po Talk rychleji pomocí těchto pohotových klávesových zkratek",
"Focus the chat input" : "Zaměřit okno psaní chatu",
@@ -1121,6 +1115,13 @@
"Error while accessing microphone" : "Chyba při přístupu k mikrofonu",
"Access to camera is only possible with HTTPS" : "Přístup ke kameře je možný pouze přes HTTPS",
"An error occurred while fetching the participants" : "Došlo k chybě při získávání seznamu účastníků",
+ "Media" : "Média",
+ "Files" : "Soubory",
+ "Deck cards" : "Karty aplikace Deck",
+ "Voice messages" : "Hlasové zprávy",
+ "Locations" : "Polohy",
+ "Audio" : "Zvuk",
+ "Other" : "Ostatní",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk bylo aktualizováno – načtěte stránku znovu",
"Do not disturb" : "Nerušit",
"Away" : "Pryč",
@@ -1129,6 +1130,7 @@
"Not enough free space to upload file \"{fileName}\"" : "Nedostatek místa pro nahrání souboru „{fileName}“",
"Error while uploading file \"{fileName}\"" : "Chyba při nahrávání „{fileName}“",
"An error happened when trying to share your file" : "Při pokusu o sdílení vašeho souboru došlo k chybě",
+ "{guest} (guest)" : "{guest} (host)",
"Could not post message: {errorMessage}" : "Zprávu se nedaří odeslat: {errorMessage}",
"Failed to add reaction" : "Nepodařilo se přidat reakci",
"Failed to remove reaction" : "Nepodařilo se odebrat reakci",
diff --git a/l10n/da.js b/l10n/da.js
index 5fb99ae2c..8c454bbc3 100644
--- a/l10n/da.js
+++ b/l10n/da.js
@@ -400,13 +400,8 @@ OC.L10N.register(
"Chat" : "Chat",
"Details" : "Detaljer",
"Settings" : "Indstillinger",
- "Media" : "Medier",
- "Files" : "Filer",
- "Locations" : "Sted",
- "Audio" : "Audio",
- "Other" : "Andet",
- "Show all files" : "Vis alle filer",
"Projects" : "Projekter",
+ "Show all files" : "Vis alle filer",
"Privacy" : "Privatliv",
"Keyboard shortcuts" : "Tastaturgenveje",
"Search" : "Søg",
@@ -425,6 +420,11 @@ OC.L10N.register(
"Link to a conversation" : "Link til samtale",
"Join a conversation or start a new one" : "Deltag i en samtale eller start en ny",
"An error occurred while fetching the participants" : "Kunne ikke hente listen over deltagere",
+ "Media" : "Medier",
+ "Files" : "Filer",
+ "Locations" : "Sted",
+ "Audio" : "Audio",
+ "Other" : "Andet",
"Do not disturb" : "Forstyr ikke",
"Away" : "Ikke tilstede",
"Error while sharing file" : "Fejl ved deling af fil",
diff --git a/l10n/da.json b/l10n/da.json
index 9f7b5895e..a42b914c0 100644
--- a/l10n/da.json
+++ b/l10n/da.json
@@ -398,13 +398,8 @@
"Chat" : "Chat",
"Details" : "Detaljer",
"Settings" : "Indstillinger",
- "Media" : "Medier",
- "Files" : "Filer",
- "Locations" : "Sted",
- "Audio" : "Audio",
- "Other" : "Andet",
- "Show all files" : "Vis alle filer",
"Projects" : "Projekter",
+ "Show all files" : "Vis alle filer",
"Privacy" : "Privatliv",
"Keyboard shortcuts" : "Tastaturgenveje",
"Search" : "Søg",
@@ -423,6 +418,11 @@
"Link to a conversation" : "Link til samtale",
"Join a conversation or start a new one" : "Deltag i en samtale eller start en ny",
"An error occurred while fetching the participants" : "Kunne ikke hente listen over deltagere",
+ "Media" : "Medier",
+ "Files" : "Filer",
+ "Locations" : "Sted",
+ "Audio" : "Audio",
+ "Other" : "Andet",
"Do not disturb" : "Forstyr ikke",
"Away" : "Ikke tilstede",
"Error while sharing file" : "Fejl ved deling af fil",
diff --git a/l10n/de.js b/l10n/de.js
index 6daef0074..8064a72e6 100644
--- a/l10n/de.js
+++ b/l10n/de.js
@@ -1039,13 +1039,7 @@ OC.L10N.register(
"Settings" : "Einstellungen",
"Shared items" : "Freigegebene Elemente",
"Participants ({count})" : "Teilnehmer ({count})",
- "Media" : "Medien",
- "Files" : "Dateien",
- "Deck cards" : "Deck-Karten",
- "Voice messages" : "Sprachnachrichten",
- "Locations" : "Orte",
- "Audio" : "Audio",
- "Other" : "Sonstiges",
+ "Projects" : "Projekte",
"Show all media" : "Alle Medien anzeigen",
"Show all files" : "Alle Dateien anzeigen",
"Show all deck cards" : "Alle Deck-Karten anzeigen",
@@ -1053,7 +1047,6 @@ OC.L10N.register(
"Show all locations" : "Alle Orte anzeigen",
"Show all audio" : "Alle Audiodateien anzeigen",
"Show all other" : "Alle sonstigen anzeigen",
- "Projects" : "Projekte",
"Meeting ID: {meetingId}" : "Meeting-ID: {meetingId}",
"Your PIN: {attendeePin}" : "Deine PIN: {attendeePin}",
"Display name: <strong>{name}</strong>" : "Anzeigename: <strong>{name}</strong>",
@@ -1123,6 +1116,13 @@ OC.L10N.register(
"Error while accessing microphone" : "Fehler beim Zugriff auf das Mikrofon",
"Access to camera is only possible with HTTPS" : "Zugriff auf Kamera ist nur über HTTPS möglich",
"An error occurred while fetching the participants" : "Es ist ein Fehler beim Abrufen der Teilnehmer aufgetreten",
+ "Media" : "Medien",
+ "Files" : "Dateien",
+ "Deck cards" : "Deck-Karten",
+ "Voice messages" : "Sprachnachrichten",
+ "Locations" : "Orte",
+ "Audio" : "Audio",
+ "Other" : "Sonstiges",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk wurde aktualisiert, bitte lade die Seite neu.",
"Do not disturb" : "Nicht stören",
"Away" : "Abwesend",
diff --git a/l10n/de.json b/l10n/de.json
index 747b960b2..1a6e5dc4a 100644
--- a/l10n/de.json
+++ b/l10n/de.json
@@ -1037,13 +1037,7 @@
"Settings" : "Einstellungen",
"Shared items" : "Freigegebene Elemente",
"Participants ({count})" : "Teilnehmer ({count})",
- "Media" : "Medien",
- "Files" : "Dateien",
- "Deck cards" : "Deck-Karten",
- "Voice messages" : "Sprachnachrichten",
- "Locations" : "Orte",
- "Audio" : "Audio",
- "Other" : "Sonstiges",
+ "Projects" : "Projekte",
"Show all media" : "Alle Medien anzeigen",
"Show all files" : "Alle Dateien anzeigen",
"Show all deck cards" : "Alle Deck-Karten anzeigen",
@@ -1051,7 +1045,6 @@
"Show all locations" : "Alle Orte anzeigen",
"Show all audio" : "Alle Audiodateien anzeigen",
"Show all other" : "Alle sonstigen anzeigen",
- "Projects" : "Projekte",
"Meeting ID: {meetingId}" : "Meeting-ID: {meetingId}",
"Your PIN: {attendeePin}" : "Deine PIN: {attendeePin}",
"Display name: <strong>{name}</strong>" : "Anzeigename: <strong>{name}</strong>",
@@ -1121,6 +1114,13 @@
"Error while accessing microphone" : "Fehler beim Zugriff auf das Mikrofon",
"Access to camera is only possible with HTTPS" : "Zugriff auf Kamera ist nur über HTTPS möglich",
"An error occurred while fetching the participants" : "Es ist ein Fehler beim Abrufen der Teilnehmer aufgetreten",
+ "Media" : "Medien",
+ "Files" : "Dateien",
+ "Deck cards" : "Deck-Karten",
+ "Voice messages" : "Sprachnachrichten",
+ "Locations" : "Orte",
+ "Audio" : "Audio",
+ "Other" : "Sonstiges",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk wurde aktualisiert, bitte lade die Seite neu.",
"Do not disturb" : "Nicht stören",
"Away" : "Abwesend",
diff --git a/l10n/de_DE.js b/l10n/de_DE.js
index b08106263..73657390a 100644
--- a/l10n/de_DE.js
+++ b/l10n/de_DE.js
@@ -1039,13 +1039,7 @@ OC.L10N.register(
"Settings" : "Einstellungen",
"Shared items" : "Geteilte Elemente",
"Participants ({count})" : "Teilnehmer ({count})",
- "Media" : "Medien",
- "Files" : "Dateien",
- "Deck cards" : "Deck-Karten",
- "Voice messages" : "Sprachnachrichten",
- "Locations" : "Orte",
- "Audio" : "Audio",
- "Other" : "Sonstiges",
+ "Projects" : "Projekte",
"Show all media" : "Alle Medien anzeigen",
"Show all files" : "Alle Dateien anzeigen",
"Show all deck cards" : "Alle Deck-Karten anzeigen",
@@ -1053,7 +1047,6 @@ OC.L10N.register(
"Show all locations" : "Alle Orte anzeigen",
"Show all audio" : "Alle Audios anzeigen",
"Show all other" : "Alle anderen anzeigen",
- "Projects" : "Projekte",
"Meeting ID: {meetingId}" : "Meeting-ID: {meetingId}",
"Your PIN: {attendeePin}" : "Ihre PIN: {attendeePin}",
"Display name: <strong>{name}</strong>" : "Anzeigename: <strong>{name}</strong>",
@@ -1063,6 +1056,7 @@ OC.L10N.register(
"Sounds" : "Töne",
"Play sounds when participants join or leave a call" : "Töne abspielen, wenn Teilnehmer einem Anruf beitreten oder verlassen",
"Sounds can currently not be played in Safari browser and iPad and iPhone devices due to technical restrictions by the manufacturer." : "Aufgrund technischer Einschränkungen des Herstellers können derzeit keine Töne in Safari-Browsern sowie iPad- und iPhone-Geräten abgespielt werden.",
+ "Sounds for chat and call notifications can be adjusted in the personal settings." : "Die Töne für Chat- und Anrufbenachrichtigungen können in den persönlichen Einstellungen angepasst werden.",
"Keyboard shortcuts" : "Tastaturkürzel",
"Speed up your Talk experience with these quick shortcuts." : "Beschleunigen Sie Ihr Gesprächserlebnis mit diesen Schnell-Verknüpfungen.",
"Focus the chat input" : "Chat-Eingabe in den Vordergrund",
@@ -1123,6 +1117,13 @@ OC.L10N.register(
"Error while accessing microphone" : "Fehler beim Zugriff auf das Mikrofon",
"Access to camera is only possible with HTTPS" : "Zugriff auf Kamera ist nur über HTTPS möglich",
"An error occurred while fetching the participants" : "Es ist ein Fehler beim Abrufen der Teilnehmer aufgetreten",
+ "Media" : "Medien",
+ "Files" : "Dateien",
+ "Deck cards" : "Deck-Karten",
+ "Voice messages" : "Sprachnachrichten",
+ "Locations" : "Orte",
+ "Audio" : "Audio",
+ "Other" : "Sonstiges",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk wurde aktualisiert, bitte laden Sie die Seite neu.",
"Do not disturb" : "Nicht stören",
"Away" : "Abwesend",
@@ -1131,6 +1132,7 @@ OC.L10N.register(
"Not enough free space to upload file \"{fileName}\"" : "Nicht genügend freier Speicherplatz zum Hochladen von \"{fileName}\"",
"Error while uploading file \"{fileName}\"" : "Fehler beim Hochladen der Datei \"{fileName}\"",
"An error happened when trying to share your file" : "Es ist ein Fehler beim Freigeben Ihrer Datei aufgetreten",
+ "{guest} (guest)" : "{guest} (Gast)",
"Could not post message: {errorMessage}" : "Nachricht konnte nicht gesendet werden: {errorMessage}",
"Failed to add reaction" : "Hinzufügen der Reaktion fehlgeschlagen",
"Failed to remove reaction" : "Entfernen der Reaktion fehlgeschlagen",
diff --git a/l10n/de_DE.json b/l10n/de_DE.json
index 0ec5f60ec..d0be3c3eb 100644
--- a/l10n/de_DE.json
+++ b/l10n/de_DE.json
@@ -1037,13 +1037,7 @@
"Settings" : "Einstellungen",
"Shared items" : "Geteilte Elemente",
"Participants ({count})" : "Teilnehmer ({count})",
- "Media" : "Medien",
- "Files" : "Dateien",
- "Deck cards" : "Deck-Karten",
- "Voice messages" : "Sprachnachrichten",
- "Locations" : "Orte",
- "Audio" : "Audio",
- "Other" : "Sonstiges",
+ "Projects" : "Projekte",
"Show all media" : "Alle Medien anzeigen",
"Show all files" : "Alle Dateien anzeigen",
"Show all deck cards" : "Alle Deck-Karten anzeigen",
@@ -1051,7 +1045,6 @@
"Show all locations" : "Alle Orte anzeigen",
"Show all audio" : "Alle Audios anzeigen",
"Show all other" : "Alle anderen anzeigen",
- "Projects" : "Projekte",
"Meeting ID: {meetingId}" : "Meeting-ID: {meetingId}",
"Your PIN: {attendeePin}" : "Ihre PIN: {attendeePin}",
"Display name: <strong>{name}</strong>" : "Anzeigename: <strong>{name}</strong>",
@@ -1061,6 +1054,7 @@
"Sounds" : "Töne",
"Play sounds when participants join or leave a call" : "Töne abspielen, wenn Teilnehmer einem Anruf beitreten oder verlassen",
"Sounds can currently not be played in Safari browser and iPad and iPhone devices due to technical restrictions by the manufacturer." : "Aufgrund technischer Einschränkungen des Herstellers können derzeit keine Töne in Safari-Browsern sowie iPad- und iPhone-Geräten abgespielt werden.",
+ "Sounds for chat and call notifications can be adjusted in the personal settings." : "Die Töne für Chat- und Anrufbenachrichtigungen können in den persönlichen Einstellungen angepasst werden.",
"Keyboard shortcuts" : "Tastaturkürzel",
"Speed up your Talk experience with these quick shortcuts." : "Beschleunigen Sie Ihr Gesprächserlebnis mit diesen Schnell-Verknüpfungen.",
"Focus the chat input" : "Chat-Eingabe in den Vordergrund",
@@ -1121,6 +1115,13 @@
"Error while accessing microphone" : "Fehler beim Zugriff auf das Mikrofon",
"Access to camera is only possible with HTTPS" : "Zugriff auf Kamera ist nur über HTTPS möglich",
"An error occurred while fetching the participants" : "Es ist ein Fehler beim Abrufen der Teilnehmer aufgetreten",
+ "Media" : "Medien",
+ "Files" : "Dateien",
+ "Deck cards" : "Deck-Karten",
+ "Voice messages" : "Sprachnachrichten",
+ "Locations" : "Orte",
+ "Audio" : "Audio",
+ "Other" : "Sonstiges",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk wurde aktualisiert, bitte laden Sie die Seite neu.",
"Do not disturb" : "Nicht stören",
"Away" : "Abwesend",
@@ -1129,6 +1130,7 @@
"Not enough free space to upload file \"{fileName}\"" : "Nicht genügend freier Speicherplatz zum Hochladen von \"{fileName}\"",
"Error while uploading file \"{fileName}\"" : "Fehler beim Hochladen der Datei \"{fileName}\"",
"An error happened when trying to share your file" : "Es ist ein Fehler beim Freigeben Ihrer Datei aufgetreten",
+ "{guest} (guest)" : "{guest} (Gast)",
"Could not post message: {errorMessage}" : "Nachricht konnte nicht gesendet werden: {errorMessage}",
"Failed to add reaction" : "Hinzufügen der Reaktion fehlgeschlagen",
"Failed to remove reaction" : "Entfernen der Reaktion fehlgeschlagen",
diff --git a/l10n/el.js b/l10n/el.js
index e51fd103b..7b2940e8e 100644
--- a/l10n/el.js
+++ b/l10n/el.js
@@ -873,13 +873,8 @@ OC.L10N.register(
"Chat" : "Συνομιλία",
"Details" : "Λεπτομέρειες",
"Settings" : "Ρυθμίσεις",
- "Media" : "Μέσα ενημέρωσης",
- "Files" : "Αρχεία",
- "Locations" : "Τοποθεσίες",
- "Audio" : "Ήχος",
- "Other" : "Άλλο",
- "Show all files" : "Εμφάνιση όλων των αρχείων",
"Projects" : "Projects",
+ "Show all files" : "Εμφάνιση όλων των αρχείων",
"Meeting ID: {meetingId}" : "Αναγνωριστικό συνάντησης: {meetingId}",
"Your PIN: {attendeePin}" : "Το PIN σας: {attendeePin}",
"Attachments folder" : "Φάκελος συνημμένων",
@@ -930,6 +925,11 @@ OC.L10N.register(
"Error while accessing microphone" : "Σφάλμα κατά την πρόσβαση στο μικρόφωνο.",
"Access to camera is only possible with HTTPS" : "Η πρόσβαση στην κάμερα είναι εφικτή μόνο μέσω HTTPS.",
"An error occurred while fetching the participants" : "Παρουσιάστηκε σφάλμα κατά την ανάκτηση των συμμετεχόντων",
+ "Media" : "Μέσα ενημέρωσης",
+ "Files" : "Αρχεία",
+ "Locations" : "Τοποθεσίες",
+ "Audio" : "Ήχος",
+ "Other" : "Άλλο",
"Nextcloud Talk was updated, please reload the page" : "Το Nextcloud Talk ενημερώθηκε, ανανεώστε τη σελίδα",
"Do not disturb" : "Μην ενοχλείτε",
"Away" : "Λείπω",
diff --git a/l10n/el.json b/l10n/el.json
index 2b768be9b..9cfe209e9 100644
--- a/l10n/el.json
+++ b/l10n/el.json
@@ -871,13 +871,8 @@
"Chat" : "Συνομιλία",
"Details" : "Λεπτομέρειες",
"Settings" : "Ρυθμίσεις",
- "Media" : "Μέσα ενημέρωσης",
- "Files" : "Αρχεία",
- "Locations" : "Τοποθεσίες",
- "Audio" : "Ήχος",
- "Other" : "Άλλο",
- "Show all files" : "Εμφάνιση όλων των αρχείων",
"Projects" : "Projects",
+ "Show all files" : "Εμφάνιση όλων των αρχείων",
"Meeting ID: {meetingId}" : "Αναγνωριστικό συνάντησης: {meetingId}",
"Your PIN: {attendeePin}" : "Το PIN σας: {attendeePin}",
"Attachments folder" : "Φάκελος συνημμένων",
@@ -928,6 +923,11 @@
"Error while accessing microphone" : "Σφάλμα κατά την πρόσβαση στο μικρόφωνο.",
"Access to camera is only possible with HTTPS" : "Η πρόσβαση στην κάμερα είναι εφικτή μόνο μέσω HTTPS.",
"An error occurred while fetching the participants" : "Παρουσιάστηκε σφάλμα κατά την ανάκτηση των συμμετεχόντων",
+ "Media" : "Μέσα ενημέρωσης",
+ "Files" : "Αρχεία",
+ "Locations" : "Τοποθεσίες",
+ "Audio" : "Ήχος",
+ "Other" : "Άλλο",
"Nextcloud Talk was updated, please reload the page" : "Το Nextcloud Talk ενημερώθηκε, ανανεώστε τη σελίδα",
"Do not disturb" : "Μην ενοχλείτε",
"Away" : "Λείπω",
diff --git a/l10n/en_GB.js b/l10n/en_GB.js
index 29d2b07e3..0acd59553 100644
--- a/l10n/en_GB.js
+++ b/l10n/en_GB.js
@@ -397,10 +397,6 @@ OC.L10N.register(
"Chat" : "Chat",
"Details" : "Details",
"Settings" : "Settings",
- "Media" : "Media",
- "Files" : "Files",
- "Audio" : "Audio",
- "Other" : "Other",
"Privacy" : "Privacy",
"Keyboard shortcuts" : "Keyboard shortcuts",
"Search" : "Search",
@@ -408,6 +404,10 @@ OC.L10N.register(
"Send" : "Send",
"Start a conversation" : "Start a conversation",
"Join a conversation or start a new one" : "Join a conversation or start a new one",
+ "Media" : "Media",
+ "Files" : "Files",
+ "Audio" : "Audio",
+ "Other" : "Other",
"Do not disturb" : "Do not disturb",
"Away" : "Away",
"Default" : "Default",
diff --git a/l10n/en_GB.json b/l10n/en_GB.json
index 4f044969a..406bbbd73 100644
--- a/l10n/en_GB.json
+++ b/l10n/en_GB.json
@@ -395,10 +395,6 @@
"Chat" : "Chat",
"Details" : "Details",
"Settings" : "Settings",
- "Media" : "Media",
- "Files" : "Files",
- "Audio" : "Audio",
- "Other" : "Other",
"Privacy" : "Privacy",
"Keyboard shortcuts" : "Keyboard shortcuts",
"Search" : "Search",
@@ -406,6 +402,10 @@
"Send" : "Send",
"Start a conversation" : "Start a conversation",
"Join a conversation or start a new one" : "Join a conversation or start a new one",
+ "Media" : "Media",
+ "Files" : "Files",
+ "Audio" : "Audio",
+ "Other" : "Other",
"Do not disturb" : "Do not disturb",
"Away" : "Away",
"Default" : "Default",
diff --git a/l10n/eo.js b/l10n/eo.js
index 5fdcb26aa..b6f5a15cf 100644
--- a/l10n/eo.js
+++ b/l10n/eo.js
@@ -305,13 +305,13 @@ OC.L10N.register(
"Chat" : "Babili",
"Details" : "Detaloj",
"Settings" : "Agordoj",
- "Files" : "Dosieroj",
- "Audio" : "Sonaĵo",
- "Other" : "Alia",
"Privacy" : "Privateco",
"Keyboard shortcuts" : "Fulmoklavoj",
"Search" : "Serĉi",
"Send" : "Sendi",
+ "Files" : "Dosieroj",
+ "Audio" : "Sonaĵo",
+ "Other" : "Alia",
"Default" : "Defaŭlta",
"The password is wrong. Try again." : "La pasvorto malĝustas. Provu denove.",
"Android app" : "Android-aplikaĵo",
diff --git a/l10n/eo.json b/l10n/eo.json
index cf1a397ac..8100ca7d7 100644
--- a/l10n/eo.json
+++ b/l10n/eo.json
@@ -303,13 +303,13 @@
"Chat" : "Babili",
"Details" : "Detaloj",
"Settings" : "Agordoj",
- "Files" : "Dosieroj",
- "Audio" : "Sonaĵo",
- "Other" : "Alia",
"Privacy" : "Privateco",
"Keyboard shortcuts" : "Fulmoklavoj",
"Search" : "Serĉi",
"Send" : "Sendi",
+ "Files" : "Dosieroj",
+ "Audio" : "Sonaĵo",
+ "Other" : "Alia",
"Default" : "Defaŭlta",
"The password is wrong. Try again." : "La pasvorto malĝustas. Provu denove.",
"Android app" : "Android-aplikaĵo",
diff --git a/l10n/es.js b/l10n/es.js
index 10d8e781c..86c89ac3b 100644
--- a/l10n/es.js
+++ b/l10n/es.js
@@ -1025,13 +1025,8 @@ OC.L10N.register(
"Details" : "Detalles",
"Settings" : "Ajustes",
"Participants ({count})" : "Participantes ({count})",
- "Media" : "Multimedia",
- "Files" : "Archivos",
- "Locations" : "Ubicaciones",
- "Audio" : "Audio",
- "Other" : "Otro",
- "Show all files" : "Mostrar todos los archivos",
"Projects" : "Proyectos",
+ "Show all files" : "Mostrar todos los archivos",
"Meeting ID: {meetingId}" : "ID de reunión: {meetingId}",
"Your PIN: {attendeePin}" : "Tu PIN: {attendeePin}",
"Attachments folder" : "Carpeta de adjuntos",
@@ -1099,6 +1094,11 @@ OC.L10N.register(
"Error while accessing microphone" : "Error al acceder al micrófono",
"Access to camera is only possible with HTTPS" : "El acceso a la cámara solo es posible con HTTPS",
"An error occurred while fetching the participants" : "Ha ocurrido un error al recuperar los participantes",
+ "Media" : "Multimedia",
+ "Files" : "Archivos",
+ "Locations" : "Ubicaciones",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk se ha actualizado. Por favor, vuelve a cargar la página",
"Do not disturb" : "No molestar",
"Away" : "Ausente",
diff --git a/l10n/es.json b/l10n/es.json
index 18cc5394a..9a903ef1d 100644
--- a/l10n/es.json
+++ b/l10n/es.json
@@ -1023,13 +1023,8 @@
"Details" : "Detalles",
"Settings" : "Ajustes",
"Participants ({count})" : "Participantes ({count})",
- "Media" : "Multimedia",
- "Files" : "Archivos",
- "Locations" : "Ubicaciones",
- "Audio" : "Audio",
- "Other" : "Otro",
- "Show all files" : "Mostrar todos los archivos",
"Projects" : "Proyectos",
+ "Show all files" : "Mostrar todos los archivos",
"Meeting ID: {meetingId}" : "ID de reunión: {meetingId}",
"Your PIN: {attendeePin}" : "Tu PIN: {attendeePin}",
"Attachments folder" : "Carpeta de adjuntos",
@@ -1097,6 +1092,11 @@
"Error while accessing microphone" : "Error al acceder al micrófono",
"Access to camera is only possible with HTTPS" : "El acceso a la cámara solo es posible con HTTPS",
"An error occurred while fetching the participants" : "Ha ocurrido un error al recuperar los participantes",
+ "Media" : "Multimedia",
+ "Files" : "Archivos",
+ "Locations" : "Ubicaciones",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk se ha actualizado. Por favor, vuelve a cargar la página",
"Do not disturb" : "No molestar",
"Away" : "Ausente",
diff --git a/l10n/es_419.js b/l10n/es_419.js
index dce0b5797..026fd2402 100644
--- a/l10n/es_419.js
+++ b/l10n/es_419.js
@@ -106,13 +106,13 @@ OC.L10N.register(
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Send" : "Enviar",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
"Access to microphone & camera was denied" : "El acceso al micrófono & cámara fue denegado",
diff --git a/l10n/es_419.json b/l10n/es_419.json
index cb0315efd..3a7248bcd 100644
--- a/l10n/es_419.json
+++ b/l10n/es_419.json
@@ -104,13 +104,13 @@
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Send" : "Enviar",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
"Access to microphone & camera was denied" : "El acceso al micrófono & cámara fue denegado",
diff --git a/l10n/es_AR.js b/l10n/es_AR.js
index ff0210357..dee8ea623 100644
--- a/l10n/es_AR.js
+++ b/l10n/es_AR.js
@@ -85,17 +85,17 @@ OC.L10N.register(
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Ajustes",
- "Media" : "Media",
- "Files" : "Archivos",
- "Locations" : "Ubicaciones",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos de teclado",
"Search" : "Buscar",
"Rename conversation" : "Renombrar conversación",
"Send" : "Enviar",
"Start a conversation" : "Iniciar una conversación",
+ "Media" : "Media",
+ "Files" : "Archivos",
+ "Locations" : "Ubicaciones",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Do not disturb" : "No molestar",
"Away" : "Lejos",
"Default" : "Predeterminado",
diff --git a/l10n/es_AR.json b/l10n/es_AR.json
index 3d7a746f1..5dc977172 100644
--- a/l10n/es_AR.json
+++ b/l10n/es_AR.json
@@ -83,17 +83,17 @@
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Ajustes",
- "Media" : "Media",
- "Files" : "Archivos",
- "Locations" : "Ubicaciones",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos de teclado",
"Search" : "Buscar",
"Rename conversation" : "Renombrar conversación",
"Send" : "Enviar",
"Start a conversation" : "Iniciar una conversación",
+ "Media" : "Media",
+ "Files" : "Archivos",
+ "Locations" : "Ubicaciones",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Do not disturb" : "No molestar",
"Away" : "Lejos",
"Default" : "Predeterminado",
diff --git a/l10n/es_CL.js b/l10n/es_CL.js
index 39a1d43d4..2095dd4ac 100644
--- a/l10n/es_CL.js
+++ b/l10n/es_CL.js
@@ -115,15 +115,15 @@ OC.L10N.register(
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Rename conversation" : "Renombrar conversación",
"Send" : "Enviar",
"Join a conversation or start a new one" : "Únete a la conversación o incia una nueva",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
"Please move your setup to HTTPS" : "Por favor cambia tu configuración a HTTPS",
diff --git a/l10n/es_CL.json b/l10n/es_CL.json
index 6f1a9a757..2b80bb32b 100644
--- a/l10n/es_CL.json
+++ b/l10n/es_CL.json
@@ -113,15 +113,15 @@
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Rename conversation" : "Renombrar conversación",
"Send" : "Enviar",
"Join a conversation or start a new one" : "Únete a la conversación o incia una nueva",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
"Please move your setup to HTTPS" : "Por favor cambia tu configuración a HTTPS",
diff --git a/l10n/es_CO.js b/l10n/es_CO.js
index 9fefde2d5..25311b8e2 100644
--- a/l10n/es_CO.js
+++ b/l10n/es_CO.js
@@ -118,15 +118,15 @@ OC.L10N.register(
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Rename conversation" : "Renombrar conversación",
"Send" : "Enviar",
"Join a conversation or start a new one" : "Únete a la conversación o incia una nueva",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Away" : "Ausente",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
diff --git a/l10n/es_CO.json b/l10n/es_CO.json
index 4cebebec2..ecf64bb16 100644
--- a/l10n/es_CO.json
+++ b/l10n/es_CO.json
@@ -116,15 +116,15 @@
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Rename conversation" : "Renombrar conversación",
"Send" : "Enviar",
"Join a conversation or start a new one" : "Únete a la conversación o incia una nueva",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Away" : "Ausente",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
diff --git a/l10n/es_CR.js b/l10n/es_CR.js
index 738af5e90..340c0baf5 100644
--- a/l10n/es_CR.js
+++ b/l10n/es_CR.js
@@ -115,15 +115,15 @@ OC.L10N.register(
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Rename conversation" : "Renombrar conversación",
"Send" : "Enviar",
"Join a conversation or start a new one" : "Únete a la conversación o incia una nueva",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
"Please move your setup to HTTPS" : "Por favor cambia tu configuración a HTTPS",
diff --git a/l10n/es_CR.json b/l10n/es_CR.json
index 55065f109..1598ad710 100644
--- a/l10n/es_CR.json
+++ b/l10n/es_CR.json
@@ -113,15 +113,15 @@
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Rename conversation" : "Renombrar conversación",
"Send" : "Enviar",
"Join a conversation or start a new one" : "Únete a la conversación o incia una nueva",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
"Please move your setup to HTTPS" : "Por favor cambia tu configuración a HTTPS",
diff --git a/l10n/es_DO.js b/l10n/es_DO.js
index ca6326fa4..9803ec4ae 100644
--- a/l10n/es_DO.js
+++ b/l10n/es_DO.js
@@ -115,15 +115,15 @@ OC.L10N.register(
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Rename conversation" : "Renombrar conversación",
"Send" : "Enviar",
"Join a conversation or start a new one" : "Únete a la conversación o incia una nueva",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Away" : "Lejos",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
diff --git a/l10n/es_DO.json b/l10n/es_DO.json
index fb9272e64..24a0248af 100644
--- a/l10n/es_DO.json
+++ b/l10n/es_DO.json
@@ -113,15 +113,15 @@
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Rename conversation" : "Renombrar conversación",
"Send" : "Enviar",
"Join a conversation or start a new one" : "Únete a la conversación o incia una nueva",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Away" : "Lejos",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
diff --git a/l10n/es_EC.js b/l10n/es_EC.js
index a86e5678d..3e194e73b 100644
--- a/l10n/es_EC.js
+++ b/l10n/es_EC.js
@@ -153,9 +153,6 @@ OC.L10N.register(
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
@@ -164,6 +161,9 @@ OC.L10N.register(
"Send" : "Enviar",
"Join a conversation or start a new one" : "Únete a la conversación o incia una nueva",
"Access to camera is only possible with HTTPS" : "El acceso a la cámara sólo es posible con HTTPS",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Camera {number}" : "Cámara {number}",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
diff --git a/l10n/es_EC.json b/l10n/es_EC.json
index 23c8e5620..9b62e1471 100644
--- a/l10n/es_EC.json
+++ b/l10n/es_EC.json
@@ -151,9 +151,6 @@
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
@@ -162,6 +159,9 @@
"Send" : "Enviar",
"Join a conversation or start a new one" : "Únete a la conversación o incia una nueva",
"Access to camera is only possible with HTTPS" : "El acceso a la cámara sólo es posible con HTTPS",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Camera {number}" : "Cámara {number}",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
diff --git a/l10n/es_GT.js b/l10n/es_GT.js
index fec1b50ae..17fb30510 100644
--- a/l10n/es_GT.js
+++ b/l10n/es_GT.js
@@ -115,15 +115,15 @@ OC.L10N.register(
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Rename conversation" : "Renombrar conversación",
"Send" : "Enviar",
"Join a conversation or start a new one" : "Únete a la conversación o incia una nueva",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
"Please move your setup to HTTPS" : "Por favor cambia tu configuración a HTTPS",
diff --git a/l10n/es_GT.json b/l10n/es_GT.json
index 32d04073a..57bff93ef 100644
--- a/l10n/es_GT.json
+++ b/l10n/es_GT.json
@@ -113,15 +113,15 @@
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Rename conversation" : "Renombrar conversación",
"Send" : "Enviar",
"Join a conversation or start a new one" : "Únete a la conversación o incia una nueva",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
"Please move your setup to HTTPS" : "Por favor cambia tu configuración a HTTPS",
diff --git a/l10n/es_HN.js b/l10n/es_HN.js
index d9d09ec85..af9397f61 100644
--- a/l10n/es_HN.js
+++ b/l10n/es_HN.js
@@ -106,13 +106,13 @@ OC.L10N.register(
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Send" : "Enviar",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
"Access to microphone & camera was denied" : "El acceso al micrófono & cámara fue denegado",
diff --git a/l10n/es_HN.json b/l10n/es_HN.json
index fcc406d5d..fb1e21659 100644
--- a/l10n/es_HN.json
+++ b/l10n/es_HN.json
@@ -104,13 +104,13 @@
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Send" : "Enviar",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
"Access to microphone & camera was denied" : "El acceso al micrófono & cámara fue denegado",
diff --git a/l10n/es_MX.js b/l10n/es_MX.js
index d7f293119..ab64b1f41 100644
--- a/l10n/es_MX.js
+++ b/l10n/es_MX.js
@@ -193,17 +193,17 @@ OC.L10N.register(
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Media" : "Multimedia",
- "Files" : "Archivos",
- "Locations" : "Ubicaciones",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Rename conversation" : "Renombrar conversación",
"Send" : "Enviar",
"Join a conversation or start a new one" : "Únete a la conversación o incia una nueva",
+ "Media" : "Multimedia",
+ "Files" : "Archivos",
+ "Locations" : "Ubicaciones",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Do not disturb" : "No molestar",
"Away" : "Ausente",
"Default" : "Predeterminado",
diff --git a/l10n/es_MX.json b/l10n/es_MX.json
index 1684814c1..b99d8c2f9 100644
--- a/l10n/es_MX.json
+++ b/l10n/es_MX.json
@@ -191,17 +191,17 @@
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Media" : "Multimedia",
- "Files" : "Archivos",
- "Locations" : "Ubicaciones",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Rename conversation" : "Renombrar conversación",
"Send" : "Enviar",
"Join a conversation or start a new one" : "Únete a la conversación o incia una nueva",
+ "Media" : "Multimedia",
+ "Files" : "Archivos",
+ "Locations" : "Ubicaciones",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Do not disturb" : "No molestar",
"Away" : "Ausente",
"Default" : "Predeterminado",
diff --git a/l10n/es_NI.js b/l10n/es_NI.js
index 6c1f5a638..ae1beec89 100644
--- a/l10n/es_NI.js
+++ b/l10n/es_NI.js
@@ -106,13 +106,13 @@ OC.L10N.register(
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Send" : "Enviar",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
"Access to microphone & camera was denied" : "El acceso al micrófono & cámara fue denegado",
diff --git a/l10n/es_NI.json b/l10n/es_NI.json
index 829d7f8bb..71825617f 100644
--- a/l10n/es_NI.json
+++ b/l10n/es_NI.json
@@ -104,13 +104,13 @@
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Send" : "Enviar",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
"Access to microphone & camera was denied" : "El acceso al micrófono & cámara fue denegado",
diff --git a/l10n/es_PA.js b/l10n/es_PA.js
index d9d09ec85..af9397f61 100644
--- a/l10n/es_PA.js
+++ b/l10n/es_PA.js
@@ -106,13 +106,13 @@ OC.L10N.register(
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Send" : "Enviar",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
"Access to microphone & camera was denied" : "El acceso al micrófono & cámara fue denegado",
diff --git a/l10n/es_PA.json b/l10n/es_PA.json
index fcc406d5d..fb1e21659 100644
--- a/l10n/es_PA.json
+++ b/l10n/es_PA.json
@@ -104,13 +104,13 @@
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Send" : "Enviar",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
"Access to microphone & camera was denied" : "El acceso al micrófono & cámara fue denegado",
diff --git a/l10n/es_PE.js b/l10n/es_PE.js
index 6c1f5a638..ae1beec89 100644
--- a/l10n/es_PE.js
+++ b/l10n/es_PE.js
@@ -106,13 +106,13 @@ OC.L10N.register(
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Send" : "Enviar",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
"Access to microphone & camera was denied" : "El acceso al micrófono & cámara fue denegado",
diff --git a/l10n/es_PE.json b/l10n/es_PE.json
index 829d7f8bb..71825617f 100644
--- a/l10n/es_PE.json
+++ b/l10n/es_PE.json
@@ -104,13 +104,13 @@
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Send" : "Enviar",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
"Access to microphone & camera was denied" : "El acceso al micrófono & cámara fue denegado",
diff --git a/l10n/es_PR.js b/l10n/es_PR.js
index 6c1f5a638..ae1beec89 100644
--- a/l10n/es_PR.js
+++ b/l10n/es_PR.js
@@ -106,13 +106,13 @@ OC.L10N.register(
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Send" : "Enviar",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
"Access to microphone & camera was denied" : "El acceso al micrófono & cámara fue denegado",
diff --git a/l10n/es_PR.json b/l10n/es_PR.json
index 829d7f8bb..71825617f 100644
--- a/l10n/es_PR.json
+++ b/l10n/es_PR.json
@@ -104,13 +104,13 @@
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Send" : "Enviar",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
"Access to microphone & camera was denied" : "El acceso al micrófono & cámara fue denegado",
diff --git a/l10n/es_PY.js b/l10n/es_PY.js
index b7c7a8423..073f6bf01 100644
--- a/l10n/es_PY.js
+++ b/l10n/es_PY.js
@@ -106,14 +106,14 @@ OC.L10N.register(
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Locations" : "Ubicaciones",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Send" : "Enviar",
+ "Files" : "Archivos",
+ "Locations" : "Ubicaciones",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
"Access to microphone & camera was denied" : "El acceso al micrófono & cámara fue denegado",
diff --git a/l10n/es_PY.json b/l10n/es_PY.json
index 03f505ee0..d3ad32ebe 100644
--- a/l10n/es_PY.json
+++ b/l10n/es_PY.json
@@ -104,14 +104,14 @@
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Locations" : "Ubicaciones",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Send" : "Enviar",
+ "Files" : "Archivos",
+ "Locations" : "Ubicaciones",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
"Access to microphone & camera was denied" : "El acceso al micrófono & cámara fue denegado",
diff --git a/l10n/es_SV.js b/l10n/es_SV.js
index 738af5e90..340c0baf5 100644
--- a/l10n/es_SV.js
+++ b/l10n/es_SV.js
@@ -115,15 +115,15 @@ OC.L10N.register(
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Rename conversation" : "Renombrar conversación",
"Send" : "Enviar",
"Join a conversation or start a new one" : "Únete a la conversación o incia una nueva",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
"Please move your setup to HTTPS" : "Por favor cambia tu configuración a HTTPS",
diff --git a/l10n/es_SV.json b/l10n/es_SV.json
index 55065f109..1598ad710 100644
--- a/l10n/es_SV.json
+++ b/l10n/es_SV.json
@@ -113,15 +113,15 @@
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Rename conversation" : "Renombrar conversación",
"Send" : "Enviar",
"Join a conversation or start a new one" : "Únete a la conversación o incia una nueva",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
"Please move your setup to HTTPS" : "Por favor cambia tu configuración a HTTPS",
diff --git a/l10n/es_UY.js b/l10n/es_UY.js
index 520099942..672837a07 100644
--- a/l10n/es_UY.js
+++ b/l10n/es_UY.js
@@ -106,13 +106,13 @@ OC.L10N.register(
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Send" : "Enviar",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Away" : "Ausente",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
diff --git a/l10n/es_UY.json b/l10n/es_UY.json
index f62a43b3c..807fd3385 100644
--- a/l10n/es_UY.json
+++ b/l10n/es_UY.json
@@ -104,13 +104,13 @@
"Chat" : "Chat",
"Details" : "Detalles",
"Settings" : "Configuraciones ",
- "Files" : "Archivos",
- "Audio" : "Audio",
- "Other" : "Otro",
"Privacy" : "Privacidad",
"Keyboard shortcuts" : "Atajos del teclado",
"Search" : "Buscar",
"Send" : "Enviar",
+ "Files" : "Archivos",
+ "Audio" : "Audio",
+ "Other" : "Otro",
"Away" : "Ausente",
"Default" : "Predeterminado",
"Access to microphone & camera is only possible with HTTPS" : "El acceso al micrófono & cámara sólo es posible mediante HTTPS",
diff --git a/l10n/et_EE.js b/l10n/et_EE.js
index bc4e8893b..2941a4ce4 100644
--- a/l10n/et_EE.js
+++ b/l10n/et_EE.js
@@ -66,14 +66,14 @@ OC.L10N.register(
"Add users or groups" : "Add users or groups",
"Details" : "Üksikasjad",
"Settings" : "Seaded",
- "Files" : "Failid",
- "Locations" : "Asukohad",
- "Audio" : "Helid",
- "Other" : "Muu",
"Privacy" : "Privaatsus",
"Keyboard shortcuts" : "Klaviatuuri otseteed",
"Search" : "Otsi",
"Send" : "Saada",
+ "Files" : "Failid",
+ "Locations" : "Asukohad",
+ "Audio" : "Helid",
+ "Other" : "Muu",
"Default" : "Vaikeväärtus",
"The password is wrong. Try again." : "Parool on vale. Proovi uuesti.",
"Android app" : "Androidi rakendus",
diff --git a/l10n/et_EE.json b/l10n/et_EE.json
index c6616adfc..450f21583 100644
--- a/l10n/et_EE.json
+++ b/l10n/et_EE.json
@@ -64,14 +64,14 @@
"Add users or groups" : "Add users or groups",
"Details" : "Üksikasjad",
"Settings" : "Seaded",
- "Files" : "Failid",
- "Locations" : "Asukohad",
- "Audio" : "Helid",
- "Other" : "Muu",
"Privacy" : "Privaatsus",
"Keyboard shortcuts" : "Klaviatuuri otseteed",
"Search" : "Otsi",
"Send" : "Saada",
+ "Files" : "Failid",
+ "Locations" : "Asukohad",
+ "Audio" : "Helid",
+ "Other" : "Muu",
"Default" : "Vaikeväärtus",
"The password is wrong. Try again." : "Parool on vale. Proovi uuesti.",
"Android app" : "Androidi rakendus",
diff --git a/l10n/eu.js b/l10n/eu.js
index a2656b32d..a255d2bac 100644
--- a/l10n/eu.js
+++ b/l10n/eu.js
@@ -1021,13 +1021,8 @@ OC.L10N.register(
"Details" : "Xehetasunak",
"Settings" : "Ezarpenak",
"Participants ({count})" : "Parte-hartzaileak ({count})",
- "Media" : "Media",
- "Files" : "Fitxategiak",
- "Locations" : "Kokapenak",
- "Audio" : "Audioa",
- "Other" : "Bestelakoa",
- "Show all files" : "Erakustsi fitxategi guztiak",
"Projects" : "Proiektuak",
+ "Show all files" : "Erakustsi fitxategi guztiak",
"Meeting ID: {meetingId}" : "Bileraren ID: {meetingId}",
"Your PIN: {attendeePin}" : "Zure PINa: {attendeePin}",
"Attachments folder" : "Eranskinen karpeta",
@@ -1096,6 +1091,11 @@ OC.L10N.register(
"Error while accessing microphone" : "Errorea kamera atzitzean",
"Access to camera is only possible with HTTPS" : "Kamera atzitzeko modu bakarra HTTPS bidez da",
"An error occurred while fetching the participants" : "Errore bat gertatu da parte-hartzaileak eskuratzean",
+ "Media" : "Media",
+ "Files" : "Fitxategiak",
+ "Locations" : "Kokapenak",
+ "Audio" : "Audioa",
+ "Other" : "Bestelakoa",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Elkarrizketak eguneratu dira, mesedez freskatu orria",
"Do not disturb" : "Ez molestatu",
"Away" : "Kanpoan",
diff --git a/l10n/eu.json b/l10n/eu.json
index cd5af0da2..92163ac10 100644
--- a/l10n/eu.json
+++ b/l10n/eu.json
@@ -1019,13 +1019,8 @@
"Details" : "Xehetasunak",
"Settings" : "Ezarpenak",
"Participants ({count})" : "Parte-hartzaileak ({count})",
- "Media" : "Media",
- "Files" : "Fitxategiak",
- "Locations" : "Kokapenak",
- "Audio" : "Audioa",
- "Other" : "Bestelakoa",
- "Show all files" : "Erakustsi fitxategi guztiak",
"Projects" : "Proiektuak",
+ "Show all files" : "Erakustsi fitxategi guztiak",
"Meeting ID: {meetingId}" : "Bileraren ID: {meetingId}",
"Your PIN: {attendeePin}" : "Zure PINa: {attendeePin}",
"Attachments folder" : "Eranskinen karpeta",
@@ -1094,6 +1089,11 @@
"Error while accessing microphone" : "Errorea kamera atzitzean",
"Access to camera is only possible with HTTPS" : "Kamera atzitzeko modu bakarra HTTPS bidez da",
"An error occurred while fetching the participants" : "Errore bat gertatu da parte-hartzaileak eskuratzean",
+ "Media" : "Media",
+ "Files" : "Fitxategiak",
+ "Locations" : "Kokapenak",
+ "Audio" : "Audioa",
+ "Other" : "Bestelakoa",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Elkarrizketak eguneratu dira, mesedez freskatu orria",
"Do not disturb" : "Ez molestatu",
"Away" : "Kanpoan",
diff --git a/l10n/fa.js b/l10n/fa.js
index 2eeae410b..a7b1b7598 100644
--- a/l10n/fa.js
+++ b/l10n/fa.js
@@ -329,13 +329,8 @@ OC.L10N.register(
"Participants" : "شركت كنندگان",
"Details" : "جزئیات",
"Settings" : "تنظیمات",
- "Media" : "رسانه‌ها",
- "Files" : "فایل ها",
- "Locations" : "مکانها",
- "Audio" : "صدا",
- "Other" : "اعلان‌ها",
- "Show all files" : "نمایش همه پرونده ها",
"Projects" : "پروژه ها",
+ "Show all files" : "نمایش همه پرونده ها",
"Privacy" : "حریم خصوصی",
"Keyboard shortcuts" : "میان بر صفحه ی کلید",
"Search" : "جستجو کردن",
@@ -345,6 +340,11 @@ OC.L10N.register(
"Start a conversation" : "مکالمه را شروع کنید",
"Join a conversation or start a new one!" : "عضو یک گفتگو شوید یا یک گفتگو جدید شروع کنید!",
"Join a conversation or start a new one" : "عضو یک گفتگو شوید یا یک گفتگو جدید شروع کنید",
+ "Media" : "رسانه‌ها",
+ "Files" : "فایل ها",
+ "Locations" : "مکانها",
+ "Audio" : "صدا",
+ "Other" : "اعلان‌ها",
"Do not disturb" : "مزاحم نشوید",
"Away" : "دور",
"Default" : "پیش‌گزیده",
diff --git a/l10n/fa.json b/l10n/fa.json
index e3b1b035d..5d9eb6cde 100644
--- a/l10n/fa.json
+++ b/l10n/fa.json
@@ -327,13 +327,8 @@
"Participants" : "شركت كنندگان",
"Details" : "جزئیات",
"Settings" : "تنظیمات",
- "Media" : "رسانه‌ها",
- "Files" : "فایل ها",
- "Locations" : "مکانها",
- "Audio" : "صدا",
- "Other" : "اعلان‌ها",
- "Show all files" : "نمایش همه پرونده ها",
"Projects" : "پروژه ها",
+ "Show all files" : "نمایش همه پرونده ها",
"Privacy" : "حریم خصوصی",
"Keyboard shortcuts" : "میان بر صفحه ی کلید",
"Search" : "جستجو کردن",
@@ -343,6 +338,11 @@
"Start a conversation" : "مکالمه را شروع کنید",
"Join a conversation or start a new one!" : "عضو یک گفتگو شوید یا یک گفتگو جدید شروع کنید!",
"Join a conversation or start a new one" : "عضو یک گفتگو شوید یا یک گفتگو جدید شروع کنید",
+ "Media" : "رسانه‌ها",
+ "Files" : "فایل ها",
+ "Locations" : "مکانها",
+ "Audio" : "صدا",
+ "Other" : "اعلان‌ها",
"Do not disturb" : "مزاحم نشوید",
"Away" : "دور",
"Default" : "پیش‌گزیده",
diff --git a/l10n/fi.js b/l10n/fi.js
index da8eb27a1..88a834b70 100644
--- a/l10n/fi.js
+++ b/l10n/fi.js
@@ -16,6 +16,7 @@ OC.L10N.register(
"Talk" : "Puhe",
"Guest" : "Vieras",
"- Raise your hand in a call with the R key" : "- Nosta kätesi puhelussa R-näppäimellä",
+ "There are currently no commands available." : "Tällä hetkellä ei komentoja käytettävissä.",
"The command does not exist" : "Komentoa ei ole olemassa",
"Talk updates ✅" : "Talk-päivitykset ✅",
"{actor} created the conversation" : "{actor} loi keskustelun",
@@ -23,6 +24,7 @@ OC.L10N.register(
"An administrator created the conversation" : "Ylläpitäjä loi keskustelun",
"{actor} renamed the conversation from \"%1$s\" to \"%2$s\"" : "{actor} muutti keskustelun \"%1$s\" uudeksi nimeksi \"%2$s\"",
"You renamed the conversation from \"%1$s\" to \"%2$s\"" : "Sinä muutit keskustelun \"%1$s\" uudeksi nimeksi \"%2$s\"",
+ "An administrator renamed the conversation from \"%1$s\" to \"%2$s\"" : "Ylläpitäjä muutti keskustelun \"%1$s\" uudeksi nimeksi \"%2$s\"",
"{actor} set the description" : "{actor} asetti kuvauksen",
"You set the description" : "Sinä asetit kuvauksen",
"An administrator set the description" : "Ylläpitäjä asetti kuvauksen",
@@ -49,6 +51,8 @@ OC.L10N.register(
"The conversation is now open to everyone" : "Keskustelu on nyt avoin kaikille",
"{actor} opened the conversation to everyone" : "{actor} avasi keskustelun kaikille",
"You opened the conversation to everyone" : "Avasit uuden keskustelun kaikille",
+ "{actor} restricted the conversation to moderators" : "{actor} rajoitti keskustelun moderaattoreille",
+ "You restricted the conversation to moderators" : "Rajoitit keskustelun moderaattoreille",
"{actor} allowed guests" : "{actor} salli vieraat",
"You allowed guests" : "Sinä sallit vieraat",
"An administrator allowed guests" : "Ylläpitäjä salli vieraat",
@@ -95,9 +99,12 @@ OC.L10N.register(
"{actor} demoted you from moderator" : "{actor} poisti sinulta moderaattorin oikeudet",
"{actor} shared a file which is no longer available" : "{actor} jakoi kanssasi tiedoston, joka ei ole enää saatavilla",
"You shared a file which is no longer available" : "Jaoit tiedoston, joka ei ole enää saatavilla",
+ "{actor} updated the Matterbridge configuration" : "{actor} päivitti Matterbridge-kokoonpanon",
+ "You updated the Matterbridge configuration" : "Päivitit Matterbridge-kokoonpanon",
"{actor} started Matterbridge" : "{actor} käynnisti Matterbridgen",
"You started Matterbridge" : "Käynnistit Matterbridgen",
"{actor} stopped Matterbridge" : "{actor} pysäytti Matterbridgen",
+ "You stopped Matterbridge" : "Pysäytit Matterbridgen",
"{actor} deleted a message" : "{actor} poisti viestin",
"You deleted a message" : "Sinä poistit viestin",
"{actor} deleted a reaction" : "{actor} poisti reaktion",
@@ -118,6 +125,7 @@ OC.L10N.register(
"Invalid image" : "Virheellinen kuva",
"Unknown filetype" : "Tuntematon tiedostotyyppi",
"An error occurred. Please contact your admin." : "Tapahtui virhe. Ole yhteydessä ylläpitäjään.",
+ "Talk mentions" : "Talk-maininnat",
"Write to conversation" : "Kirjoita keskusteluun",
"%s invited you to a conversation." : "%s kutsui sinut keskusteluun.",
"You were invited to a conversation." : "Sinut kutsuttiin keskusteluun.",
@@ -142,6 +150,7 @@ OC.L10N.register(
"{user} replied to your private message" : "{user} vastasi yksityisviestiisi",
"{user} replied to your message in conversation {call}" : "{user} vastasi viestiisi keskustelussa {call}",
"A guest replied to your message in conversation {call}" : "Vieras vastasi viestiisi keskustelussa {call}",
+ "{user} reacted with {reaction} to your private message" : "{user} vastasi reaktiolla {reaction} yksityisviestiisi",
"{user} mentioned you in a private conversation" : "{user} mainitsi sinut yksityiskeskustelussa",
"{user} mentioned you in conversation {call}" : "{user} mainitsi sinut keskustelussa {call}",
"A deleted user mentioned you in conversation {call}" : "Poistettu käyttäjä mainitsi sinut keskustelussa {call}",
@@ -155,7 +164,9 @@ OC.L10N.register(
"{user} would like to talk with you" : "{user} haluaa puhua kanssasi",
"Call back" : "Soita takaisin",
"A group call has started in {call}" : "Ryhmäpuhelu on alkanut puhelussa {call}",
+ "{email} is requesting the password to access {file}" : "{email} pyytää salasanaa saadakseen pääsyn tiedostoon {file}",
"Open settings" : "Avaa asetukset",
+ "Open Talk" : "Avaa Talk",
"Conversations" : "Keskustelut",
"Messages" : "Viestit",
"{user}" : "{user}",
@@ -169,6 +180,7 @@ OC.L10N.register(
"The language is invalid." : "Kieli on virheellinen.",
"The country is invalid." : "Maa on virheellinen.",
"Something unexpected happened. Please try again later." : "Jotain odottamatonta tapahtui. Yritä myöhemmin uudelleen.",
+ "There is no such account registered." : "Kyseistä tiliä ei ole rekisteröity.",
"Andorra" : "Andorra",
"United Arab Emirates" : "Yhdistyneet arabiemiirikunnat",
"Afghanistan" : "Afganistan",
@@ -445,9 +457,12 @@ OC.L10N.register(
"Matterbridge integration" : "Matterbridge-integraatio",
"Enable Matterbridge integration" : "Käytä Matterbridge-integraatiota",
"Downloading …" : "Ladataan…",
+ "Install Talk Matterbridge" : "Asenna Talk Matterbridge",
"Installed version: {version}" : "Asennettu versio: {version}",
"Matterbridge binary was not found or couldn't be executed." : "Matterbridge-binääriä ei löytynyt tai sitä ei voitu suorittaa.",
+ "An error occurred while installing the Matterbridge app." : "Virhe Matterbridge-sovellusta asentaessa.",
"Failed to execute Matterbridge binary." : "Matterbridge-binäärin suorittaminen epäonnistui.",
+ "SIP configuration" : "SIP-määritys",
"Shared secret" : "Jaettu salaisuus",
"Validate SSL certificate" : "Vahvista SSL-varmenne",
"Delete this server" : "Poista tämä palvelin",
@@ -455,6 +470,7 @@ OC.L10N.register(
"OK: Running version: {version}" : "OK: Suoritetaan versiota: {version}",
"Error: Cannot connect to server" : "Virhe: Ei voi yhdistää palvelimeen",
"Could not get version" : "Versiota ei saatu",
+ "Error: Server responded with: {error}" : "Virhe: palvelin vastasi: {error}",
"Error: Unknown error occurred" : "Virhe: Tuntematon virhe",
"Saved" : "Tallennettu",
"Add a new server" : "Lisää uusi palvelin",
@@ -517,6 +533,7 @@ OC.L10N.register(
"Back" : "Takaisin",
"Access to camera was denied" : "Pääsy kameraan estettiin",
"Error while accessing camera: It is likely in use by another program" : "Virhe yrittäessä käyttää kameraa: se on luultavasti toisen ohjelman käytössä",
+ "Error while accessing camera" : "Virhe käyttäessä kameraa",
"You have been muted by a moderator" : "Moderaattori on mykistänyt sinut",
"You" : "Sinä",
"Connection could not be established …" : "Yhteyttä ei voitu muodostaa…",
@@ -547,6 +564,8 @@ OC.L10N.register(
"Guests access" : "Vieraiden pääsy",
"Participants permissions" : "Osallistujan oikeudet",
"Meeting settings" : "Kokouksen asetukset",
+ "Matterbridge" : "Matterbridge",
+ "Error while updating conversation description" : "Virhe keskustelun kuvausta päivittäessä",
"Leave conversation" : "Poistu keskustelusta",
"Delete conversation" : "Poista keskustelu",
"Permanently delete this conversation." : "Poista tämä keskustelu pysyvästi.",
@@ -585,6 +604,7 @@ OC.L10N.register(
"Nextcloud URL" : "Nextcloudin URL-osoite",
"Nextcloud user" : "Nextcloud-käyttäjä",
"User password" : "Käyttäjän salasana",
+ "Talk conversation" : "Talk-keskustelu",
"Matrix server URL" : "Matrix-palvelimen URL-osoite",
"Matrix channel" : "Matrix-kanava",
"Mattermost server URL" : "Mattermost-palvelimen URL-osoite",
@@ -597,6 +617,8 @@ OC.L10N.register(
"Rocket.Chat channel" : "Rocket.Chat-kanava",
"Skip TLS verification" : "Ohita TLS-vahvistus",
"Zulip server URL" : "Zulip-palvelimen URL-osoite",
+ "Bot user name" : "Botin käyttäjätunnus",
+ "Bot API key" : "Botin rajapinta-avain",
"API token" : "API-poletti",
"Slack channel" : "Slack-kanava",
"Channel" : "Kanava",
@@ -635,6 +657,7 @@ OC.L10N.register(
"Groups" : "Ryhmät",
"Circles" : "Piirit",
"Unread mentions" : "Lukemattomat maininnat",
+ "Talk settings" : "Talk-asetukset",
"Users, groups and circles" : "Käyttäjät, ryhmät ja piirit",
"Users and groups" : "Käyttäjät ja ryhmät",
"Users and circles" : "Käyttäjät ja piirit",
@@ -687,6 +710,8 @@ OC.L10N.register(
"Yesterday" : "Eilen",
"Record voice message" : "Äänitä ääniviesti",
"End recording and send" : "Lopeta äänitys ja lähetä",
+ "Access to the microphone was denied" : "Pääsy mikrofoniin estettiin",
+ "Microphone either not available or disabled in settings" : "Mikrofoni ei ole käytettävissä tai se on poistettu käytöstä asetuksista",
"Error while recording audio" : "Virhe äänittäessä",
"Share files to the conversation" : "Jaa tiedostoja keskusteluun",
"Upload new files" : "Lähetä uusia tiedostoja",
@@ -742,19 +767,13 @@ OC.L10N.register(
"Settings" : "Asetukset",
"Shared items" : "Jaetut tietueet",
"Participants ({count})" : "Osallistujat ({count})",
- "Media" : "Media",
- "Files" : "Tiedostot",
- "Voice messages" : "Ääniviestit",
- "Locations" : "Sijainnit",
- "Audio" : "Ääni",
- "Other" : "Muu",
+ "Projects" : "Projektit",
"Show all media" : "Näytä kaikki media",
"Show all files" : "Näytä kaikki tiedostot",
"Show all voice messages" : "Näytä kaikki ääniviestit",
"Show all locations" : "Näytä kaikki sijainnit",
"Show all audio" : "Näytä kaikki ääni",
"Show all other" : "Näytä kaikki muu",
- "Projects" : "Projektit",
"Meeting ID: {meetingId}" : "Tapaamisen tunniste: {meetingId}",
"Your PIN: {attendeePin}" : "PIN-koodisi: {attendeePin}",
"Display name: <strong>{name}</strong>" : "Näyttönimi: <strong>{name}</strong>",
@@ -806,6 +825,12 @@ OC.L10N.register(
"Access to microphone is only possible with HTTPS" : "Mikrofonin käyttäminen on mahdollista vain HTTPS-yhteydellä",
"Access to microphone was denied" : "Pääsy mikrofoniin estettiin",
"Access to camera is only possible with HTTPS" : "Kameran käyttäminen on mahdollista vain HTTPS-yhteydellä",
+ "Media" : "Media",
+ "Files" : "Tiedostot",
+ "Voice messages" : "Ääniviestit",
+ "Locations" : "Sijainnit",
+ "Audio" : "Ääni",
+ "Other" : "Muu",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk päivitettiin, lataa sivu uudelleen",
"Do not disturb" : "Älä häiritse",
"Away" : "Poissa",
@@ -842,6 +867,7 @@ OC.L10N.register(
"{actor} set the description to \"%1$s\"" : "{actor} asetti kuvaukseksi \"%1$s\"",
"You set the description to \"%1$s\"" : "Asetit kuvaukseksi \"%1$s\"",
"An administrator set the description to \"%1$s\"" : "Ylläpitäjä asetti kuvaukseksi \"%1$s\"",
+ "{actor} started Matterbridge." : "{actor} käynnisti Matterbridgen.",
"You started Matterbridge." : "Käynnistit Matterbridgen.",
"{actor} stopped Matterbridge." : "{actor} pysäytti Matterbridgen.",
"You stopped Matterbridge." : "Pysäytit Matterbridgen.",
diff --git a/l10n/fi.json b/l10n/fi.json
index 8f98c5574..02de0fa0b 100644
--- a/l10n/fi.json
+++ b/l10n/fi.json
@@ -14,6 +14,7 @@
"Talk" : "Puhe",
"Guest" : "Vieras",
"- Raise your hand in a call with the R key" : "- Nosta kätesi puhelussa R-näppäimellä",
+ "There are currently no commands available." : "Tällä hetkellä ei komentoja käytettävissä.",
"The command does not exist" : "Komentoa ei ole olemassa",
"Talk updates ✅" : "Talk-päivitykset ✅",
"{actor} created the conversation" : "{actor} loi keskustelun",
@@ -21,6 +22,7 @@
"An administrator created the conversation" : "Ylläpitäjä loi keskustelun",
"{actor} renamed the conversation from \"%1$s\" to \"%2$s\"" : "{actor} muutti keskustelun \"%1$s\" uudeksi nimeksi \"%2$s\"",
"You renamed the conversation from \"%1$s\" to \"%2$s\"" : "Sinä muutit keskustelun \"%1$s\" uudeksi nimeksi \"%2$s\"",
+ "An administrator renamed the conversation from \"%1$s\" to \"%2$s\"" : "Ylläpitäjä muutti keskustelun \"%1$s\" uudeksi nimeksi \"%2$s\"",
"{actor} set the description" : "{actor} asetti kuvauksen",
"You set the description" : "Sinä asetit kuvauksen",
"An administrator set the description" : "Ylläpitäjä asetti kuvauksen",
@@ -47,6 +49,8 @@
"The conversation is now open to everyone" : "Keskustelu on nyt avoin kaikille",
"{actor} opened the conversation to everyone" : "{actor} avasi keskustelun kaikille",
"You opened the conversation to everyone" : "Avasit uuden keskustelun kaikille",
+ "{actor} restricted the conversation to moderators" : "{actor} rajoitti keskustelun moderaattoreille",
+ "You restricted the conversation to moderators" : "Rajoitit keskustelun moderaattoreille",
"{actor} allowed guests" : "{actor} salli vieraat",
"You allowed guests" : "Sinä sallit vieraat",
"An administrator allowed guests" : "Ylläpitäjä salli vieraat",
@@ -93,9 +97,12 @@
"{actor} demoted you from moderator" : "{actor} poisti sinulta moderaattorin oikeudet",
"{actor} shared a file which is no longer available" : "{actor} jakoi kanssasi tiedoston, joka ei ole enää saatavilla",
"You shared a file which is no longer available" : "Jaoit tiedoston, joka ei ole enää saatavilla",
+ "{actor} updated the Matterbridge configuration" : "{actor} päivitti Matterbridge-kokoonpanon",
+ "You updated the Matterbridge configuration" : "Päivitit Matterbridge-kokoonpanon",
"{actor} started Matterbridge" : "{actor} käynnisti Matterbridgen",
"You started Matterbridge" : "Käynnistit Matterbridgen",
"{actor} stopped Matterbridge" : "{actor} pysäytti Matterbridgen",
+ "You stopped Matterbridge" : "Pysäytit Matterbridgen",
"{actor} deleted a message" : "{actor} poisti viestin",
"You deleted a message" : "Sinä poistit viestin",
"{actor} deleted a reaction" : "{actor} poisti reaktion",
@@ -116,6 +123,7 @@
"Invalid image" : "Virheellinen kuva",
"Unknown filetype" : "Tuntematon tiedostotyyppi",
"An error occurred. Please contact your admin." : "Tapahtui virhe. Ole yhteydessä ylläpitäjään.",
+ "Talk mentions" : "Talk-maininnat",
"Write to conversation" : "Kirjoita keskusteluun",
"%s invited you to a conversation." : "%s kutsui sinut keskusteluun.",
"You were invited to a conversation." : "Sinut kutsuttiin keskusteluun.",
@@ -140,6 +148,7 @@
"{user} replied to your private message" : "{user} vastasi yksityisviestiisi",
"{user} replied to your message in conversation {call}" : "{user} vastasi viestiisi keskustelussa {call}",
"A guest replied to your message in conversation {call}" : "Vieras vastasi viestiisi keskustelussa {call}",
+ "{user} reacted with {reaction} to your private message" : "{user} vastasi reaktiolla {reaction} yksityisviestiisi",
"{user} mentioned you in a private conversation" : "{user} mainitsi sinut yksityiskeskustelussa",
"{user} mentioned you in conversation {call}" : "{user} mainitsi sinut keskustelussa {call}",
"A deleted user mentioned you in conversation {call}" : "Poistettu käyttäjä mainitsi sinut keskustelussa {call}",
@@ -153,7 +162,9 @@
"{user} would like to talk with you" : "{user} haluaa puhua kanssasi",
"Call back" : "Soita takaisin",
"A group call has started in {call}" : "Ryhmäpuhelu on alkanut puhelussa {call}",
+ "{email} is requesting the password to access {file}" : "{email} pyytää salasanaa saadakseen pääsyn tiedostoon {file}",
"Open settings" : "Avaa asetukset",
+ "Open Talk" : "Avaa Talk",
"Conversations" : "Keskustelut",
"Messages" : "Viestit",
"{user}" : "{user}",
@@ -167,6 +178,7 @@
"The language is invalid." : "Kieli on virheellinen.",
"The country is invalid." : "Maa on virheellinen.",
"Something unexpected happened. Please try again later." : "Jotain odottamatonta tapahtui. Yritä myöhemmin uudelleen.",
+ "There is no such account registered." : "Kyseistä tiliä ei ole rekisteröity.",
"Andorra" : "Andorra",
"United Arab Emirates" : "Yhdistyneet arabiemiirikunnat",
"Afghanistan" : "Afganistan",
@@ -443,9 +455,12 @@
"Matterbridge integration" : "Matterbridge-integraatio",
"Enable Matterbridge integration" : "Käytä Matterbridge-integraatiota",
"Downloading …" : "Ladataan…",
+ "Install Talk Matterbridge" : "Asenna Talk Matterbridge",
"Installed version: {version}" : "Asennettu versio: {version}",
"Matterbridge binary was not found or couldn't be executed." : "Matterbridge-binääriä ei löytynyt tai sitä ei voitu suorittaa.",
+ "An error occurred while installing the Matterbridge app." : "Virhe Matterbridge-sovellusta asentaessa.",
"Failed to execute Matterbridge binary." : "Matterbridge-binäärin suorittaminen epäonnistui.",
+ "SIP configuration" : "SIP-määritys",
"Shared secret" : "Jaettu salaisuus",
"Validate SSL certificate" : "Vahvista SSL-varmenne",
"Delete this server" : "Poista tämä palvelin",
@@ -453,6 +468,7 @@
"OK: Running version: {version}" : "OK: Suoritetaan versiota: {version}",
"Error: Cannot connect to server" : "Virhe: Ei voi yhdistää palvelimeen",
"Could not get version" : "Versiota ei saatu",
+ "Error: Server responded with: {error}" : "Virhe: palvelin vastasi: {error}",
"Error: Unknown error occurred" : "Virhe: Tuntematon virhe",
"Saved" : "Tallennettu",
"Add a new server" : "Lisää uusi palvelin",
@@ -515,6 +531,7 @@
"Back" : "Takaisin",
"Access to camera was denied" : "Pääsy kameraan estettiin",
"Error while accessing camera: It is likely in use by another program" : "Virhe yrittäessä käyttää kameraa: se on luultavasti toisen ohjelman käytössä",
+ "Error while accessing camera" : "Virhe käyttäessä kameraa",
"You have been muted by a moderator" : "Moderaattori on mykistänyt sinut",
"You" : "Sinä",
"Connection could not be established …" : "Yhteyttä ei voitu muodostaa…",
@@ -545,6 +562,8 @@
"Guests access" : "Vieraiden pääsy",
"Participants permissions" : "Osallistujan oikeudet",
"Meeting settings" : "Kokouksen asetukset",
+ "Matterbridge" : "Matterbridge",
+ "Error while updating conversation description" : "Virhe keskustelun kuvausta päivittäessä",
"Leave conversation" : "Poistu keskustelusta",
"Delete conversation" : "Poista keskustelu",
"Permanently delete this conversation." : "Poista tämä keskustelu pysyvästi.",
@@ -583,6 +602,7 @@
"Nextcloud URL" : "Nextcloudin URL-osoite",
"Nextcloud user" : "Nextcloud-käyttäjä",
"User password" : "Käyttäjän salasana",
+ "Talk conversation" : "Talk-keskustelu",
"Matrix server URL" : "Matrix-palvelimen URL-osoite",
"Matrix channel" : "Matrix-kanava",
"Mattermost server URL" : "Mattermost-palvelimen URL-osoite",
@@ -595,6 +615,8 @@
"Rocket.Chat channel" : "Rocket.Chat-kanava",
"Skip TLS verification" : "Ohita TLS-vahvistus",
"Zulip server URL" : "Zulip-palvelimen URL-osoite",
+ "Bot user name" : "Botin käyttäjätunnus",
+ "Bot API key" : "Botin rajapinta-avain",
"API token" : "API-poletti",
"Slack channel" : "Slack-kanava",
"Channel" : "Kanava",
@@ -633,6 +655,7 @@
"Groups" : "Ryhmät",
"Circles" : "Piirit",
"Unread mentions" : "Lukemattomat maininnat",
+ "Talk settings" : "Talk-asetukset",
"Users, groups and circles" : "Käyttäjät, ryhmät ja piirit",
"Users and groups" : "Käyttäjät ja ryhmät",
"Users and circles" : "Käyttäjät ja piirit",
@@ -685,6 +708,8 @@
"Yesterday" : "Eilen",
"Record voice message" : "Äänitä ääniviesti",
"End recording and send" : "Lopeta äänitys ja lähetä",
+ "Access to the microphone was denied" : "Pääsy mikrofoniin estettiin",
+ "Microphone either not available or disabled in settings" : "Mikrofoni ei ole käytettävissä tai se on poistettu käytöstä asetuksista",
"Error while recording audio" : "Virhe äänittäessä",
"Share files to the conversation" : "Jaa tiedostoja keskusteluun",
"Upload new files" : "Lähetä uusia tiedostoja",
@@ -740,19 +765,13 @@
"Settings" : "Asetukset",
"Shared items" : "Jaetut tietueet",
"Participants ({count})" : "Osallistujat ({count})",
- "Media" : "Media",
- "Files" : "Tiedostot",
- "Voice messages" : "Ääniviestit",
- "Locations" : "Sijainnit",
- "Audio" : "Ääni",
- "Other" : "Muu",
+ "Projects" : "Projektit",
"Show all media" : "Näytä kaikki media",
"Show all files" : "Näytä kaikki tiedostot",
"Show all voice messages" : "Näytä kaikki ääniviestit",
"Show all locations" : "Näytä kaikki sijainnit",
"Show all audio" : "Näytä kaikki ääni",
"Show all other" : "Näytä kaikki muu",
- "Projects" : "Projektit",
"Meeting ID: {meetingId}" : "Tapaamisen tunniste: {meetingId}",
"Your PIN: {attendeePin}" : "PIN-koodisi: {attendeePin}",
"Display name: <strong>{name}</strong>" : "Näyttönimi: <strong>{name}</strong>",
@@ -804,6 +823,12 @@
"Access to microphone is only possible with HTTPS" : "Mikrofonin käyttäminen on mahdollista vain HTTPS-yhteydellä",
"Access to microphone was denied" : "Pääsy mikrofoniin estettiin",
"Access to camera is only possible with HTTPS" : "Kameran käyttäminen on mahdollista vain HTTPS-yhteydellä",
+ "Media" : "Media",
+ "Files" : "Tiedostot",
+ "Voice messages" : "Ääniviestit",
+ "Locations" : "Sijainnit",
+ "Audio" : "Ääni",
+ "Other" : "Muu",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk päivitettiin, lataa sivu uudelleen",
"Do not disturb" : "Älä häiritse",
"Away" : "Poissa",
@@ -840,6 +865,7 @@
"{actor} set the description to \"%1$s\"" : "{actor} asetti kuvaukseksi \"%1$s\"",
"You set the description to \"%1$s\"" : "Asetit kuvaukseksi \"%1$s\"",
"An administrator set the description to \"%1$s\"" : "Ylläpitäjä asetti kuvaukseksi \"%1$s\"",
+ "{actor} started Matterbridge." : "{actor} käynnisti Matterbridgen.",
"You started Matterbridge." : "Käynnistit Matterbridgen.",
"{actor} stopped Matterbridge." : "{actor} pysäytti Matterbridgen.",
"You stopped Matterbridge." : "Pysäytit Matterbridgen.",
diff --git a/l10n/fr.js b/l10n/fr.js
index 2bd6be2c7..a96642595 100644
--- a/l10n/fr.js
+++ b/l10n/fr.js
@@ -1038,13 +1038,8 @@ OC.L10N.register(
"Details" : "Details",
"Settings" : "Réglages",
"Participants ({count})" : "Participants ({count})",
- "Media" : "Média",
- "Files" : "Fichiers",
- "Locations" : "Lieux",
- "Audio" : "Son",
- "Other" : "Autre",
- "Show all files" : "Afficher tous les fichiers",
"Projects" : "Projets",
+ "Show all files" : "Afficher tous les fichiers",
"Meeting ID: {meetingId}" : "ID de réunion : {meetingId}",
"Your PIN: {attendeePin}" : "Votre PIN : {attendeePin}",
"Display name: <strong>{name}</strong>" : "Nom affiché : <strong>{name}</strong>",
@@ -1114,6 +1109,11 @@ OC.L10N.register(
"Error while accessing microphone" : "Erreur d'accès au microphone",
"Access to camera is only possible with HTTPS" : "L'accès à la caméra est seulement possible en HTTPS",
"An error occurred while fetching the participants" : "Une erreur est survenue pendant la récupération des participants",
+ "Media" : "Média",
+ "Files" : "Fichiers",
+ "Locations" : "Lieux",
+ "Audio" : "Son",
+ "Other" : "Autre",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk a été mis à jour, veuillez actualiser la page",
"Do not disturb" : "Ne pas déranger",
"Away" : "Absent",
diff --git a/l10n/fr.json b/l10n/fr.json
index c06bca2cd..4ce5bbda1 100644
--- a/l10n/fr.json
+++ b/l10n/fr.json
@@ -1036,13 +1036,8 @@
"Details" : "Details",
"Settings" : "Réglages",
"Participants ({count})" : "Participants ({count})",
- "Media" : "Média",
- "Files" : "Fichiers",
- "Locations" : "Lieux",
- "Audio" : "Son",
- "Other" : "Autre",
- "Show all files" : "Afficher tous les fichiers",
"Projects" : "Projets",
+ "Show all files" : "Afficher tous les fichiers",
"Meeting ID: {meetingId}" : "ID de réunion : {meetingId}",
"Your PIN: {attendeePin}" : "Votre PIN : {attendeePin}",
"Display name: <strong>{name}</strong>" : "Nom affiché : <strong>{name}</strong>",
@@ -1112,6 +1107,11 @@
"Error while accessing microphone" : "Erreur d'accès au microphone",
"Access to camera is only possible with HTTPS" : "L'accès à la caméra est seulement possible en HTTPS",
"An error occurred while fetching the participants" : "Une erreur est survenue pendant la récupération des participants",
+ "Media" : "Média",
+ "Files" : "Fichiers",
+ "Locations" : "Lieux",
+ "Audio" : "Son",
+ "Other" : "Autre",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk a été mis à jour, veuillez actualiser la page",
"Do not disturb" : "Ne pas déranger",
"Away" : "Absent",
diff --git a/l10n/gl.js b/l10n/gl.js
index 5b2ad2abc..622052e98 100644
--- a/l10n/gl.js
+++ b/l10n/gl.js
@@ -878,13 +878,8 @@ OC.L10N.register(
"Chat" : "Conversa",
"Details" : "Detalles",
"Settings" : "Axustes",
- "Media" : "Multimedia",
- "Files" : "Ficheiros",
- "Locations" : "Localizacións",
- "Audio" : "Son",
- "Other" : "Outro",
- "Show all files" : "Amosar todos os ficheiros",
"Projects" : "Proxectos",
+ "Show all files" : "Amosar todos os ficheiros",
"Meeting ID: {meetingId}" : "ID da xuntanza: {meetingId}",
"Your PIN: {attendeePin}" : "O seu PIN: {attendeePin}",
"Attachments folder" : "Cartafol de anexos",
@@ -940,6 +935,11 @@ OC.L10N.register(
"Error while accessing microphone" : "Produciuse un erro ao acceder ao micrófono",
"Access to camera is only possible with HTTPS" : "O acceso á cámara só é posíbel con HTTPS",
"An error occurred while fetching the participants" : "Produciuse un erro ao obter os participantes",
+ "Media" : "Multimedia",
+ "Files" : "Ficheiros",
+ "Locations" : "Localizacións",
+ "Audio" : "Son",
+ "Other" : "Outro",
"Nextcloud Talk was updated, please reload the page" : "Actualizouse o Nextcloud Talk, volva cargar a páxina",
"Do not disturb" : "Non molestar",
"Away" : "Ausente",
diff --git a/l10n/gl.json b/l10n/gl.json
index adccadd2c..e2566546f 100644
--- a/l10n/gl.json
+++ b/l10n/gl.json
@@ -876,13 +876,8 @@
"Chat" : "Conversa",
"Details" : "Detalles",
"Settings" : "Axustes",
- "Media" : "Multimedia",
- "Files" : "Ficheiros",
- "Locations" : "Localizacións",
- "Audio" : "Son",
- "Other" : "Outro",
- "Show all files" : "Amosar todos os ficheiros",
"Projects" : "Proxectos",
+ "Show all files" : "Amosar todos os ficheiros",
"Meeting ID: {meetingId}" : "ID da xuntanza: {meetingId}",
"Your PIN: {attendeePin}" : "O seu PIN: {attendeePin}",
"Attachments folder" : "Cartafol de anexos",
@@ -938,6 +933,11 @@
"Error while accessing microphone" : "Produciuse un erro ao acceder ao micrófono",
"Access to camera is only possible with HTTPS" : "O acceso á cámara só é posíbel con HTTPS",
"An error occurred while fetching the participants" : "Produciuse un erro ao obter os participantes",
+ "Media" : "Multimedia",
+ "Files" : "Ficheiros",
+ "Locations" : "Localizacións",
+ "Audio" : "Son",
+ "Other" : "Outro",
"Nextcloud Talk was updated, please reload the page" : "Actualizouse o Nextcloud Talk, volva cargar a páxina",
"Do not disturb" : "Non molestar",
"Away" : "Ausente",
diff --git a/l10n/he.js b/l10n/he.js
index 62bd5a391..f4328ca3f 100644
--- a/l10n/he.js
+++ b/l10n/he.js
@@ -532,10 +532,6 @@ OC.L10N.register(
"Chat" : "צ׳אט",
"Details" : "פרטים",
"Settings" : "הגדרות",
- "Files" : "קבצים",
- "Locations" : "מיקומים",
- "Audio" : "שמע",
- "Other" : "אחר",
"Projects" : "מיזמים",
"Attachments folder" : "תיקיית הקבצים המצורפים",
"Privacy" : "פרטיות",
@@ -562,6 +558,10 @@ OC.L10N.register(
"Calls are not supported in your browser" : "אין תמיכה בשיחות בדפדפן שלך",
"Access to microphone was denied" : "הגישה למיקרופון נדחתה",
"An error occurred while fetching the participants" : "אירעה שגיאה במהלך קבלת המשתתפים",
+ "Files" : "קבצים",
+ "Locations" : "מיקומים",
+ "Audio" : "שמע",
+ "Other" : "אחר",
"Do not disturb" : "לא להפריע",
"Away" : "לא פה",
"Error while sharing file" : "שגיאה בשיתוף הקובץ",
diff --git a/l10n/he.json b/l10n/he.json
index 9802816eb..92ff69967 100644
--- a/l10n/he.json
+++ b/l10n/he.json
@@ -530,10 +530,6 @@
"Chat" : "צ׳אט",
"Details" : "פרטים",
"Settings" : "הגדרות",
- "Files" : "קבצים",
- "Locations" : "מיקומים",
- "Audio" : "שמע",
- "Other" : "אחר",
"Projects" : "מיזמים",
"Attachments folder" : "תיקיית הקבצים המצורפים",
"Privacy" : "פרטיות",
@@ -560,6 +556,10 @@
"Calls are not supported in your browser" : "אין תמיכה בשיחות בדפדפן שלך",
"Access to microphone was denied" : "הגישה למיקרופון נדחתה",
"An error occurred while fetching the participants" : "אירעה שגיאה במהלך קבלת המשתתפים",
+ "Files" : "קבצים",
+ "Locations" : "מיקומים",
+ "Audio" : "שמע",
+ "Other" : "אחר",
"Do not disturb" : "לא להפריע",
"Away" : "לא פה",
"Error while sharing file" : "שגיאה בשיתוף הקובץ",
diff --git a/l10n/hr.js b/l10n/hr.js
index 7301835b5..4a1f8d7c1 100644
--- a/l10n/hr.js
+++ b/l10n/hr.js
@@ -1004,13 +1004,8 @@ OC.L10N.register(
"Details" : "Pojedinosti",
"Settings" : "Postavke",
"Participants ({count})" : "Sudionici ({count})",
- "Media" : "Medij",
- "Files" : "Datoteke",
- "Locations" : "Lokacije",
- "Audio" : "Audio",
- "Other" : "Drugo",
- "Show all files" : "Prikaži sve datoteke",
"Projects" : "Projekti",
+ "Show all files" : "Prikaži sve datoteke",
"Meeting ID: {meetingId}" : "ID sastanka: {meetingId}",
"Your PIN: {attendeePin}" : "Vaš PIN: {attendeePin}",
"Attachments folder" : "Mapa s privicima",
@@ -1078,6 +1073,11 @@ OC.L10N.register(
"Error while accessing microphone" : "Došlo je do pogreške pri pristupanju mikrofonu",
"Access to camera is only possible with HTTPS" : "Pristup kameri moguć je samo putem HTTPS-a",
"An error occurred while fetching the participants" : "Došlo je do pogreške pri dohvaćanju sudionika",
+ "Media" : "Medij",
+ "Files" : "Datoteke",
+ "Locations" : "Lokacije",
+ "Audio" : "Audio",
+ "Other" : "Drugo",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk je ažuriran, ponovno učitajte stranicu",
"Do not disturb" : "Ne ometaj",
"Away" : "Odsutan",
diff --git a/l10n/hr.json b/l10n/hr.json
index 7e060c949..d3896fe94 100644
--- a/l10n/hr.json
+++ b/l10n/hr.json
@@ -1002,13 +1002,8 @@
"Details" : "Pojedinosti",
"Settings" : "Postavke",
"Participants ({count})" : "Sudionici ({count})",
- "Media" : "Medij",
- "Files" : "Datoteke",
- "Locations" : "Lokacije",
- "Audio" : "Audio",
- "Other" : "Drugo",
- "Show all files" : "Prikaži sve datoteke",
"Projects" : "Projekti",
+ "Show all files" : "Prikaži sve datoteke",
"Meeting ID: {meetingId}" : "ID sastanka: {meetingId}",
"Your PIN: {attendeePin}" : "Vaš PIN: {attendeePin}",
"Attachments folder" : "Mapa s privicima",
@@ -1076,6 +1071,11 @@
"Error while accessing microphone" : "Došlo je do pogreške pri pristupanju mikrofonu",
"Access to camera is only possible with HTTPS" : "Pristup kameri moguć je samo putem HTTPS-a",
"An error occurred while fetching the participants" : "Došlo je do pogreške pri dohvaćanju sudionika",
+ "Media" : "Medij",
+ "Files" : "Datoteke",
+ "Locations" : "Lokacije",
+ "Audio" : "Audio",
+ "Other" : "Drugo",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk je ažuriran, ponovno učitajte stranicu",
"Do not disturb" : "Ne ometaj",
"Away" : "Odsutan",
diff --git a/l10n/hu.js b/l10n/hu.js
index 8a202c091..014c3beab 100644
--- a/l10n/hu.js
+++ b/l10n/hu.js
@@ -1039,13 +1039,7 @@ OC.L10N.register(
"Settings" : "Beállítások",
"Shared items" : "Megosztott elemek",
"Participants ({count})" : "Résztvevők ({count})",
- "Media" : "Média",
- "Files" : "Fájlok",
- "Deck cards" : "Kártyák",
- "Voice messages" : "Hangüzenetek",
- "Locations" : "Helyek",
- "Audio" : "Hangok",
- "Other" : "Egyebek",
+ "Projects" : "Projektek",
"Show all media" : "Összes média megjelenítése",
"Show all files" : "Összes fájl megjelenítése",
"Show all deck cards" : "Összes kártya megjelenítése",
@@ -1053,7 +1047,6 @@ OC.L10N.register(
"Show all locations" : "Összes hely megjelenítése",
"Show all audio" : "Összes hang megjelenítése",
"Show all other" : "Összes egyéb megtekintése",
- "Projects" : "Projektek",
"Meeting ID: {meetingId}" : "Megbeszélés azonosító: {meetingId}",
"Your PIN: {attendeePin}" : "Az Ön PIN-kódja: {attendeePin}",
"Display name: <strong>{name}</strong>" : "Megjelenítendő név: <strong>{name}</strong>",
@@ -1063,6 +1056,7 @@ OC.L10N.register(
"Sounds" : "Hangok",
"Play sounds when participants join or leave a call" : "Hangjelzések lejátszása, amikor a résztvevők csatlakoznak, vagy elhagyják a hívást",
"Sounds can currently not be played in Safari browser and iPad and iPhone devices due to technical restrictions by the manufacturer." : "A hangokat a gyártó műszaki korlátozásai miatt jelenleg nem lehet lejátszani Safari böngészőben, valamint iPad és iPhone eszközökön.",
+ "Sounds for chat and call notifications can be adjusted in the personal settings." : "A csevegési és hívásértesítések hangja a személyes beállításokban módosítható. ",
"Keyboard shortcuts" : "Gyorsbillentyűk",
"Speed up your Talk experience with these quick shortcuts." : "Ezekkel a billentyűparancsokkal felpörgetheti a Beszélgetés élményét.",
"Focus the chat input" : "Fókusz a csevegés beviteli mezőjére",
@@ -1123,6 +1117,13 @@ OC.L10N.register(
"Error while accessing microphone" : "Hiba történt a mikrofon elérésekor",
"Access to camera is only possible with HTTPS" : "A kamera hozzáférése csak HTTPS-en lehetséges",
"An error occurred while fetching the participants" : "Hiba történt a résztvevők letöltése során",
+ "Media" : "Média",
+ "Files" : "Fájlok",
+ "Deck cards" : "Kártyák",
+ "Voice messages" : "Hangüzenetek",
+ "Locations" : "Helyek",
+ "Audio" : "Hangok",
+ "Other" : "Egyebek",
"Nextcloud Talk was updated, please reload the page" : "A Nextcloud Beszélgetés frissült, töltse újra az oldalt",
"Do not disturb" : "Ne zavarjanak",
"Away" : "Távol",
@@ -1131,6 +1132,7 @@ OC.L10N.register(
"Not enough free space to upload file \"{fileName}\"" : "Nincs elég szabad hely a(z) „{fileName}” fájl feltöltéséhez",
"Error while uploading file \"{fileName}\"" : "Hiba történt a(z) „{fileName}” fájl feltöltésekor",
"An error happened when trying to share your file" : "Hiba történt a fájl megosztása során",
+ "{guest} (guest)" : "{guest} (vendég)",
"Could not post message: {errorMessage}" : "Nem sikerült elküldeni az üzenetet: {errorMessage}",
"Failed to add reaction" : "A reakció hozzáadása sikertelen",
"Failed to remove reaction" : "A reakció eltávolítása sikertelen",
diff --git a/l10n/hu.json b/l10n/hu.json
index 0fc311179..25bbd083f 100644
--- a/l10n/hu.json
+++ b/l10n/hu.json
@@ -1037,13 +1037,7 @@
"Settings" : "Beállítások",
"Shared items" : "Megosztott elemek",
"Participants ({count})" : "Résztvevők ({count})",
- "Media" : "Média",
- "Files" : "Fájlok",
- "Deck cards" : "Kártyák",
- "Voice messages" : "Hangüzenetek",
- "Locations" : "Helyek",
- "Audio" : "Hangok",
- "Other" : "Egyebek",
+ "Projects" : "Projektek",
"Show all media" : "Összes média megjelenítése",
"Show all files" : "Összes fájl megjelenítése",
"Show all deck cards" : "Összes kártya megjelenítése",
@@ -1051,7 +1045,6 @@
"Show all locations" : "Összes hely megjelenítése",
"Show all audio" : "Összes hang megjelenítése",
"Show all other" : "Összes egyéb megtekintése",
- "Projects" : "Projektek",
"Meeting ID: {meetingId}" : "Megbeszélés azonosító: {meetingId}",
"Your PIN: {attendeePin}" : "Az Ön PIN-kódja: {attendeePin}",
"Display name: <strong>{name}</strong>" : "Megjelenítendő név: <strong>{name}</strong>",
@@ -1061,6 +1054,7 @@
"Sounds" : "Hangok",
"Play sounds when participants join or leave a call" : "Hangjelzések lejátszása, amikor a résztvevők csatlakoznak, vagy elhagyják a hívást",
"Sounds can currently not be played in Safari browser and iPad and iPhone devices due to technical restrictions by the manufacturer." : "A hangokat a gyártó műszaki korlátozásai miatt jelenleg nem lehet lejátszani Safari böngészőben, valamint iPad és iPhone eszközökön.",
+ "Sounds for chat and call notifications can be adjusted in the personal settings." : "A csevegési és hívásértesítések hangja a személyes beállításokban módosítható. ",
"Keyboard shortcuts" : "Gyorsbillentyűk",
"Speed up your Talk experience with these quick shortcuts." : "Ezekkel a billentyűparancsokkal felpörgetheti a Beszélgetés élményét.",
"Focus the chat input" : "Fókusz a csevegés beviteli mezőjére",
@@ -1121,6 +1115,13 @@
"Error while accessing microphone" : "Hiba történt a mikrofon elérésekor",
"Access to camera is only possible with HTTPS" : "A kamera hozzáférése csak HTTPS-en lehetséges",
"An error occurred while fetching the participants" : "Hiba történt a résztvevők letöltése során",
+ "Media" : "Média",
+ "Files" : "Fájlok",
+ "Deck cards" : "Kártyák",
+ "Voice messages" : "Hangüzenetek",
+ "Locations" : "Helyek",
+ "Audio" : "Hangok",
+ "Other" : "Egyebek",
"Nextcloud Talk was updated, please reload the page" : "A Nextcloud Beszélgetés frissült, töltse újra az oldalt",
"Do not disturb" : "Ne zavarjanak",
"Away" : "Távol",
@@ -1129,6 +1130,7 @@
"Not enough free space to upload file \"{fileName}\"" : "Nincs elég szabad hely a(z) „{fileName}” fájl feltöltéséhez",
"Error while uploading file \"{fileName}\"" : "Hiba történt a(z) „{fileName}” fájl feltöltésekor",
"An error happened when trying to share your file" : "Hiba történt a fájl megosztása során",
+ "{guest} (guest)" : "{guest} (vendég)",
"Could not post message: {errorMessage}" : "Nem sikerült elküldeni az üzenetet: {errorMessage}",
"Failed to add reaction" : "A reakció hozzáadása sikertelen",
"Failed to remove reaction" : "A reakció eltávolítása sikertelen",
diff --git a/l10n/id.js b/l10n/id.js
index 6aa35a76a..f3d435906 100644
--- a/l10n/id.js
+++ b/l10n/id.js
@@ -58,15 +58,15 @@ OC.L10N.register(
"Add users or groups" : "Add users or groups",
"Details" : "Detail",
"Settings" : "Setelan",
- "Media" : "Media",
- "Files" : "Berkas",
- "Audio" : "Audio",
- "Other" : "Lainnya",
"Projects" : "Proyek",
"Privacy" : "Privasi",
"Keyboard shortcuts" : "Pintasan keyboard",
"Search" : "Cari",
"Send" : "Kirim",
+ "Media" : "Media",
+ "Files" : "Berkas",
+ "Audio" : "Audio",
+ "Other" : "Lainnya",
"Do not disturb" : "Jangan diganggu",
"Away" : "Jauh",
"Default" : "Bawaan",
diff --git a/l10n/id.json b/l10n/id.json
index f24bb0875..fa7a900f6 100644
--- a/l10n/id.json
+++ b/l10n/id.json
@@ -56,15 +56,15 @@
"Add users or groups" : "Add users or groups",
"Details" : "Detail",
"Settings" : "Setelan",
- "Media" : "Media",
- "Files" : "Berkas",
- "Audio" : "Audio",
- "Other" : "Lainnya",
"Projects" : "Proyek",
"Privacy" : "Privasi",
"Keyboard shortcuts" : "Pintasan keyboard",
"Search" : "Cari",
"Send" : "Kirim",
+ "Media" : "Media",
+ "Files" : "Berkas",
+ "Audio" : "Audio",
+ "Other" : "Lainnya",
"Do not disturb" : "Jangan diganggu",
"Away" : "Jauh",
"Default" : "Bawaan",
diff --git a/l10n/is.js b/l10n/is.js
index 4273b989c..dcb3db123 100644
--- a/l10n/is.js
+++ b/l10n/is.js
@@ -482,11 +482,6 @@ OC.L10N.register(
"Chat" : "Spjall",
"Details" : "Details",
"Settings" : "Stillingar",
- "Media" : "Margmiðlunargögn",
- "Files" : "Skrár",
- "Locations" : "Staðsetningar",
- "Audio" : "Hljóð",
- "Other" : "Annað",
"Projects" : "Verkefni",
"Privacy" : "Gagnaleynd",
"Keyboard shortcuts" : "Flýtileiðir á lyklaborði",
@@ -499,6 +494,11 @@ OC.L10N.register(
"Select conversation" : "Veldu samtal",
"Link to a conversation" : "Tengill á samtal",
"Join a conversation or start a new one" : "Taktu þátt í samtali eða byrjaðu nýtt samtal",
+ "Media" : "Margmiðlunargögn",
+ "Files" : "Skrár",
+ "Locations" : "Staðsetningar",
+ "Audio" : "Hljóð",
+ "Other" : "Annað",
"Do not disturb" : "Ónáðið ekki",
"Away" : "Fjarverandi",
"Error while sharing file" : "Villa við deilingu skráar",
diff --git a/l10n/is.json b/l10n/is.json
index 38e9f1396..b62e63342 100644
--- a/l10n/is.json
+++ b/l10n/is.json
@@ -480,11 +480,6 @@
"Chat" : "Spjall",
"Details" : "Details",
"Settings" : "Stillingar",
- "Media" : "Margmiðlunargögn",
- "Files" : "Skrár",
- "Locations" : "Staðsetningar",
- "Audio" : "Hljóð",
- "Other" : "Annað",
"Projects" : "Verkefni",
"Privacy" : "Gagnaleynd",
"Keyboard shortcuts" : "Flýtileiðir á lyklaborði",
@@ -497,6 +492,11 @@
"Select conversation" : "Veldu samtal",
"Link to a conversation" : "Tengill á samtal",
"Join a conversation or start a new one" : "Taktu þátt í samtali eða byrjaðu nýtt samtal",
+ "Media" : "Margmiðlunargögn",
+ "Files" : "Skrár",
+ "Locations" : "Staðsetningar",
+ "Audio" : "Hljóð",
+ "Other" : "Annað",
"Do not disturb" : "Ónáðið ekki",
"Away" : "Fjarverandi",
"Error while sharing file" : "Villa við deilingu skráar",
diff --git a/l10n/it.js b/l10n/it.js
index 5f055a066..918868e2a 100644
--- a/l10n/it.js
+++ b/l10n/it.js
@@ -1035,13 +1035,8 @@ OC.L10N.register(
"Details" : "Dettagli",
"Settings" : "Impostazioni",
"Participants ({count})" : "Partecipanti ({count})",
- "Media" : "Media",
- "Files" : "File",
- "Locations" : "Posizioni",
- "Audio" : "Audio",
- "Other" : "Altro",
- "Show all files" : "Mostra tutti i file",
"Projects" : "Progetti",
+ "Show all files" : "Mostra tutti i file",
"Meeting ID: {meetingId}" : "ID riunione: {meetingId}",
"Your PIN: {attendeePin}" : "Il tuo PIN: {attendeePin}",
"Attachments folder" : "Cartella degli allegati",
@@ -1110,6 +1105,11 @@ OC.L10N.register(
"Error while accessing microphone" : "Errore durante l'accesso al microfono",
"Access to camera is only possible with HTTPS" : "L'accesso alla fotocamera è possibile solo con HTTPS",
"An error occurred while fetching the participants" : "Si è verificato un errore durante il recupero dei partecipanti",
+ "Media" : "Media",
+ "Files" : "File",
+ "Locations" : "Posizioni",
+ "Audio" : "Audio",
+ "Other" : "Altro",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk è stato aggiornato, ricarica la pagina",
"Do not disturb" : "Non disturbare",
"Away" : "Assente",
diff --git a/l10n/it.json b/l10n/it.json
index 27d16a4e3..46582069b 100644
--- a/l10n/it.json
+++ b/l10n/it.json
@@ -1033,13 +1033,8 @@
"Details" : "Dettagli",
"Settings" : "Impostazioni",
"Participants ({count})" : "Partecipanti ({count})",
- "Media" : "Media",
- "Files" : "File",
- "Locations" : "Posizioni",
- "Audio" : "Audio",
- "Other" : "Altro",
- "Show all files" : "Mostra tutti i file",
"Projects" : "Progetti",
+ "Show all files" : "Mostra tutti i file",
"Meeting ID: {meetingId}" : "ID riunione: {meetingId}",
"Your PIN: {attendeePin}" : "Il tuo PIN: {attendeePin}",
"Attachments folder" : "Cartella degli allegati",
@@ -1108,6 +1103,11 @@
"Error while accessing microphone" : "Errore durante l'accesso al microfono",
"Access to camera is only possible with HTTPS" : "L'accesso alla fotocamera è possibile solo con HTTPS",
"An error occurred while fetching the participants" : "Si è verificato un errore durante il recupero dei partecipanti",
+ "Media" : "Media",
+ "Files" : "File",
+ "Locations" : "Posizioni",
+ "Audio" : "Audio",
+ "Other" : "Altro",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk è stato aggiornato, ricarica la pagina",
"Do not disturb" : "Non disturbare",
"Away" : "Assente",
diff --git a/l10n/ja.js b/l10n/ja.js
index a65aeacfe..8fd34a759 100644
--- a/l10n/ja.js
+++ b/l10n/ja.js
@@ -985,13 +985,8 @@ OC.L10N.register(
"Chat" : "チャット",
"Details" : "詳細",
"Settings" : "設定",
- "Media" : "メディア",
- "Files" : "ファイル",
- "Locations" : "場所",
- "Audio" : "オーディオ",
- "Other" : "その他",
- "Show all files" : "すべてのファイルを表示",
"Projects" : "プロジェクト",
+ "Show all files" : "すべてのファイルを表示",
"Meeting ID: {meetingId}" : "ミーティングID: {meetingId}",
"Your PIN: {attendeePin}" : "PIN: {attendeePin}",
"Attachments folder" : "添付ファイルフォルダー",
@@ -1058,6 +1053,11 @@ OC.L10N.register(
"Error while accessing microphone" : "マイクへのアクセス中にエラーが発生しました",
"Access to camera is only possible with HTTPS" : "カメラへのアクセスはHTTPSでのみ可能です",
"An error occurred while fetching the participants" : "参加者の取得中にエラーが発生しました。",
+ "Media" : "メディア",
+ "Files" : "ファイル",
+ "Locations" : "場所",
+ "Audio" : "オーディオ",
+ "Other" : "その他",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talkを更新しました。ページをリロードしてください。",
"Do not disturb" : "取り込み中",
"Away" : "離席中",
diff --git a/l10n/ja.json b/l10n/ja.json
index f8dc55d04..08256b49c 100644
--- a/l10n/ja.json
+++ b/l10n/ja.json
@@ -983,13 +983,8 @@
"Chat" : "チャット",
"Details" : "詳細",
"Settings" : "設定",
- "Media" : "メディア",
- "Files" : "ファイル",
- "Locations" : "場所",
- "Audio" : "オーディオ",
- "Other" : "その他",
- "Show all files" : "すべてのファイルを表示",
"Projects" : "プロジェクト",
+ "Show all files" : "すべてのファイルを表示",
"Meeting ID: {meetingId}" : "ミーティングID: {meetingId}",
"Your PIN: {attendeePin}" : "PIN: {attendeePin}",
"Attachments folder" : "添付ファイルフォルダー",
@@ -1056,6 +1051,11 @@
"Error while accessing microphone" : "マイクへのアクセス中にエラーが発生しました",
"Access to camera is only possible with HTTPS" : "カメラへのアクセスはHTTPSでのみ可能です",
"An error occurred while fetching the participants" : "参加者の取得中にエラーが発生しました。",
+ "Media" : "メディア",
+ "Files" : "ファイル",
+ "Locations" : "場所",
+ "Audio" : "オーディオ",
+ "Other" : "その他",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talkを更新しました。ページをリロードしてください。",
"Do not disturb" : "取り込み中",
"Away" : "離席中",
diff --git a/l10n/ka_GE.js b/l10n/ka_GE.js
index a9cb14815..d7c7a6159 100644
--- a/l10n/ka_GE.js
+++ b/l10n/ka_GE.js
@@ -112,13 +112,13 @@ OC.L10N.register(
"Chat" : "ჩეთი",
"Details" : "დეტალები",
"Settings" : "პარამეტრები",
- "Files" : "ფაილები",
- "Audio" : "აუდიო",
- "Other" : "სხვა",
"Privacy" : "კონფიდენციალურობა",
"Keyboard shortcuts" : "კლავიატურის კომბინაციები",
"Search" : "ძიება",
"Send" : "გაგზავნა",
+ "Files" : "ფაილები",
+ "Audio" : "აუდიო",
+ "Other" : "სხვა",
"Default" : "საწყისი",
"Access to microphone & camera is only possible with HTTPS" : "წვდომა მიკროფონთან და კამერასთან მხარდაჭერილია მხოლოდ HTTPS პროტოკოლით",
"Access to microphone & camera was denied" : "წვდომა მიკროფონზე და კამერაზე აიკრძალა",
diff --git a/l10n/ka_GE.json b/l10n/ka_GE.json
index a19c1c5a0..662b89a88 100644
--- a/l10n/ka_GE.json
+++ b/l10n/ka_GE.json
@@ -110,13 +110,13 @@
"Chat" : "ჩეთი",
"Details" : "დეტალები",
"Settings" : "პარამეტრები",
- "Files" : "ფაილები",
- "Audio" : "აუდიო",
- "Other" : "სხვა",
"Privacy" : "კონფიდენციალურობა",
"Keyboard shortcuts" : "კლავიატურის კომბინაციები",
"Search" : "ძიება",
"Send" : "გაგზავნა",
+ "Files" : "ფაილები",
+ "Audio" : "აუდიო",
+ "Other" : "სხვა",
"Default" : "საწყისი",
"Access to microphone & camera is only possible with HTTPS" : "წვდომა მიკროფონთან და კამერასთან მხარდაჭერილია მხოლოდ HTTPS პროტოკოლით",
"Access to microphone & camera was denied" : "წვდომა მიკროფონზე და კამერაზე აიკრძალა",
diff --git a/l10n/ko.js b/l10n/ko.js
index bb7bbc6eb..915dabac2 100644
--- a/l10n/ko.js
+++ b/l10n/ko.js
@@ -828,12 +828,8 @@ OC.L10N.register(
"Chat" : "대화",
"Details" : "세부사항",
"Settings" : "설정",
- "Media" : "미디어",
- "Files" : "파일",
- "Audio" : "오디오",
- "Other" : "기타",
- "Show all files" : "모든 파일 보기",
"Projects" : "프로젝트",
+ "Show all files" : "모든 파일 보기",
"Meeting ID: {meetingId}" : "미팅 아이디 : {meetingId}",
"Your PIN: {attendeePin}" : "당신의 PIN : {attendeePin}",
"Attachments folder" : "첨부 파일 폴더",
@@ -883,6 +879,10 @@ OC.L10N.register(
"Error while accessing microphone" : "마이크 액세스 중 오류 발생",
"Access to camera is only possible with HTTPS" : "카메라에 대한 액세스는 HTTPS를 통해서만 가능합니다.",
"An error occurred while fetching the participants" : "참가자를 가져오는 동안 오류가 발생했습니다.",
+ "Media" : "미디어",
+ "Files" : "파일",
+ "Audio" : "오디오",
+ "Other" : "기타",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud 토크가 업데이트되었습니다. 페이지를 다시 로드하십시오.",
"Do not disturb" : "방해 금지",
"Away" : "자리 비움",
diff --git a/l10n/ko.json b/l10n/ko.json
index 49db6d5f8..ebf241410 100644
--- a/l10n/ko.json
+++ b/l10n/ko.json
@@ -826,12 +826,8 @@
"Chat" : "대화",
"Details" : "세부사항",
"Settings" : "설정",
- "Media" : "미디어",
- "Files" : "파일",
- "Audio" : "오디오",
- "Other" : "기타",
- "Show all files" : "모든 파일 보기",
"Projects" : "프로젝트",
+ "Show all files" : "모든 파일 보기",
"Meeting ID: {meetingId}" : "미팅 아이디 : {meetingId}",
"Your PIN: {attendeePin}" : "당신의 PIN : {attendeePin}",
"Attachments folder" : "첨부 파일 폴더",
@@ -881,6 +877,10 @@
"Error while accessing microphone" : "마이크 액세스 중 오류 발생",
"Access to camera is only possible with HTTPS" : "카메라에 대한 액세스는 HTTPS를 통해서만 가능합니다.",
"An error occurred while fetching the participants" : "참가자를 가져오는 동안 오류가 발생했습니다.",
+ "Media" : "미디어",
+ "Files" : "파일",
+ "Audio" : "오디오",
+ "Other" : "기타",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud 토크가 업데이트되었습니다. 페이지를 다시 로드하십시오.",
"Do not disturb" : "방해 금지",
"Away" : "자리 비움",
diff --git a/l10n/lt_LT.js b/l10n/lt_LT.js
index 7169f6682..5da8efc63 100644
--- a/l10n/lt_LT.js
+++ b/l10n/lt_LT.js
@@ -628,12 +628,8 @@ OC.L10N.register(
"Chat" : "Pokalbis",
"Details" : "Išsamiau",
"Settings" : "Nustatymai",
- "Media" : "Medija",
- "Files" : "Failai",
- "Locations" : "Vietos",
- "Other" : "Kita",
- "Show all files" : "Rodyti visus failus",
"Projects" : "Projektai",
+ "Show all files" : "Rodyti visus failus",
"Meeting ID: {meetingId}" : "Susitikimo ID: {meetingId}",
"Your PIN: {attendeePin}" : "Jūsų PIN kodas: {attendeePin}",
"Attachments folder" : "Priedų aplankas",
@@ -667,6 +663,10 @@ OC.L10N.register(
"Error while accessing microphone" : "Klaida gaunant prieigą prie mikrofono",
"Access to camera is only possible with HTTPS" : "Prieiga prie kameros yra įmanoma tik naudojant HTTPS",
"An error occurred while fetching the participants" : "Gaunant dalyvius, įvyko klaida",
+ "Media" : "Medija",
+ "Files" : "Failai",
+ "Locations" : "Vietos",
+ "Other" : "Kita",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Pokalbiai buvo atnaujinti, įkelkite puslapį iš naujo",
"Do not disturb" : "Netrukdyti",
"Away" : "Atsitraukęs",
diff --git a/l10n/lt_LT.json b/l10n/lt_LT.json
index ccc51d925..e25eb83da 100644
--- a/l10n/lt_LT.json
+++ b/l10n/lt_LT.json
@@ -626,12 +626,8 @@
"Chat" : "Pokalbis",
"Details" : "Išsamiau",
"Settings" : "Nustatymai",
- "Media" : "Medija",
- "Files" : "Failai",
- "Locations" : "Vietos",
- "Other" : "Kita",
- "Show all files" : "Rodyti visus failus",
"Projects" : "Projektai",
+ "Show all files" : "Rodyti visus failus",
"Meeting ID: {meetingId}" : "Susitikimo ID: {meetingId}",
"Your PIN: {attendeePin}" : "Jūsų PIN kodas: {attendeePin}",
"Attachments folder" : "Priedų aplankas",
@@ -665,6 +661,10 @@
"Error while accessing microphone" : "Klaida gaunant prieigą prie mikrofono",
"Access to camera is only possible with HTTPS" : "Prieiga prie kameros yra įmanoma tik naudojant HTTPS",
"An error occurred while fetching the participants" : "Gaunant dalyvius, įvyko klaida",
+ "Media" : "Medija",
+ "Files" : "Failai",
+ "Locations" : "Vietos",
+ "Other" : "Kita",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Pokalbiai buvo atnaujinti, įkelkite puslapį iš naujo",
"Do not disturb" : "Netrukdyti",
"Away" : "Atsitraukęs",
diff --git a/l10n/lv.js b/l10n/lv.js
index f8368557d..3cf82fa15 100644
--- a/l10n/lv.js
+++ b/l10n/lv.js
@@ -205,9 +205,6 @@ OC.L10N.register(
"Chat" : "Tērzēt",
"Details" : "Detaļas",
"Settings" : "Iestatījumi",
- "Files" : "Datnes",
- "Audio" : "Audio",
- "Other" : "Cits",
"Projects" : "Projekti",
"Privacy" : "Privātums",
"Keyboard shortcuts" : "Tastatūras saīsnes",
@@ -219,6 +216,9 @@ OC.L10N.register(
"Select conversation" : "Izvēlēties sarunu",
"Link to a conversation" : "Saite uz sarunu",
"Join a conversation or start a new one" : "Pievienoties sarunai vai sākt jaunu",
+ "Files" : "Datnes",
+ "Audio" : "Audio",
+ "Other" : "Cits",
"Default" : "Noklusējuma",
"This is taking longer than expected. Are the media permissions already granted (or rejected)? If yes please restart your browser, as audio and video are failing" : "Tas aizņem vairāk laika, nekā paredzēts. Vai multivides atļaujas jau ir piešķirtas (vai noraidītas)? Ja atbilde ir “jā”, restartējiet pārlūkprogrammu, jo ir audio un video kļūme",
"Access to microphone & camera is only possible with HTTPS" : "Piekļuve mikrofonam un kamerai ir iespējama izmantojot tikai HTTPS",
diff --git a/l10n/lv.json b/l10n/lv.json
index 4e39cfc48..9b1688df3 100644
--- a/l10n/lv.json
+++ b/l10n/lv.json
@@ -203,9 +203,6 @@
"Chat" : "Tērzēt",
"Details" : "Detaļas",
"Settings" : "Iestatījumi",
- "Files" : "Datnes",
- "Audio" : "Audio",
- "Other" : "Cits",
"Projects" : "Projekti",
"Privacy" : "Privātums",
"Keyboard shortcuts" : "Tastatūras saīsnes",
@@ -217,6 +214,9 @@
"Select conversation" : "Izvēlēties sarunu",
"Link to a conversation" : "Saite uz sarunu",
"Join a conversation or start a new one" : "Pievienoties sarunai vai sākt jaunu",
+ "Files" : "Datnes",
+ "Audio" : "Audio",
+ "Other" : "Cits",
"Default" : "Noklusējuma",
"This is taking longer than expected. Are the media permissions already granted (or rejected)? If yes please restart your browser, as audio and video are failing" : "Tas aizņem vairāk laika, nekā paredzēts. Vai multivides atļaujas jau ir piešķirtas (vai noraidītas)? Ja atbilde ir “jā”, restartējiet pārlūkprogrammu, jo ir audio un video kļūme",
"Access to microphone & camera is only possible with HTTPS" : "Piekļuve mikrofonam un kamerai ir iespējama izmantojot tikai HTTPS",
diff --git a/l10n/mk.js b/l10n/mk.js
index 28df02a5c..a0b16a083 100644
--- a/l10n/mk.js
+++ b/l10n/mk.js
@@ -667,12 +667,8 @@ OC.L10N.register(
"Chat" : "Разговор",
"Details" : "Детали",
"Settings" : "Параметри",
- "Files" : "Датотеки",
- "Locations" : "Локации",
- "Audio" : "Аудио",
- "Other" : "Останато",
- "Show all files" : "Прикажи ги сите датотеки",
"Projects" : "Проекти",
+ "Show all files" : "Прикажи ги сите датотеки",
"Attachments folder" : "Папка со прилози",
"Privacy" : "Приватност",
"Keyboard shortcuts" : "Кратенки преку тастатура",
@@ -707,6 +703,10 @@ OC.L10N.register(
"Access to microphone was denied" : "Нема пристап до микрофонот",
"Error while accessing microphone" : "Грешка при пристап до микрофон",
"Access to camera is only possible with HTTPS" : "Пристап до камерата е возможен само преку безбедноска врска HTTPS",
+ "Files" : "Датотеки",
+ "Locations" : "Локации",
+ "Audio" : "Аудио",
+ "Other" : "Останато",
"Do not disturb" : "Не вознемирувај",
"Away" : "Далеку",
"Error while sharing file" : "Грешка при споделување на датотека",
diff --git a/l10n/mk.json b/l10n/mk.json
index d854de8f8..93712a0c8 100644
--- a/l10n/mk.json
+++ b/l10n/mk.json
@@ -665,12 +665,8 @@
"Chat" : "Разговор",
"Details" : "Детали",
"Settings" : "Параметри",
- "Files" : "Датотеки",
- "Locations" : "Локации",
- "Audio" : "Аудио",
- "Other" : "Останато",
- "Show all files" : "Прикажи ги сите датотеки",
"Projects" : "Проекти",
+ "Show all files" : "Прикажи ги сите датотеки",
"Attachments folder" : "Папка со прилози",
"Privacy" : "Приватност",
"Keyboard shortcuts" : "Кратенки преку тастатура",
@@ -705,6 +701,10 @@
"Access to microphone was denied" : "Нема пристап до микрофонот",
"Error while accessing microphone" : "Грешка при пристап до микрофон",
"Access to camera is only possible with HTTPS" : "Пристап до камерата е возможен само преку безбедноска врска HTTPS",
+ "Files" : "Датотеки",
+ "Locations" : "Локации",
+ "Audio" : "Аудио",
+ "Other" : "Останато",
"Do not disturb" : "Не вознемирувај",
"Away" : "Далеку",
"Error while sharing file" : "Грешка при споделување на датотека",
diff --git a/l10n/nb.js b/l10n/nb.js
index 851e5a639..498479a92 100644
--- a/l10n/nb.js
+++ b/l10n/nb.js
@@ -537,13 +537,8 @@ OC.L10N.register(
"Chat" : "Sludre",
"Details" : "Detaljer",
"Settings" : "Innstillinger",
- "Media" : "Media",
- "Files" : "Filer",
- "Locations" : "Steder",
- "Audio" : "Lyd",
- "Other" : "Annet",
- "Show all files" : "Vis alle filer",
"Projects" : "Prosjekter",
+ "Show all files" : "Vis alle filer",
"Privacy" : "Personvern",
"Keyboard shortcuts" : "Tastatursnarveier",
"Search" : "Søk",
@@ -552,6 +547,11 @@ OC.L10N.register(
"Send" : "Send",
"Start a conversation" : "Start en samtale",
"Join a conversation or start a new one" : "Ta del i en samtale eller start en ny",
+ "Media" : "Media",
+ "Files" : "Filer",
+ "Locations" : "Steder",
+ "Audio" : "Lyd",
+ "Other" : "Annet",
"Do not disturb" : "Ikke forstyrr",
"Away" : "Borte",
"Default" : "Standard",
diff --git a/l10n/nb.json b/l10n/nb.json
index 2da19d00b..99264c03b 100644
--- a/l10n/nb.json
+++ b/l10n/nb.json
@@ -535,13 +535,8 @@
"Chat" : "Sludre",
"Details" : "Detaljer",
"Settings" : "Innstillinger",
- "Media" : "Media",
- "Files" : "Filer",
- "Locations" : "Steder",
- "Audio" : "Lyd",
- "Other" : "Annet",
- "Show all files" : "Vis alle filer",
"Projects" : "Prosjekter",
+ "Show all files" : "Vis alle filer",
"Privacy" : "Personvern",
"Keyboard shortcuts" : "Tastatursnarveier",
"Search" : "Søk",
@@ -550,6 +545,11 @@
"Send" : "Send",
"Start a conversation" : "Start en samtale",
"Join a conversation or start a new one" : "Ta del i en samtale eller start en ny",
+ "Media" : "Media",
+ "Files" : "Filer",
+ "Locations" : "Steder",
+ "Audio" : "Lyd",
+ "Other" : "Annet",
"Do not disturb" : "Ikke forstyrr",
"Away" : "Borte",
"Default" : "Standard",
diff --git a/l10n/nl.js b/l10n/nl.js
index 3ba30f762..dd2713f65 100644
--- a/l10n/nl.js
+++ b/l10n/nl.js
@@ -1028,13 +1028,8 @@ OC.L10N.register(
"Details" : "Details",
"Settings" : "Instellingen",
"Participants ({count})" : "Deelnemers ({count})",
- "Media" : "Media",
- "Files" : "Bestanden",
- "Locations" : "Locaties",
- "Audio" : "Geluid",
- "Other" : "Andere",
- "Show all files" : "Toon alle bestanden",
"Projects" : "Projecten",
+ "Show all files" : "Toon alle bestanden",
"Meeting ID: {meetingId}" : "Meeting ID: {meetingId}",
"Your PIN: {attendeePin}" : "Je PIN: {attendeePin}",
"Attachments folder" : "Bijlagemap",
@@ -1103,6 +1098,11 @@ OC.L10N.register(
"Error while accessing microphone" : "Fout bij toegang tot microfoon",
"Access to camera is only possible with HTTPS" : "Toegang tot de camera is alleen mogelijk met HTTPS",
"An error occurred while fetching the participants" : "Er trad een fout op bij het ophalen van de deelnemers",
+ "Media" : "Media",
+ "Files" : "Bestanden",
+ "Locations" : "Locaties",
+ "Audio" : "Geluid",
+ "Other" : "Andere",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk is bijgewerkt, herlaad de pagina",
"Do not disturb" : "Niet storen",
"Away" : "Afwezig",
diff --git a/l10n/nl.json b/l10n/nl.json
index 645a3a7ea..c41eecfa4 100644
--- a/l10n/nl.json
+++ b/l10n/nl.json
@@ -1026,13 +1026,8 @@
"Details" : "Details",
"Settings" : "Instellingen",
"Participants ({count})" : "Deelnemers ({count})",
- "Media" : "Media",
- "Files" : "Bestanden",
- "Locations" : "Locaties",
- "Audio" : "Geluid",
- "Other" : "Andere",
- "Show all files" : "Toon alle bestanden",
"Projects" : "Projecten",
+ "Show all files" : "Toon alle bestanden",
"Meeting ID: {meetingId}" : "Meeting ID: {meetingId}",
"Your PIN: {attendeePin}" : "Je PIN: {attendeePin}",
"Attachments folder" : "Bijlagemap",
@@ -1101,6 +1096,11 @@
"Error while accessing microphone" : "Fout bij toegang tot microfoon",
"Access to camera is only possible with HTTPS" : "Toegang tot de camera is alleen mogelijk met HTTPS",
"An error occurred while fetching the participants" : "Er trad een fout op bij het ophalen van de deelnemers",
+ "Media" : "Media",
+ "Files" : "Bestanden",
+ "Locations" : "Locaties",
+ "Audio" : "Geluid",
+ "Other" : "Andere",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk is bijgewerkt, herlaad de pagina",
"Do not disturb" : "Niet storen",
"Away" : "Afwezig",
diff --git a/l10n/nn_NO.js b/l10n/nn_NO.js
index 18d95bd44..1dbb19f21 100644
--- a/l10n/nn_NO.js
+++ b/l10n/nn_NO.js
@@ -10,6 +10,7 @@ OC.L10N.register(
"An error occurred. Please contact your admin." : "Det oppstod ein feil. Ver venleg og kontakt systemadministrator.",
"Open settings" : "Opne instillingar",
"Messages" : "Meldingar",
+ "Everyone" : "Alle",
"None" : "Ingen",
"User" : "Bruker",
"Users" : "Brukarare",
@@ -67,8 +68,6 @@ OC.L10N.register(
"Chat" : "Lynmelding",
"Details" : "Detaljar",
"Settings" : "Instillingar",
- "Files" : "Filer",
- "Other" : "Anna",
"Privacy" : "Personvern",
"Keyboard shortcuts" : "Tastatursnarvegar",
"Search" : "Search",
@@ -76,6 +75,8 @@ OC.L10N.register(
"Send" : "Send",
"Say hi to your friends and colleagues!" : "Sei hei til dine venner og kollegar!",
"Join a conversation or start a new one" : "Delta i ein samtale eller start ein ny ein",
+ "Files" : "Filer",
+ "Other" : "Anna",
"Away" : "Borte",
"Default" : "Standard",
"The password is wrong. Try again." : "Passordet er gale. Prøv igjen.",
diff --git a/l10n/nn_NO.json b/l10n/nn_NO.json
index 1e4cb4da3..16b3b58b5 100644
--- a/l10n/nn_NO.json
+++ b/l10n/nn_NO.json
@@ -8,6 +8,7 @@
"An error occurred. Please contact your admin." : "Det oppstod ein feil. Ver venleg og kontakt systemadministrator.",
"Open settings" : "Opne instillingar",
"Messages" : "Meldingar",
+ "Everyone" : "Alle",
"None" : "Ingen",
"User" : "Bruker",
"Users" : "Brukarare",
@@ -65,8 +66,6 @@
"Chat" : "Lynmelding",
"Details" : "Detaljar",
"Settings" : "Instillingar",
- "Files" : "Filer",
- "Other" : "Anna",
"Privacy" : "Personvern",
"Keyboard shortcuts" : "Tastatursnarvegar",
"Search" : "Search",
@@ -74,6 +73,8 @@
"Send" : "Send",
"Say hi to your friends and colleagues!" : "Sei hei til dine venner og kollegar!",
"Join a conversation or start a new one" : "Delta i ein samtale eller start ein ny ein",
+ "Files" : "Filer",
+ "Other" : "Anna",
"Away" : "Borte",
"Default" : "Standard",
"The password is wrong. Try again." : "Passordet er gale. Prøv igjen.",
diff --git a/l10n/oc.js b/l10n/oc.js
index 1ef051bb7..700af8262 100644
--- a/l10n/oc.js
+++ b/l10n/oc.js
@@ -920,19 +920,13 @@ OC.L10N.register(
"Settings" : "Paramètres",
"Shared items" : "Elements partejats",
"Participants ({count})" : "Participants ({count})",
- "Media" : "Mèdia",
- "Files" : "Fichièrs",
- "Voice messages" : "Messatges àudio",
- "Locations" : "Emplaçaments",
- "Audio" : "Àudio",
- "Other" : "Autre",
+ "Projects" : "Projèctes",
"Show all media" : "Veire totes los mèdia",
"Show all files" : "Veire totes los fichièrs",
"Show all voice messages" : "Veire totes los messatges àudio",
"Show all locations" : "Veire totes los emplaçaments",
"Show all audio" : "Veire totes los àudios",
"Show all other" : "Veire tota la rèsta",
- "Projects" : "Projèctes",
"Meeting ID: {meetingId}" : "ID reünion : {meetingId}",
"Your PIN: {attendeePin}" : "Vòstre PIN : {attendeePin}",
"Display name: <strong>{name}</strong>" : "Nom d’afichatge : <strong>{name}</strong>",
@@ -996,6 +990,12 @@ OC.L10N.register(
"Error while accessing microphone" : "Error pendent l’accès al microfòn",
"Access to camera is only possible with HTTPS" : "L’accès a la camèra es sonque possibla amb HTTPS",
"An error occurred while fetching the participants" : "Una error s’es producha en recuperant los participants",
+ "Media" : "Mèdia",
+ "Files" : "Fichièrs",
+ "Voice messages" : "Messatges àudio",
+ "Locations" : "Emplaçaments",
+ "Audio" : "Àudio",
+ "Other" : "Autre",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk foguèt mes a jorn, mercés de tornar cargar la pagina",
"Do not disturb" : "Me desrengar pas",
"Away" : "Absent",
diff --git a/l10n/oc.json b/l10n/oc.json
index ec7743fde..97576c429 100644
--- a/l10n/oc.json
+++ b/l10n/oc.json
@@ -918,19 +918,13 @@
"Settings" : "Paramètres",
"Shared items" : "Elements partejats",
"Participants ({count})" : "Participants ({count})",
- "Media" : "Mèdia",
- "Files" : "Fichièrs",
- "Voice messages" : "Messatges àudio",
- "Locations" : "Emplaçaments",
- "Audio" : "Àudio",
- "Other" : "Autre",
+ "Projects" : "Projèctes",
"Show all media" : "Veire totes los mèdia",
"Show all files" : "Veire totes los fichièrs",
"Show all voice messages" : "Veire totes los messatges àudio",
"Show all locations" : "Veire totes los emplaçaments",
"Show all audio" : "Veire totes los àudios",
"Show all other" : "Veire tota la rèsta",
- "Projects" : "Projèctes",
"Meeting ID: {meetingId}" : "ID reünion : {meetingId}",
"Your PIN: {attendeePin}" : "Vòstre PIN : {attendeePin}",
"Display name: <strong>{name}</strong>" : "Nom d’afichatge : <strong>{name}</strong>",
@@ -994,6 +988,12 @@
"Error while accessing microphone" : "Error pendent l’accès al microfòn",
"Access to camera is only possible with HTTPS" : "L’accès a la camèra es sonque possibla amb HTTPS",
"An error occurred while fetching the participants" : "Una error s’es producha en recuperant los participants",
+ "Media" : "Mèdia",
+ "Files" : "Fichièrs",
+ "Voice messages" : "Messatges àudio",
+ "Locations" : "Emplaçaments",
+ "Audio" : "Àudio",
+ "Other" : "Autre",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk foguèt mes a jorn, mercés de tornar cargar la pagina",
"Do not disturb" : "Me desrengar pas",
"Away" : "Absent",
diff --git a/l10n/pl.js b/l10n/pl.js
index de80f1ea0..53d2a0526 100644
--- a/l10n/pl.js
+++ b/l10n/pl.js
@@ -1039,13 +1039,7 @@ OC.L10N.register(
"Settings" : "Ustawienia",
"Shared items" : "Udostępnione elementy",
"Participants ({count})" : "Uczestnicy ({count})",
- "Media" : "Multimedia",
- "Files" : "Pliki",
- "Deck cards" : "Karty tablicy",
- "Voice messages" : "Wiadomości głosowe",
- "Locations" : "Lokalizacje",
- "Audio" : "Audio",
- "Other" : "Inne",
+ "Projects" : "Projekty",
"Show all media" : "Pokaż wszystkie multimedia",
"Show all files" : "Pokaż wszystkie pliki",
"Show all deck cards" : "Pokaż wszystkie karty tablicy",
@@ -1053,7 +1047,6 @@ OC.L10N.register(
"Show all locations" : "Pokaż wszystkie lokalizacje",
"Show all audio" : "Pokaż wszystkie audio",
"Show all other" : "Pokaż wszystkie inne",
- "Projects" : "Projekty",
"Meeting ID: {meetingId}" : "ID spotkania: {meetingId}",
"Your PIN: {attendeePin}" : "Twój kod PIN: {attendeePin}",
"Display name: <strong>{name}</strong>" : "Wyświetlana nazwa: <strong>{name}</strong>",
@@ -1063,6 +1056,7 @@ OC.L10N.register(
"Sounds" : "Dźwięki",
"Play sounds when participants join or leave a call" : "Odtwarzaj dźwięki, gdy uczestnicy dołączają lub opuszczają połączenie",
"Sounds can currently not be played in Safari browser and iPad and iPhone devices due to technical restrictions by the manufacturer." : "Obecnie nie można odtwarzać dźwięków w przeglądarce Safari oraz na urządzeniach iPad i iPhone ze względu na ograniczenia techniczne producenta.",
+ "Sounds for chat and call notifications can be adjusted in the personal settings." : "Dźwięki powiadomień dla czatu i połączeń można dostosować w ustawieniach osobistych.",
"Keyboard shortcuts" : "Skróty klawiaturowe",
"Speed up your Talk experience with these quick shortcuts." : "Przyspiesz korzystanie z Talka dzięki szybkim skrótom.",
"Focus the chat input" : "Skoncentruj się na rozmowie",
@@ -1123,6 +1117,13 @@ OC.L10N.register(
"Error while accessing microphone" : "Błąd podczas uzyskiwania dostępu do mikrofonu",
"Access to camera is only possible with HTTPS" : "Dostęp do kamery jest możliwy tylko przy użyciu protokołu HTTPS",
"An error occurred while fetching the participants" : "Wystąpił błąd podczas pobierania uczestników",
+ "Media" : "Multimedia",
+ "Files" : "Pliki",
+ "Deck cards" : "Karty tablicy",
+ "Voice messages" : "Wiadomości głosowe",
+ "Locations" : "Lokalizacje",
+ "Audio" : "Audio",
+ "Other" : "Inne",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk został zaktualizowany, przeładuj stronę",
"Do not disturb" : "Nie przeszkadzać",
"Away" : "Bezczynny",
@@ -1131,6 +1132,7 @@ OC.L10N.register(
"Not enough free space to upload file \"{fileName}\"" : "Za mało wolnego miejsca, aby wysłać plik \"{fileName}\"",
"Error while uploading file \"{fileName}\"" : "Błąd podczas wysyłania pliku \"{fileName}\"",
"An error happened when trying to share your file" : "Wystąpił błąd podczas udostępnienia tego pliku",
+ "{guest} (guest)" : "{guest} (gość)",
"Could not post message: {errorMessage}" : "Nie można wysłać wiadomości: {errorMessage}",
"Failed to add reaction" : "Nie udało się dodać reakcji",
"Failed to remove reaction" : "Nie udało się usunąć reakcji",
diff --git a/l10n/pl.json b/l10n/pl.json
index 4fca4484a..3a414558d 100644
--- a/l10n/pl.json
+++ b/l10n/pl.json
@@ -1037,13 +1037,7 @@
"Settings" : "Ustawienia",
"Shared items" : "Udostępnione elementy",
"Participants ({count})" : "Uczestnicy ({count})",
- "Media" : "Multimedia",
- "Files" : "Pliki",
- "Deck cards" : "Karty tablicy",
- "Voice messages" : "Wiadomości głosowe",
- "Locations" : "Lokalizacje",
- "Audio" : "Audio",
- "Other" : "Inne",
+ "Projects" : "Projekty",
"Show all media" : "Pokaż wszystkie multimedia",
"Show all files" : "Pokaż wszystkie pliki",
"Show all deck cards" : "Pokaż wszystkie karty tablicy",
@@ -1051,7 +1045,6 @@
"Show all locations" : "Pokaż wszystkie lokalizacje",
"Show all audio" : "Pokaż wszystkie audio",
"Show all other" : "Pokaż wszystkie inne",
- "Projects" : "Projekty",
"Meeting ID: {meetingId}" : "ID spotkania: {meetingId}",
"Your PIN: {attendeePin}" : "Twój kod PIN: {attendeePin}",
"Display name: <strong>{name}</strong>" : "Wyświetlana nazwa: <strong>{name}</strong>",
@@ -1061,6 +1054,7 @@
"Sounds" : "Dźwięki",
"Play sounds when participants join or leave a call" : "Odtwarzaj dźwięki, gdy uczestnicy dołączają lub opuszczają połączenie",
"Sounds can currently not be played in Safari browser and iPad and iPhone devices due to technical restrictions by the manufacturer." : "Obecnie nie można odtwarzać dźwięków w przeglądarce Safari oraz na urządzeniach iPad i iPhone ze względu na ograniczenia techniczne producenta.",
+ "Sounds for chat and call notifications can be adjusted in the personal settings." : "Dźwięki powiadomień dla czatu i połączeń można dostosować w ustawieniach osobistych.",
"Keyboard shortcuts" : "Skróty klawiaturowe",
"Speed up your Talk experience with these quick shortcuts." : "Przyspiesz korzystanie z Talka dzięki szybkim skrótom.",
"Focus the chat input" : "Skoncentruj się na rozmowie",
@@ -1121,6 +1115,13 @@
"Error while accessing microphone" : "Błąd podczas uzyskiwania dostępu do mikrofonu",
"Access to camera is only possible with HTTPS" : "Dostęp do kamery jest możliwy tylko przy użyciu protokołu HTTPS",
"An error occurred while fetching the participants" : "Wystąpił błąd podczas pobierania uczestników",
+ "Media" : "Multimedia",
+ "Files" : "Pliki",
+ "Deck cards" : "Karty tablicy",
+ "Voice messages" : "Wiadomości głosowe",
+ "Locations" : "Lokalizacje",
+ "Audio" : "Audio",
+ "Other" : "Inne",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk został zaktualizowany, przeładuj stronę",
"Do not disturb" : "Nie przeszkadzać",
"Away" : "Bezczynny",
@@ -1129,6 +1130,7 @@
"Not enough free space to upload file \"{fileName}\"" : "Za mało wolnego miejsca, aby wysłać plik \"{fileName}\"",
"Error while uploading file \"{fileName}\"" : "Błąd podczas wysyłania pliku \"{fileName}\"",
"An error happened when trying to share your file" : "Wystąpił błąd podczas udostępnienia tego pliku",
+ "{guest} (guest)" : "{guest} (gość)",
"Could not post message: {errorMessage}" : "Nie można wysłać wiadomości: {errorMessage}",
"Failed to add reaction" : "Nie udało się dodać reakcji",
"Failed to remove reaction" : "Nie udało się usunąć reakcji",
diff --git a/l10n/pt_BR.js b/l10n/pt_BR.js
index 30734c464..5cf2db172 100644
--- a/l10n/pt_BR.js
+++ b/l10n/pt_BR.js
@@ -1039,13 +1039,7 @@ OC.L10N.register(
"Settings" : "Configurações",
"Shared items" : "Itens compartilhados",
"Participants ({count})" : "Participantes ({count}) ",
- "Media" : "Meios de comunicação",
- "Files" : "Arquivos",
- "Deck cards" : "Cartas de baralho",
- "Voice messages" : "Mensagens de voz",
- "Locations" : "Localizações",
- "Audio" : "Áudio",
- "Other" : "Outro",
+ "Projects" : "Projetos",
"Show all media" : "Mostrar todas as mídias",
"Show all files" : "Mostrar todos os arquivos",
"Show all deck cards" : "Mostrar todas as cartas de baralho",
@@ -1053,7 +1047,6 @@ OC.L10N.register(
"Show all locations" : "Mostrar todos os locais",
"Show all audio" : "Mostrar todo o áudio",
"Show all other" : "Mostrar todos os outros",
- "Projects" : "Projetos",
"Meeting ID: {meetingId}" : "ID da reunião: {meetingId}",
"Your PIN: {attendeePin}" : "Seu PIN: {attendeePin}",
"Display name: <strong>{name}</strong>" : "Mostrar nome: <strong>{name}</strong>",
@@ -1123,6 +1116,13 @@ OC.L10N.register(
"Error while accessing microphone" : "Erro ao acessar microfone",
"Access to camera is only possible with HTTPS" : "O acesso à câmera somente é possível via HTTPS",
"An error occurred while fetching the participants" : "Erro ao buscar os participantes",
+ "Media" : "Meios de comunicação",
+ "Files" : "Arquivos",
+ "Deck cards" : "Cartas de baralho",
+ "Voice messages" : "Mensagens de voz",
+ "Locations" : "Localizações",
+ "Audio" : "Áudio",
+ "Other" : "Outro",
"Nextcloud Talk was updated, please reload the page" : "O Nextcloud Talk foi atualizado, recarregue a página",
"Do not disturb" : "Não perturbe",
"Away" : "Fora",
diff --git a/l10n/pt_BR.json b/l10n/pt_BR.json
index 3f472b044..4ac6b42b9 100644
--- a/l10n/pt_BR.json
+++ b/l10n/pt_BR.json
@@ -1037,13 +1037,7 @@
"Settings" : "Configurações",
"Shared items" : "Itens compartilhados",
"Participants ({count})" : "Participantes ({count}) ",
- "Media" : "Meios de comunicação",
- "Files" : "Arquivos",
- "Deck cards" : "Cartas de baralho",
- "Voice messages" : "Mensagens de voz",
- "Locations" : "Localizações",
- "Audio" : "Áudio",
- "Other" : "Outro",
+ "Projects" : "Projetos",
"Show all media" : "Mostrar todas as mídias",
"Show all files" : "Mostrar todos os arquivos",
"Show all deck cards" : "Mostrar todas as cartas de baralho",
@@ -1051,7 +1045,6 @@
"Show all locations" : "Mostrar todos os locais",
"Show all audio" : "Mostrar todo o áudio",
"Show all other" : "Mostrar todos os outros",
- "Projects" : "Projetos",
"Meeting ID: {meetingId}" : "ID da reunião: {meetingId}",
"Your PIN: {attendeePin}" : "Seu PIN: {attendeePin}",
"Display name: <strong>{name}</strong>" : "Mostrar nome: <strong>{name}</strong>",
@@ -1121,6 +1114,13 @@
"Error while accessing microphone" : "Erro ao acessar microfone",
"Access to camera is only possible with HTTPS" : "O acesso à câmera somente é possível via HTTPS",
"An error occurred while fetching the participants" : "Erro ao buscar os participantes",
+ "Media" : "Meios de comunicação",
+ "Files" : "Arquivos",
+ "Deck cards" : "Cartas de baralho",
+ "Voice messages" : "Mensagens de voz",
+ "Locations" : "Localizações",
+ "Audio" : "Áudio",
+ "Other" : "Outro",
"Nextcloud Talk was updated, please reload the page" : "O Nextcloud Talk foi atualizado, recarregue a página",
"Do not disturb" : "Não perturbe",
"Away" : "Fora",
diff --git a/l10n/pt_PT.js b/l10n/pt_PT.js
index a36175a23..e69f0c2a6 100644
--- a/l10n/pt_PT.js
+++ b/l10n/pt_PT.js
@@ -297,17 +297,17 @@ OC.L10N.register(
"Chat" : "Conversa",
"Details" : "Detalhes",
"Settings" : "Definições",
- "Media" : "Media",
- "Files" : "Ficheiros",
- "Locations" : "Localizações",
- "Audio" : "Áudio",
- "Other" : "Outro",
"Privacy" : "Privacidade",
"Keyboard shortcuts" : "Atalhos de teclado",
"Search" : "Pesquisa sobre",
"Rename conversation" : "Renomear conversação",
"Send" : "Enviar",
"Start a conversation" : "Iniciar uma conversação",
+ "Media" : "Media",
+ "Files" : "Ficheiros",
+ "Locations" : "Localizações",
+ "Audio" : "Áudio",
+ "Other" : "Outro",
"Do not disturb" : "Não incomodar",
"Away" : "Ausente",
"Error while sharing file" : "Erro ao partilhar ficheiro",
diff --git a/l10n/pt_PT.json b/l10n/pt_PT.json
index ebd42f344..155f08eb5 100644
--- a/l10n/pt_PT.json
+++ b/l10n/pt_PT.json
@@ -295,17 +295,17 @@
"Chat" : "Conversa",
"Details" : "Detalhes",
"Settings" : "Definições",
- "Media" : "Media",
- "Files" : "Ficheiros",
- "Locations" : "Localizações",
- "Audio" : "Áudio",
- "Other" : "Outro",
"Privacy" : "Privacidade",
"Keyboard shortcuts" : "Atalhos de teclado",
"Search" : "Pesquisa sobre",
"Rename conversation" : "Renomear conversação",
"Send" : "Enviar",
"Start a conversation" : "Iniciar uma conversação",
+ "Media" : "Media",
+ "Files" : "Ficheiros",
+ "Locations" : "Localizações",
+ "Audio" : "Áudio",
+ "Other" : "Outro",
"Do not disturb" : "Não incomodar",
"Away" : "Ausente",
"Error while sharing file" : "Erro ao partilhar ficheiro",
diff --git a/l10n/ro.js b/l10n/ro.js
index fd3c302f9..698e60151 100644
--- a/l10n/ro.js
+++ b/l10n/ro.js
@@ -299,16 +299,16 @@ OC.L10N.register(
"Add users or groups" : "Add users or groups",
"Details" : "Detalii",
"Settings" : "Setări",
- "Media" : "Media",
- "Files" : "Fișiere",
- "Audio" : "Audio",
- "Other" : "Altele",
- "Show all files" : "Arată toate fișierele",
"Projects" : "Proiecte",
+ "Show all files" : "Arată toate fișierele",
"Privacy" : "Confindențialitate",
"Keyboard shortcuts" : "Scurtături din tastatură",
"Search" : "Caută",
"Send" : "Trimite",
+ "Media" : "Media",
+ "Files" : "Fișiere",
+ "Audio" : "Audio",
+ "Other" : "Altele",
"Do not disturb" : "Nu deranja",
"Away" : "Plecat",
"Default" : "Implicit",
diff --git a/l10n/ro.json b/l10n/ro.json
index a05270748..60fbfd2fa 100644
--- a/l10n/ro.json
+++ b/l10n/ro.json
@@ -297,16 +297,16 @@
"Add users or groups" : "Add users or groups",
"Details" : "Detalii",
"Settings" : "Setări",
- "Media" : "Media",
- "Files" : "Fișiere",
- "Audio" : "Audio",
- "Other" : "Altele",
- "Show all files" : "Arată toate fișierele",
"Projects" : "Proiecte",
+ "Show all files" : "Arată toate fișierele",
"Privacy" : "Confindențialitate",
"Keyboard shortcuts" : "Scurtături din tastatură",
"Search" : "Caută",
"Send" : "Trimite",
+ "Media" : "Media",
+ "Files" : "Fișiere",
+ "Audio" : "Audio",
+ "Other" : "Altele",
"Do not disturb" : "Nu deranja",
"Away" : "Plecat",
"Default" : "Implicit",
diff --git a/l10n/ru.js b/l10n/ru.js
index c3fe5458c..a283fb35c 100644
--- a/l10n/ru.js
+++ b/l10n/ru.js
@@ -1028,13 +1028,8 @@ OC.L10N.register(
"Details" : "Дополнительно",
"Settings" : "Настройки",
"Participants ({count})" : "Участники ({count})",
- "Media" : "Медиа",
- "Files" : "Файлы",
- "Locations" : "Места",
- "Audio" : "Звук",
- "Other" : "Другое",
- "Show all files" : "Показать всё файлы",
"Projects" : "Проекты",
+ "Show all files" : "Показать всё файлы",
"Meeting ID: {meetingId}" : "ID встречи: {meetingId}",
"Your PIN: {attendeePin}" : "Ваш PIN: {attendeePin}",
"Attachments folder" : "Папка для вложений",
@@ -1103,6 +1098,11 @@ OC.L10N.register(
"Error while accessing microphone" : "Не удалось получить доступ к микрофону",
"Access to camera is only possible with HTTPS" : "Доступ к камере разрешён только при использовании протокола HTTPS",
"An error occurred while fetching the participants" : "Не удалось получить список участников",
+ "Media" : "Медиа",
+ "Files" : "Файлы",
+ "Locations" : "Места",
+ "Audio" : "Звук",
+ "Other" : "Другое",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk обновлен, пожалуйста обновите страницу",
"Do not disturb" : "Не беспокоить",
"Away" : "Отсутствует",
diff --git a/l10n/ru.json b/l10n/ru.json
index 12cddb62d..28f83720a 100644
--- a/l10n/ru.json
+++ b/l10n/ru.json
@@ -1026,13 +1026,8 @@
"Details" : "Дополнительно",
"Settings" : "Настройки",
"Participants ({count})" : "Участники ({count})",
- "Media" : "Медиа",
- "Files" : "Файлы",
- "Locations" : "Места",
- "Audio" : "Звук",
- "Other" : "Другое",
- "Show all files" : "Показать всё файлы",
"Projects" : "Проекты",
+ "Show all files" : "Показать всё файлы",
"Meeting ID: {meetingId}" : "ID встречи: {meetingId}",
"Your PIN: {attendeePin}" : "Ваш PIN: {attendeePin}",
"Attachments folder" : "Папка для вложений",
@@ -1101,6 +1096,11 @@
"Error while accessing microphone" : "Не удалось получить доступ к микрофону",
"Access to camera is only possible with HTTPS" : "Доступ к камере разрешён только при использовании протокола HTTPS",
"An error occurred while fetching the participants" : "Не удалось получить список участников",
+ "Media" : "Медиа",
+ "Files" : "Файлы",
+ "Locations" : "Места",
+ "Audio" : "Звук",
+ "Other" : "Другое",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk обновлен, пожалуйста обновите страницу",
"Do not disturb" : "Не беспокоить",
"Away" : "Отсутствует",
diff --git a/l10n/sc.js b/l10n/sc.js
index 28db58bf2..53b405e05 100644
--- a/l10n/sc.js
+++ b/l10n/sc.js
@@ -949,13 +949,8 @@ OC.L10N.register(
"Chat" : "Tzarrada",
"Details" : "Detàllios",
"Settings" : "Impostatziones",
- "Media" : "Media",
- "Files" : "Archìvios",
- "Locations" : "Positziones",
- "Audio" : "Àudio",
- "Other" : "Àteru",
- "Show all files" : "Mustra totu is documentos",
"Projects" : "Progetos",
+ "Show all files" : "Mustra totu is documentos",
"Meeting ID: {meetingId}" : "ID de s'atòbiu : {meetingId}",
"Your PIN: {attendeePin}" : "Su PIN tuo: {attendeePin}",
"Attachments folder" : "Cartella de alligongiados",
@@ -1022,6 +1017,11 @@ OC.L10N.register(
"Error while accessing microphone" : "Errore in s'atzessu a su micròfonu",
"Access to camera is only possible with HTTPS" : "S'atzessu a sa càmera si podet fàghere isceti cun HTTPS",
"An error occurred while fetching the participants" : "B'at àpidu un'errore in su recùperu de is partetzipantes",
+ "Media" : "Media",
+ "Files" : "Archìvios",
+ "Locations" : "Positziones",
+ "Audio" : "Àudio",
+ "Other" : "Àteru",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud est istadu agiornadu, torra a carrigare sa pàgina",
"Do not disturb" : "No istorbes",
"Away" : "Foras",
diff --git a/l10n/sc.json b/l10n/sc.json
index 21c6213c6..7e9f4946b 100644
--- a/l10n/sc.json
+++ b/l10n/sc.json
@@ -947,13 +947,8 @@
"Chat" : "Tzarrada",
"Details" : "Detàllios",
"Settings" : "Impostatziones",
- "Media" : "Media",
- "Files" : "Archìvios",
- "Locations" : "Positziones",
- "Audio" : "Àudio",
- "Other" : "Àteru",
- "Show all files" : "Mustra totu is documentos",
"Projects" : "Progetos",
+ "Show all files" : "Mustra totu is documentos",
"Meeting ID: {meetingId}" : "ID de s'atòbiu : {meetingId}",
"Your PIN: {attendeePin}" : "Su PIN tuo: {attendeePin}",
"Attachments folder" : "Cartella de alligongiados",
@@ -1020,6 +1015,11 @@
"Error while accessing microphone" : "Errore in s'atzessu a su micròfonu",
"Access to camera is only possible with HTTPS" : "S'atzessu a sa càmera si podet fàghere isceti cun HTTPS",
"An error occurred while fetching the participants" : "B'at àpidu un'errore in su recùperu de is partetzipantes",
+ "Media" : "Media",
+ "Files" : "Archìvios",
+ "Locations" : "Positziones",
+ "Audio" : "Àudio",
+ "Other" : "Àteru",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud est istadu agiornadu, torra a carrigare sa pàgina",
"Do not disturb" : "No istorbes",
"Away" : "Foras",
diff --git a/l10n/sk.js b/l10n/sk.js
index 617d3e1ef..e7dde36b9 100644
--- a/l10n/sk.js
+++ b/l10n/sk.js
@@ -1038,13 +1038,8 @@ OC.L10N.register(
"Details" : "Podrobnosti",
"Settings" : "Nastavenia",
"Participants ({count})" : "({count}) účastníkov",
- "Media" : "Média",
- "Files" : "Súbory",
- "Locations" : "Umiestnenie",
- "Audio" : "Zvuk",
- "Other" : "Iné",
- "Show all files" : "Zobraziť všetky súbory",
"Projects" : "Projekty",
+ "Show all files" : "Zobraziť všetky súbory",
"Meeting ID: {meetingId}" : "ID schôdzky: {meetingId}",
"Your PIN: {attendeePin}" : "Váš PIN: {attendeePin}",
"Attachments folder" : "Priečinok s prílohami",
@@ -1113,6 +1108,11 @@ OC.L10N.register(
"Error while accessing microphone" : "Nastala chyba pri prístupe k mikrofónu",
"Access to camera is only possible with HTTPS" : "Prístup ku kamere je možný iba pomocou protokolu HTTPS",
"An error occurred while fetching the participants" : "Pri načítavaní účastníkov sa vyskytla chyba",
+ "Media" : "Média",
+ "Files" : "Súbory",
+ "Locations" : "Umiestnenie",
+ "Audio" : "Zvuk",
+ "Other" : "Iné",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk bolo aktualizované, znova načítajte stránku",
"Do not disturb" : "Nerušiť",
"Away" : "Preč",
diff --git a/l10n/sk.json b/l10n/sk.json
index c78aad1fd..3319b4233 100644
--- a/l10n/sk.json
+++ b/l10n/sk.json
@@ -1036,13 +1036,8 @@
"Details" : "Podrobnosti",
"Settings" : "Nastavenia",
"Participants ({count})" : "({count}) účastníkov",
- "Media" : "Média",
- "Files" : "Súbory",
- "Locations" : "Umiestnenie",
- "Audio" : "Zvuk",
- "Other" : "Iné",
- "Show all files" : "Zobraziť všetky súbory",
"Projects" : "Projekty",
+ "Show all files" : "Zobraziť všetky súbory",
"Meeting ID: {meetingId}" : "ID schôdzky: {meetingId}",
"Your PIN: {attendeePin}" : "Váš PIN: {attendeePin}",
"Attachments folder" : "Priečinok s prílohami",
@@ -1111,6 +1106,11 @@
"Error while accessing microphone" : "Nastala chyba pri prístupe k mikrofónu",
"Access to camera is only possible with HTTPS" : "Prístup ku kamere je možný iba pomocou protokolu HTTPS",
"An error occurred while fetching the participants" : "Pri načítavaní účastníkov sa vyskytla chyba",
+ "Media" : "Média",
+ "Files" : "Súbory",
+ "Locations" : "Umiestnenie",
+ "Audio" : "Zvuk",
+ "Other" : "Iné",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk bolo aktualizované, znova načítajte stránku",
"Do not disturb" : "Nerušiť",
"Away" : "Preč",
diff --git a/l10n/sl.js b/l10n/sl.js
index 31e946788..483d8c746 100644
--- a/l10n/sl.js
+++ b/l10n/sl.js
@@ -998,13 +998,8 @@ OC.L10N.register(
"Details" : "Podrobnosti",
"Settings" : "Nastavitve",
"Participants ({count})" : "Udeleženci ({count})",
- "Media" : "Predstavna vsebina",
- "Files" : "Datoteke",
- "Locations" : "Mesta",
- "Audio" : "Zvok",
- "Other" : "Drugo",
- "Show all files" : "Pokaži vse datoteke",
"Projects" : "Projekti",
+ "Show all files" : "Pokaži vse datoteke",
"Meeting ID: {meetingId}" : "ID Sestanka: {meetingId}",
"Your PIN: {attendeePin}" : "Koda PIN: {attendeePin}",
"Attachments folder" : "Mapa prilog",
@@ -1072,6 +1067,11 @@ OC.L10N.register(
"Error while accessing microphone" : "Napaka med dostopom do mikrofona",
"Access to camera is only possible with HTTPS" : "Dostop do kamere je mogoče le prek protokola HTTPS",
"An error occurred while fetching the participants" : "Med pridobivanjem udeležencev je prišlo do napake",
+ "Media" : "Predstavna vsebina",
+ "Files" : "Datoteke",
+ "Locations" : "Mesta",
+ "Audio" : "Zvok",
+ "Other" : "Drugo",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk je bil posodobljen, ponovno je treba naložiti stran",
"Do not disturb" : "Ne pustim se motiti",
"Away" : "Trenutno ne spremljam",
diff --git a/l10n/sl.json b/l10n/sl.json
index ddcf7e048..fe8502aeb 100644
--- a/l10n/sl.json
+++ b/l10n/sl.json
@@ -996,13 +996,8 @@
"Details" : "Podrobnosti",
"Settings" : "Nastavitve",
"Participants ({count})" : "Udeleženci ({count})",
- "Media" : "Predstavna vsebina",
- "Files" : "Datoteke",
- "Locations" : "Mesta",
- "Audio" : "Zvok",
- "Other" : "Drugo",
- "Show all files" : "Pokaži vse datoteke",
"Projects" : "Projekti",
+ "Show all files" : "Pokaži vse datoteke",
"Meeting ID: {meetingId}" : "ID Sestanka: {meetingId}",
"Your PIN: {attendeePin}" : "Koda PIN: {attendeePin}",
"Attachments folder" : "Mapa prilog",
@@ -1070,6 +1065,11 @@
"Error while accessing microphone" : "Napaka med dostopom do mikrofona",
"Access to camera is only possible with HTTPS" : "Dostop do kamere je mogoče le prek protokola HTTPS",
"An error occurred while fetching the participants" : "Med pridobivanjem udeležencev je prišlo do napake",
+ "Media" : "Predstavna vsebina",
+ "Files" : "Datoteke",
+ "Locations" : "Mesta",
+ "Audio" : "Zvok",
+ "Other" : "Drugo",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk je bil posodobljen, ponovno je treba naložiti stran",
"Do not disturb" : "Ne pustim se motiti",
"Away" : "Trenutno ne spremljam",
diff --git a/l10n/sq.js b/l10n/sq.js
index a85ef6905..4d96cefef 100644
--- a/l10n/sq.js
+++ b/l10n/sq.js
@@ -225,16 +225,16 @@ OC.L10N.register(
"Add users or groups" : "Add users or groups",
"Details" : "Detajet",
"Settings" : "Rregullimet",
- "Media" : "Media",
- "Files" : "Skedarët",
- "Audio" : "Audio",
- "Other" : "Tjetër",
"Privacy" : "Privatësia",
"Keyboard shortcuts" : "Shkurtoret e tastierës",
"Search" : "Kërko",
"Rename conversation" : "Riemërtojeni bisedën",
"Send" : "Dërgo",
"Start a conversation" : "Filloni një bisedë",
+ "Media" : "Media",
+ "Files" : "Skedarët",
+ "Audio" : "Audio",
+ "Other" : "Tjetër",
"Default" : "E paracaktuar",
"Access to microphone & camera is only possible with HTTPS" : "Aksesi në mikrofon & kamera është i mundur vetëm me HTTPS",
"Access to microphone & camera was denied" : "Aksesi tek mikrofoni & kamera u mohua",
diff --git a/l10n/sq.json b/l10n/sq.json
index 7449903b2..9995f4ea2 100644
--- a/l10n/sq.json
+++ b/l10n/sq.json
@@ -223,16 +223,16 @@
"Add users or groups" : "Add users or groups",
"Details" : "Detajet",
"Settings" : "Rregullimet",
- "Media" : "Media",
- "Files" : "Skedarët",
- "Audio" : "Audio",
- "Other" : "Tjetër",
"Privacy" : "Privatësia",
"Keyboard shortcuts" : "Shkurtoret e tastierës",
"Search" : "Kërko",
"Rename conversation" : "Riemërtojeni bisedën",
"Send" : "Dërgo",
"Start a conversation" : "Filloni një bisedë",
+ "Media" : "Media",
+ "Files" : "Skedarët",
+ "Audio" : "Audio",
+ "Other" : "Tjetër",
"Default" : "E paracaktuar",
"Access to microphone & camera is only possible with HTTPS" : "Aksesi në mikrofon & kamera është i mundur vetëm me HTTPS",
"Access to microphone & camera was denied" : "Aksesi tek mikrofoni & kamera u mohua",
diff --git a/l10n/sr.js b/l10n/sr.js
index f42980d49..63b310c6f 100644
--- a/l10n/sr.js
+++ b/l10n/sr.js
@@ -511,13 +511,8 @@ OC.L10N.register(
"Chat" : "Ћаскање",
"Details" : "Детаљи",
"Settings" : "Поставке",
- "Media" : "Мултимедија",
- "Files" : "Фајлови",
- "Locations" : "Локације",
- "Audio" : "Звук",
- "Other" : "Остало",
- "Show all files" : "Прикажи све фајлове",
"Projects" : "Пројекти",
+ "Show all files" : "Прикажи све фајлове",
"Privacy" : "Приватност",
"Keyboard shortcuts" : "Пречице на тастатури",
"Search" : "Претрага",
@@ -530,6 +525,11 @@ OC.L10N.register(
"Link to a conversation" : "Веза ка разговору",
"Join a conversation or start a new one" : "Придружи се разговору или започни нови",
"An error occurred while fetching the participants" : "Грешка приликом дохватања учесника",
+ "Media" : "Мултимедија",
+ "Files" : "Фајлови",
+ "Locations" : "Локације",
+ "Audio" : "Звук",
+ "Other" : "Остало",
"Error while sharing file" : "Грешка приликом дељења фајла",
"Default" : "Подразумевано",
"You seem to be talking while muted, please unmute yourself for others to hear you" : "Изгледа да причате док сте утишани. Укључите звук да би Вас други чули",
diff --git a/l10n/sr.json b/l10n/sr.json
index f92a343db..eeb42ac3e 100644
--- a/l10n/sr.json
+++ b/l10n/sr.json
@@ -509,13 +509,8 @@
"Chat" : "Ћаскање",
"Details" : "Детаљи",
"Settings" : "Поставке",
- "Media" : "Мултимедија",
- "Files" : "Фајлови",
- "Locations" : "Локације",
- "Audio" : "Звук",
- "Other" : "Остало",
- "Show all files" : "Прикажи све фајлове",
"Projects" : "Пројекти",
+ "Show all files" : "Прикажи све фајлове",
"Privacy" : "Приватност",
"Keyboard shortcuts" : "Пречице на тастатури",
"Search" : "Претрага",
@@ -528,6 +523,11 @@
"Link to a conversation" : "Веза ка разговору",
"Join a conversation or start a new one" : "Придружи се разговору или започни нови",
"An error occurred while fetching the participants" : "Грешка приликом дохватања учесника",
+ "Media" : "Мултимедија",
+ "Files" : "Фајлови",
+ "Locations" : "Локације",
+ "Audio" : "Звук",
+ "Other" : "Остало",
"Error while sharing file" : "Грешка приликом дељења фајла",
"Default" : "Подразумевано",
"You seem to be talking while muted, please unmute yourself for others to hear you" : "Изгледа да причате док сте утишани. Укључите звук да би Вас други чули",
diff --git a/l10n/sv.js b/l10n/sv.js
index fff4e1522..35c32e9dd 100644
--- a/l10n/sv.js
+++ b/l10n/sv.js
@@ -637,13 +637,8 @@ OC.L10N.register(
"Chat" : "Chat",
"Details" : "Detaljer",
"Settings" : "Inställningar",
- "Media" : "Media",
- "Files" : "Filer",
- "Locations" : "Platser",
- "Audio" : "Ljud",
- "Other" : "Annat",
- "Show all files" : "Visa alla filer",
"Projects" : "Projekt",
+ "Show all files" : "Visa alla filer",
"Privacy" : "Integritet",
"Keyboard shortcuts" : "Tangentbordsgenvägar",
"Search" : "Sök",
@@ -666,6 +661,11 @@ OC.L10N.register(
"Link to a conversation" : "Länka till en konversation",
"Join a conversation or start a new one" : "Anslut till en konversation eller starta en ny",
"An error occurred while fetching the participants" : "Ett fel uppstod när deltagarna hämtades",
+ "Media" : "Media",
+ "Files" : "Filer",
+ "Locations" : "Platser",
+ "Audio" : "Ljud",
+ "Other" : "Annat",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk uppdaterades, ladda om sidan igen",
"Do not disturb" : "Stör ej",
"Away" : "Iväg",
diff --git a/l10n/sv.json b/l10n/sv.json
index f0302e81b..27ff1f09d 100644
--- a/l10n/sv.json
+++ b/l10n/sv.json
@@ -635,13 +635,8 @@
"Chat" : "Chat",
"Details" : "Detaljer",
"Settings" : "Inställningar",
- "Media" : "Media",
- "Files" : "Filer",
- "Locations" : "Platser",
- "Audio" : "Ljud",
- "Other" : "Annat",
- "Show all files" : "Visa alla filer",
"Projects" : "Projekt",
+ "Show all files" : "Visa alla filer",
"Privacy" : "Integritet",
"Keyboard shortcuts" : "Tangentbordsgenvägar",
"Search" : "Sök",
@@ -664,6 +659,11 @@
"Link to a conversation" : "Länka till en konversation",
"Join a conversation or start a new one" : "Anslut till en konversation eller starta en ny",
"An error occurred while fetching the participants" : "Ett fel uppstod när deltagarna hämtades",
+ "Media" : "Media",
+ "Files" : "Filer",
+ "Locations" : "Platser",
+ "Audio" : "Ljud",
+ "Other" : "Annat",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk uppdaterades, ladda om sidan igen",
"Do not disturb" : "Stör ej",
"Away" : "Iväg",
diff --git a/l10n/th.js b/l10n/th.js
index b53dd232e..9c19944f3 100644
--- a/l10n/th.js
+++ b/l10n/th.js
@@ -62,15 +62,15 @@ OC.L10N.register(
"Chat" : "แชท",
"Details" : "รายละเอียด",
"Settings" : "ตั้งค่า",
+ "Privacy" : "ความเป็นส่วนตัว",
+ "Keyboard shortcuts" : "ปุ่มลัด",
+ "Search" : "Search",
+ "Send" : "ส่ง",
"Media" : "สื่อ",
"Files" : "ไฟล์",
"Locations" : "สถานที่",
"Audio" : "เสียง",
"Other" : "อื่น ๆ",
- "Privacy" : "ความเป็นส่วนตัว",
- "Keyboard shortcuts" : "ปุ่มลัด",
- "Search" : "Search",
- "Send" : "ส่ง",
"Do not disturb" : "ห้ามรบกวน",
"Away" : "ไม่อยู่",
"Default" : "ค่าเริ่มต้น",
diff --git a/l10n/th.json b/l10n/th.json
index 810fc941d..c95314ca5 100644
--- a/l10n/th.json
+++ b/l10n/th.json
@@ -60,15 +60,15 @@
"Chat" : "แชท",
"Details" : "รายละเอียด",
"Settings" : "ตั้งค่า",
+ "Privacy" : "ความเป็นส่วนตัว",
+ "Keyboard shortcuts" : "ปุ่มลัด",
+ "Search" : "Search",
+ "Send" : "ส่ง",
"Media" : "สื่อ",
"Files" : "ไฟล์",
"Locations" : "สถานที่",
"Audio" : "เสียง",
"Other" : "อื่น ๆ",
- "Privacy" : "ความเป็นส่วนตัว",
- "Keyboard shortcuts" : "ปุ่มลัด",
- "Search" : "Search",
- "Send" : "ส่ง",
"Do not disturb" : "ห้ามรบกวน",
"Away" : "ไม่อยู่",
"Default" : "ค่าเริ่มต้น",
diff --git a/l10n/tr.js b/l10n/tr.js
index 2eefd34e4..bf2799be9 100644
--- a/l10n/tr.js
+++ b/l10n/tr.js
@@ -1039,13 +1039,7 @@ OC.L10N.register(
"Settings" : "Ayarlar",
"Shared items" : "Paylaşılmış ögeler",
"Participants ({count})" : "Katılımcılar ({count})",
- "Media" : "Ortam",
- "Files" : "Dosyalar",
- "Deck cards" : "Tahta kartları",
- "Voice messages" : "Sesli iletiler",
- "Locations" : "Konumlar",
- "Audio" : "Ses",
- "Other" : "Diğer",
+ "Projects" : "Projeler",
"Show all media" : "Tüm ortamları görüntüle",
"Show all files" : "Tüm dosyaları görüntüle",
"Show all deck cards" : "Tüm tahta kartlarını görüntüle",
@@ -1053,7 +1047,6 @@ OC.L10N.register(
"Show all locations" : "Tüm konumları görüntüle",
"Show all audio" : "Tüm sesleri görüntüle",
"Show all other" : "Tüm diğerlerini görüntüle",
- "Projects" : "Projeler",
"Meeting ID: {meetingId}" : "Toplantı kodu: {meetingId}",
"Your PIN: {attendeePin}" : "PIN kodunuz: {attendeePin}",
"Display name: <strong>{name}</strong>" : "Görüntülenecek ad: <strong>{name}</strong>",
@@ -1063,6 +1056,7 @@ OC.L10N.register(
"Sounds" : "Sesler",
"Play sounds when participants join or leave a call" : "Katılımcılar görüşmeye girdiğinde ya da çıktığında ses çalınsın",
"Sounds can currently not be played in Safari browser and iPad and iPhone devices due to technical restrictions by the manufacturer." : "Üreticinin sınırlamaları nedeniyle, sesler şu anda Safari web tarayıcısı, iPad ve iPhone aygıtlarında çalınamıyor. ",
+ "Sounds for chat and call notifications can be adjusted in the personal settings." : "Sohbet ve çağrı bildirimlerinin kişisel ayarlar bölümünden belirlenebilen sesleri.",
"Keyboard shortcuts" : "Kısayol tuşları",
"Speed up your Talk experience with these quick shortcuts." : "Şu kısayol tuşlarını kullanarak Talk deneyiminizi hızlandırabilirsiniz.",
"Focus the chat input" : "Sohbet girişine odaklan",
@@ -1123,6 +1117,13 @@ OC.L10N.register(
"Error while accessing microphone" : "Mikrofon erişilirken sorun çıktı",
"Access to camera is only possible with HTTPS" : "Kameraya yalnız HTTPS üzerinden erişilebilir",
"An error occurred while fetching the participants" : "Katılımcılar alınırken bir sorun çıktı",
+ "Media" : "Ortam",
+ "Files" : "Dosyalar",
+ "Deck cards" : "Tahta kartları",
+ "Voice messages" : "Sesli iletiler",
+ "Locations" : "Konumlar",
+ "Audio" : "Ses",
+ "Other" : "Diğer",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk güncellendi. Lütfen sayfayı yeniden yükleyin",
"Do not disturb" : "Rahatsız etmeyin",
"Away" : "Uzakta",
@@ -1131,6 +1132,7 @@ OC.L10N.register(
"Not enough free space to upload file \"{fileName}\"" : "\"{fileName}\" dosyasını yüklemek için yeterli boş alan yok",
"Error while uploading file \"{fileName}\"" : "\"{fileName}\" dosyası yüklenirken sorun çıktı",
"An error happened when trying to share your file" : "Dosyanız paylaşılmaya çalışılırken bir sorun çıktı",
+ "{guest} (guest)" : "{guest} (konuk)",
"Could not post message: {errorMessage}" : "İleti gönderilemedi: {errorMessage}",
"Failed to add reaction" : "Tepki eklenemedi",
"Failed to remove reaction" : "Tepki silinemedi",
diff --git a/l10n/tr.json b/l10n/tr.json
index 6ccc2b8b0..c7cbf70e2 100644
--- a/l10n/tr.json
+++ b/l10n/tr.json
@@ -1037,13 +1037,7 @@
"Settings" : "Ayarlar",
"Shared items" : "Paylaşılmış ögeler",
"Participants ({count})" : "Katılımcılar ({count})",
- "Media" : "Ortam",
- "Files" : "Dosyalar",
- "Deck cards" : "Tahta kartları",
- "Voice messages" : "Sesli iletiler",
- "Locations" : "Konumlar",
- "Audio" : "Ses",
- "Other" : "Diğer",
+ "Projects" : "Projeler",
"Show all media" : "Tüm ortamları görüntüle",
"Show all files" : "Tüm dosyaları görüntüle",
"Show all deck cards" : "Tüm tahta kartlarını görüntüle",
@@ -1051,7 +1045,6 @@
"Show all locations" : "Tüm konumları görüntüle",
"Show all audio" : "Tüm sesleri görüntüle",
"Show all other" : "Tüm diğerlerini görüntüle",
- "Projects" : "Projeler",
"Meeting ID: {meetingId}" : "Toplantı kodu: {meetingId}",
"Your PIN: {attendeePin}" : "PIN kodunuz: {attendeePin}",
"Display name: <strong>{name}</strong>" : "Görüntülenecek ad: <strong>{name}</strong>",
@@ -1061,6 +1054,7 @@
"Sounds" : "Sesler",
"Play sounds when participants join or leave a call" : "Katılımcılar görüşmeye girdiğinde ya da çıktığında ses çalınsın",
"Sounds can currently not be played in Safari browser and iPad and iPhone devices due to technical restrictions by the manufacturer." : "Üreticinin sınırlamaları nedeniyle, sesler şu anda Safari web tarayıcısı, iPad ve iPhone aygıtlarında çalınamıyor. ",
+ "Sounds for chat and call notifications can be adjusted in the personal settings." : "Sohbet ve çağrı bildirimlerinin kişisel ayarlar bölümünden belirlenebilen sesleri.",
"Keyboard shortcuts" : "Kısayol tuşları",
"Speed up your Talk experience with these quick shortcuts." : "Şu kısayol tuşlarını kullanarak Talk deneyiminizi hızlandırabilirsiniz.",
"Focus the chat input" : "Sohbet girişine odaklan",
@@ -1121,6 +1115,13 @@
"Error while accessing microphone" : "Mikrofon erişilirken sorun çıktı",
"Access to camera is only possible with HTTPS" : "Kameraya yalnız HTTPS üzerinden erişilebilir",
"An error occurred while fetching the participants" : "Katılımcılar alınırken bir sorun çıktı",
+ "Media" : "Ortam",
+ "Files" : "Dosyalar",
+ "Deck cards" : "Tahta kartları",
+ "Voice messages" : "Sesli iletiler",
+ "Locations" : "Konumlar",
+ "Audio" : "Ses",
+ "Other" : "Diğer",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk güncellendi. Lütfen sayfayı yeniden yükleyin",
"Do not disturb" : "Rahatsız etmeyin",
"Away" : "Uzakta",
@@ -1129,6 +1130,7 @@
"Not enough free space to upload file \"{fileName}\"" : "\"{fileName}\" dosyasını yüklemek için yeterli boş alan yok",
"Error while uploading file \"{fileName}\"" : "\"{fileName}\" dosyası yüklenirken sorun çıktı",
"An error happened when trying to share your file" : "Dosyanız paylaşılmaya çalışılırken bir sorun çıktı",
+ "{guest} (guest)" : "{guest} (konuk)",
"Could not post message: {errorMessage}" : "İleti gönderilemedi: {errorMessage}",
"Failed to add reaction" : "Tepki eklenemedi",
"Failed to remove reaction" : "Tepki silinemedi",
diff --git a/l10n/uk.js b/l10n/uk.js
index e914e0497..0e7d08c7d 100644
--- a/l10n/uk.js
+++ b/l10n/uk.js
@@ -152,11 +152,6 @@ OC.L10N.register(
"Chat" : "Чат",
"Details" : "Деталі",
"Settings" : "Налаштування",
- "Media" : "Медіа",
- "Files" : "Робота з файлами",
- "Locations" : "Розташування",
- "Audio" : "Аудіо",
- "Other" : "Інші",
"Projects" : "Проєкти",
"Privacy" : "Конфіденційність",
"Keyboard shortcuts" : "Гарячі клавіші",
@@ -168,6 +163,11 @@ OC.L10N.register(
"Say hi to your friends and colleagues!" : "Привітайтеся з вашими друзями та колегами!",
"Join a conversation or start a new one!" : "Приєднайтеся до розмови або почніть нову!",
"Join a conversation or start a new one" : "Приєднайтеся до розмови або почніть нову",
+ "Media" : "Медіа",
+ "Files" : "Робота з файлами",
+ "Locations" : "Розташування",
+ "Audio" : "Аудіо",
+ "Other" : "Інші",
"Do not disturb" : "Не турбувати",
"Away" : "Піти",
"Error while sharing file" : "Помилка під час надання спільного доступу до файлу",
diff --git a/l10n/uk.json b/l10n/uk.json
index 3357b7bec..05bfa4bae 100644
--- a/l10n/uk.json
+++ b/l10n/uk.json
@@ -150,11 +150,6 @@
"Chat" : "Чат",
"Details" : "Деталі",
"Settings" : "Налаштування",
- "Media" : "Медіа",
- "Files" : "Робота з файлами",
- "Locations" : "Розташування",
- "Audio" : "Аудіо",
- "Other" : "Інші",
"Projects" : "Проєкти",
"Privacy" : "Конфіденційність",
"Keyboard shortcuts" : "Гарячі клавіші",
@@ -166,6 +161,11 @@
"Say hi to your friends and colleagues!" : "Привітайтеся з вашими друзями та колегами!",
"Join a conversation or start a new one!" : "Приєднайтеся до розмови або почніть нову!",
"Join a conversation or start a new one" : "Приєднайтеся до розмови або почніть нову",
+ "Media" : "Медіа",
+ "Files" : "Робота з файлами",
+ "Locations" : "Розташування",
+ "Audio" : "Аудіо",
+ "Other" : "Інші",
"Do not disturb" : "Не турбувати",
"Away" : "Піти",
"Error while sharing file" : "Помилка під час надання спільного доступу до файлу",
diff --git a/l10n/vi.js b/l10n/vi.js
index f8e89719a..9f73411b0 100644
--- a/l10n/vi.js
+++ b/l10n/vi.js
@@ -651,13 +651,8 @@ OC.L10N.register(
"Chat" : "Trò chuyện",
"Details" : "Chi tiết",
"Settings" : "Thiết lập",
- "Media" : "Phương tiện",
- "Files" : "Tệp",
- "Locations" : "Các địa điểm",
- "Audio" : "Âm thanh",
- "Other" : "Khác",
- "Show all files" : "Hiện tất cả tệp",
"Projects" : "Dự án",
+ "Show all files" : "Hiện tất cả tệp",
"Meeting ID: {meetingId}" : "Mã cuộc họp: {meetingId}",
"Your PIN: {attendeePin}" : "Mã PIN của bạn: {attendeePin}",
"Attachments folder" : "Thư mục đính kèm",
@@ -716,6 +711,11 @@ OC.L10N.register(
"Error while accessing microphone" : "Lỗi khi truy cập micrô",
"Access to camera is only possible with HTTPS" : "Chỉ có thể truy cập vào camera với HTTPS",
"An error occurred while fetching the participants" : "Đã xảy ra lỗi khi tìm nạp những người tham gia",
+ "Media" : "Phương tiện",
+ "Files" : "Tệp",
+ "Locations" : "Các địa điểm",
+ "Audio" : "Âm thanh",
+ "Other" : "Khác",
"Nextcloud Talk was updated, please reload the page" : "vWorkspace Talk đã được cập nhật, vui lòng tải lại trang",
"Do not disturb" : "Đừng làm phiền",
"Away" : "Tạm vắng",
diff --git a/l10n/vi.json b/l10n/vi.json
index 3087532be..5587fc442 100644
--- a/l10n/vi.json
+++ b/l10n/vi.json
@@ -649,13 +649,8 @@
"Chat" : "Trò chuyện",
"Details" : "Chi tiết",
"Settings" : "Thiết lập",
- "Media" : "Phương tiện",
- "Files" : "Tệp",
- "Locations" : "Các địa điểm",
- "Audio" : "Âm thanh",
- "Other" : "Khác",
- "Show all files" : "Hiện tất cả tệp",
"Projects" : "Dự án",
+ "Show all files" : "Hiện tất cả tệp",
"Meeting ID: {meetingId}" : "Mã cuộc họp: {meetingId}",
"Your PIN: {attendeePin}" : "Mã PIN của bạn: {attendeePin}",
"Attachments folder" : "Thư mục đính kèm",
@@ -714,6 +709,11 @@
"Error while accessing microphone" : "Lỗi khi truy cập micrô",
"Access to camera is only possible with HTTPS" : "Chỉ có thể truy cập vào camera với HTTPS",
"An error occurred while fetching the participants" : "Đã xảy ra lỗi khi tìm nạp những người tham gia",
+ "Media" : "Phương tiện",
+ "Files" : "Tệp",
+ "Locations" : "Các địa điểm",
+ "Audio" : "Âm thanh",
+ "Other" : "Khác",
"Nextcloud Talk was updated, please reload the page" : "vWorkspace Talk đã được cập nhật, vui lòng tải lại trang",
"Do not disturb" : "Đừng làm phiền",
"Away" : "Tạm vắng",
diff --git a/l10n/zh_CN.js b/l10n/zh_CN.js
index fe31ea185..993e259b4 100644
--- a/l10n/zh_CN.js
+++ b/l10n/zh_CN.js
@@ -1030,13 +1030,8 @@ OC.L10N.register(
"Details" : "详情",
"Settings" : "设置",
"Participants ({count})" : "参与者数量 ({count})",
- "Media" : "媒体文件",
- "Files" : "文件",
- "Locations" : "地点",
- "Audio" : "音频",
- "Other" : "其他",
- "Show all files" : "显示所有文件",
"Projects" : "项目",
+ "Show all files" : "显示所有文件",
"Meeting ID: {meetingId}" : "会议 ID:{meetingId}",
"Your PIN: {attendeePin}" : "你的 PIN 码:{attendeePin}",
"Attachments folder" : "附件文件夹",
@@ -1105,6 +1100,11 @@ OC.L10N.register(
"Error while accessing microphone" : "访问麦克风时出错",
"Access to camera is only possible with HTTPS" : "摄像头只能通过 HTTPS 协议访问",
"An error occurred while fetching the participants" : "获取参与者时出错",
+ "Media" : "媒体文件",
+ "Files" : "文件",
+ "Locations" : "地点",
+ "Audio" : "音频",
+ "Other" : "其他",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk已更新,请重新加载页面",
"Do not disturb" : "请勿打扰",
"Away" : "离开",
diff --git a/l10n/zh_CN.json b/l10n/zh_CN.json
index af66e6fba..4ae81331e 100644
--- a/l10n/zh_CN.json
+++ b/l10n/zh_CN.json
@@ -1028,13 +1028,8 @@
"Details" : "详情",
"Settings" : "设置",
"Participants ({count})" : "参与者数量 ({count})",
- "Media" : "媒体文件",
- "Files" : "文件",
- "Locations" : "地点",
- "Audio" : "音频",
- "Other" : "其他",
- "Show all files" : "显示所有文件",
"Projects" : "项目",
+ "Show all files" : "显示所有文件",
"Meeting ID: {meetingId}" : "会议 ID:{meetingId}",
"Your PIN: {attendeePin}" : "你的 PIN 码:{attendeePin}",
"Attachments folder" : "附件文件夹",
@@ -1103,6 +1098,11 @@
"Error while accessing microphone" : "访问麦克风时出错",
"Access to camera is only possible with HTTPS" : "摄像头只能通过 HTTPS 协议访问",
"An error occurred while fetching the participants" : "获取参与者时出错",
+ "Media" : "媒体文件",
+ "Files" : "文件",
+ "Locations" : "地点",
+ "Audio" : "音频",
+ "Other" : "其他",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk已更新,请重新加载页面",
"Do not disturb" : "请勿打扰",
"Away" : "离开",
diff --git a/l10n/zh_HK.js b/l10n/zh_HK.js
index 63b1cb6f7..36493d53f 100644
--- a/l10n/zh_HK.js
+++ b/l10n/zh_HK.js
@@ -1039,13 +1039,7 @@ OC.L10N.register(
"Settings" : "設定",
"Shared items" : "分享項目",
"Participants ({count})" : "參與者 ({count})",
- "Media" : "多媒體",
- "Files" : "檔案",
- "Deck cards" : "看板卡片",
- "Voice messages" : "語音留言",
- "Locations" : "地點",
- "Audio" : "音頻",
- "Other" : "其它",
+ "Projects" : "專案項目",
"Show all media" : "顯示所有媒體",
"Show all files" : "顯示所有檔案",
"Show all deck cards" : "顯示所有卡片",
@@ -1053,7 +1047,6 @@ OC.L10N.register(
"Show all locations" : "顯示所有位置",
"Show all audio" : "顯示所有音頻",
"Show all other" : "顯示所有其他",
- "Projects" : "專案項目",
"Meeting ID: {meetingId}" : "會議 ID:{meetingId}",
"Your PIN: {attendeePin}" : "您的PIN:{attendeePin}",
"Display name: <strong>{name}</strong>" : "顯示名稱:<strong>{name}</strong>",
@@ -1063,6 +1056,7 @@ OC.L10N.register(
"Sounds" : "聲音 ",
"Play sounds when participants join or leave a call" : "參與者加入或離開通話時播放聲音",
"Sounds can currently not be played in Safari browser and iPad and iPhone devices due to technical restrictions by the manufacturer." : "由於製造商的技術限制,當前無法在 Safari 瀏覽器以及 iPad 和 iPhone 裝置中播放聲音。",
+ "Sounds for chat and call notifications can be adjusted in the personal settings." : "可以在個人設置中調整聊天和通話通告的聲音。",
"Keyboard shortcuts" : "鍵盤快捷鍵",
"Speed up your Talk experience with these quick shortcuts." : "這些快速捷徑可加快您的 Talk 體驗。",
"Focus the chat input" : "關注聊天輸入字段",
@@ -1123,6 +1117,13 @@ OC.L10N.register(
"Error while accessing microphone" : "存取米高風時發生錯誤",
"Access to camera is only possible with HTTPS" : "只有使用 HTTPS 才能存取相機",
"An error occurred while fetching the participants" : "擷取參與者時發生錯誤",
+ "Media" : "多媒體",
+ "Files" : "檔案",
+ "Deck cards" : "看板卡片",
+ "Voice messages" : "語音留言",
+ "Locations" : "地點",
+ "Audio" : "音頻",
+ "Other" : "其它",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk 已更新,請重新加載頁面",
"Do not disturb" : "請勿打擾",
"Away" : "離開",
@@ -1131,6 +1132,7 @@ OC.L10N.register(
"Not enough free space to upload file \"{fileName}\"" : "空間不足,不能上傳檔案 \"{fileName}\"",
"Error while uploading file \"{fileName}\"" : "上傳檔案時發生錯誤 \"{fileName}\"",
"An error happened when trying to share your file" : "分享檔案時發生錯誤",
+ "{guest} (guest)" : "{guest}(訪客)",
"Could not post message: {errorMessage}" : "無法發佈訊息:{errorMessage}",
"Failed to add reaction" : "添加反應失敗",
"Failed to remove reaction" : "移除反應失敗",
diff --git a/l10n/zh_HK.json b/l10n/zh_HK.json
index 6538c80a6..0c10c6d48 100644
--- a/l10n/zh_HK.json
+++ b/l10n/zh_HK.json
@@ -1037,13 +1037,7 @@
"Settings" : "設定",
"Shared items" : "分享項目",
"Participants ({count})" : "參與者 ({count})",
- "Media" : "多媒體",
- "Files" : "檔案",
- "Deck cards" : "看板卡片",
- "Voice messages" : "語音留言",
- "Locations" : "地點",
- "Audio" : "音頻",
- "Other" : "其它",
+ "Projects" : "專案項目",
"Show all media" : "顯示所有媒體",
"Show all files" : "顯示所有檔案",
"Show all deck cards" : "顯示所有卡片",
@@ -1051,7 +1045,6 @@
"Show all locations" : "顯示所有位置",
"Show all audio" : "顯示所有音頻",
"Show all other" : "顯示所有其他",
- "Projects" : "專案項目",
"Meeting ID: {meetingId}" : "會議 ID:{meetingId}",
"Your PIN: {attendeePin}" : "您的PIN:{attendeePin}",
"Display name: <strong>{name}</strong>" : "顯示名稱:<strong>{name}</strong>",
@@ -1061,6 +1054,7 @@
"Sounds" : "聲音 ",
"Play sounds when participants join or leave a call" : "參與者加入或離開通話時播放聲音",
"Sounds can currently not be played in Safari browser and iPad and iPhone devices due to technical restrictions by the manufacturer." : "由於製造商的技術限制,當前無法在 Safari 瀏覽器以及 iPad 和 iPhone 裝置中播放聲音。",
+ "Sounds for chat and call notifications can be adjusted in the personal settings." : "可以在個人設置中調整聊天和通話通告的聲音。",
"Keyboard shortcuts" : "鍵盤快捷鍵",
"Speed up your Talk experience with these quick shortcuts." : "這些快速捷徑可加快您的 Talk 體驗。",
"Focus the chat input" : "關注聊天輸入字段",
@@ -1121,6 +1115,13 @@
"Error while accessing microphone" : "存取米高風時發生錯誤",
"Access to camera is only possible with HTTPS" : "只有使用 HTTPS 才能存取相機",
"An error occurred while fetching the participants" : "擷取參與者時發生錯誤",
+ "Media" : "多媒體",
+ "Files" : "檔案",
+ "Deck cards" : "看板卡片",
+ "Voice messages" : "語音留言",
+ "Locations" : "地點",
+ "Audio" : "音頻",
+ "Other" : "其它",
"Nextcloud Talk was updated, please reload the page" : "Nextcloud Talk 已更新,請重新加載頁面",
"Do not disturb" : "請勿打擾",
"Away" : "離開",
@@ -1129,6 +1130,7 @@
"Not enough free space to upload file \"{fileName}\"" : "空間不足,不能上傳檔案 \"{fileName}\"",
"Error while uploading file \"{fileName}\"" : "上傳檔案時發生錯誤 \"{fileName}\"",
"An error happened when trying to share your file" : "分享檔案時發生錯誤",
+ "{guest} (guest)" : "{guest}(訪客)",
"Could not post message: {errorMessage}" : "無法發佈訊息:{errorMessage}",
"Failed to add reaction" : "添加反應失敗",
"Failed to remove reaction" : "移除反應失敗",
diff --git a/l10n/zh_TW.js b/l10n/zh_TW.js
index 0bca4244a..6bcf3f888 100644
--- a/l10n/zh_TW.js
+++ b/l10n/zh_TW.js
@@ -375,13 +375,8 @@ OC.L10N.register(
"Chat" : "聊天",
"Details" : "詳細資訊",
"Settings" : "設定",
- "Media" : "多媒體",
- "Files" : "檔案",
- "Locations" : "地點",
- "Audio" : "音樂",
- "Other" : "其他",
- "Show all files" : "顯示全部檔案",
"Projects" : "專案",
+ "Show all files" : "顯示全部檔案",
"Privacy" : "隱私",
"Keyboard shortcuts" : "快速鍵",
"Search" : "搜尋",
@@ -392,6 +387,11 @@ OC.L10N.register(
"Start a conversation" : "新對話",
"You were mentioned" : "您被提及",
"Join a conversation or start a new one" : "加入一個對話或者開一個新的",
+ "Media" : "多媒體",
+ "Files" : "檔案",
+ "Locations" : "地點",
+ "Audio" : "音樂",
+ "Other" : "其他",
"Do not disturb" : "請勿打擾",
"Away" : "外出",
"Error while sharing file" : "分享檔案時發生錯誤",
diff --git a/l10n/zh_TW.json b/l10n/zh_TW.json
index 9d291d08c..0eac3abb1 100644
--- a/l10n/zh_TW.json
+++ b/l10n/zh_TW.json
@@ -373,13 +373,8 @@
"Chat" : "聊天",
"Details" : "詳細資訊",
"Settings" : "設定",
- "Media" : "多媒體",
- "Files" : "檔案",
- "Locations" : "地點",
- "Audio" : "音樂",
- "Other" : "其他",
- "Show all files" : "顯示全部檔案",
"Projects" : "專案",
+ "Show all files" : "顯示全部檔案",
"Privacy" : "隱私",
"Keyboard shortcuts" : "快速鍵",
"Search" : "搜尋",
@@ -390,6 +385,11 @@
"Start a conversation" : "新對話",
"You were mentioned" : "您被提及",
"Join a conversation or start a new one" : "加入一個對話或者開一個新的",
+ "Media" : "多媒體",
+ "Files" : "檔案",
+ "Locations" : "地點",
+ "Audio" : "音樂",
+ "Other" : "其他",
"Do not disturb" : "請勿打擾",
"Away" : "外出",
"Error while sharing file" : "分享檔案時發生錯誤",
diff --git a/lib/Capabilities.php b/lib/Capabilities.php
index 50dad5816..aa613833d 100644
--- a/lib/Capabilities.php
+++ b/lib/Capabilities.php
@@ -101,6 +101,7 @@ class Capabilities implements IPublicCapability {
'conversation-permissions',
'rich-object-list-media',
'rich-object-delete',
+ 'chat-permission',
],
'config' => [
'attachments' => [
diff --git a/lib/Chat/SystemMessage/Listener.php b/lib/Chat/SystemMessage/Listener.php
index 6190c1e48..e8a50724c 100644
--- a/lib/Chat/SystemMessage/Listener.php
+++ b/lib/Chat/SystemMessage/Listener.php
@@ -77,281 +77,307 @@ class Listener implements IEventListener {
}
public static function register(IEventDispatcher $dispatcher): void {
- $dispatcher->addListener(Room::EVENT_BEFORE_SESSION_JOIN_CALL, static function (ModifyParticipantEvent $event) {
- $room = $event->getRoom();
- /** @var self $listener */
- $listener = \OC::$server->get(self::class);
- /** @var ParticipantService $participantService */
- $participantService = \OC::$server->get(ParticipantService::class);
+ $dispatcher->addListener(Room::EVENT_BEFORE_SESSION_JOIN_CALL, self::class . '::sendSystemMessageAboutBeginOfCall');
+ $dispatcher->addListener(Room::EVENT_AFTER_SESSION_LEAVE_CALL, self::class . '::sendSystemMessageAboutCallLeft');
+ $dispatcher->addListener(Room::EVENT_AFTER_ROOM_CREATE, self::class . '::sendSystemMessageAboutConversationCreated');
+ $dispatcher->addListener(Room::EVENT_AFTER_NAME_SET, self::class . '::sendSystemMessageAboutConversationRenamed');
+ $dispatcher->addListener(Room::EVENT_AFTER_DESCRIPTION_SET, self::class . '::sendSystemMessageAboutRoomDescriptionChanges');
+ $dispatcher->addListener(Room::EVENT_AFTER_PASSWORD_SET, self::class . '::sendSystemMessageAboutRoomPassword');
+ $dispatcher->addListener(Room::EVENT_AFTER_TYPE_SET, self::class . '::sendSystemGuestPermissionsMessage');
+ $dispatcher->addListener(Room::EVENT_AFTER_READONLY_SET, self::class . '::sendSystemReadOnlyMessage');
+ $dispatcher->addListener(Room::EVENT_AFTER_LISTABLE_SET, self::class . '::sendSystemListableMessage');
+ $dispatcher->addListener(Room::EVENT_AFTER_LOBBY_STATE_SET, self::class . '::sendSystemLobbyMessage');
+ $dispatcher->addListener(Room::EVENT_AFTER_USERS_ADD, self::class . '::addSystemMessageUserAdded');
+ $dispatcher->addListener(Room::EVENT_AFTER_USER_REMOVE, self::class . '::sendSystemMessageUserRemoved');
+ $dispatcher->addListener(Room::EVENT_AFTER_PARTICIPANT_TYPE_SET, self::class . '::sendSystemMessageAboutPromoteOrDemoteModerator');
+ $dispatcher->addListener('OCP\Share::postShare', self::class . '::fixMimeTypeOfVoiceMessage');
+ $dispatcher->addListener(RoomShareProvider::EVENT_SHARE_FILE_AGAIN, self::class . '::fixMimeTypeOfVoiceMessage');
+ }
- if ($participantService->hasActiveSessionsInCall($room)) {
- $listener->sendSystemMessage($room, 'call_joined', [], $event->getParticipant());
- } else {
- $listener->sendSystemMessage($room, 'call_started', [], $event->getParticipant());
- }
- });
- $dispatcher->addListener(Room::EVENT_AFTER_SESSION_LEAVE_CALL, static function (ModifyParticipantEvent $event) {
- if ($event instanceof ModifyEveryoneEvent) {
- // No individual system message if the call is ended for everyone
- return;
- }
+ public static function sendSystemMessageAboutBeginOfCall(ModifyParticipantEvent $event): void {
+ $room = $event->getRoom();
+ /** @var self $listener */
+ $listener = \OC::$server->get(self::class);
+ /** @var ParticipantService $participantService */
+ $participantService = \OC::$server->get(ParticipantService::class);
- if ($event->getNewValue() === $event->getOldValue()) {
- return;
- }
+ if ($participantService->hasActiveSessionsInCall($room)) {
+ $listener->sendSystemMessage($room, 'call_joined', [], $event->getParticipant());
+ } else {
+ $listener->sendSystemMessage($room, 'call_started', [], $event->getParticipant());
+ }
+ }
- $room = $event->getRoom();
+ public static function sendSystemMessageAboutCallLeft(ModifyParticipantEvent $event): void {
+ if ($event instanceof ModifyEveryoneEvent) {
+ // No individual system message if the call is ended for everyone
+ return;
+ }
- $session = $event->getParticipant()->getSession();
- if (!$session instanceof Session) {
- // This happens in case the user was kicked/lobbied
- return;
- }
+ if ($event->getNewValue() === $event->getOldValue()) {
+ return;
+ }
- /** @var self $listener */
- $listener = \OC::$server->get(self::class);
+ $room = $event->getRoom();
- $listener->sendSystemMessage($room, 'call_left', [], $event->getParticipant());
- });
+ $session = $event->getParticipant()->getSession();
+ if (!$session instanceof Session) {
+ // This happens in case the user was kicked/lobbied
+ return;
+ }
- $dispatcher->addListener(Room::EVENT_AFTER_ROOM_CREATE, static function (RoomEvent $event) {
- $room = $event->getRoom();
- /** @var self $listener */
- $listener = \OC::$server->get(self::class);
+ /** @var self $listener */
+ $listener = \OC::$server->get(self::class);
- $listener->sendSystemMessage($room, 'conversation_created');
- });
- $dispatcher->addListener(Room::EVENT_AFTER_NAME_SET, static function (ModifyRoomEvent $event) {
- if ($event->getOldValue() === '' ||
- $event->getNewValue() === '') {
- return;
- }
+ $listener->sendSystemMessage($room, 'call_left', [], $event->getParticipant());
+ }
- $room = $event->getRoom();
- /** @var self $listener */
- $listener = \OC::$server->get(self::class);
+ public static function sendSystemMessageAboutConversationCreated(RoomEvent $event): void {
+ $room = $event->getRoom();
+ /** @var self $listener */
+ $listener = \OC::$server->get(self::class);
+
+ $listener->sendSystemMessage($room, 'conversation_created');
+ }
- $listener->sendSystemMessage($room, 'conversation_renamed', [
- 'newName' => $event->getNewValue(),
- 'oldName' => $event->getOldValue(),
+ public static function sendSystemMessageAboutConversationRenamed(ModifyRoomEvent $event): void {
+ if ($event->getOldValue() === '' ||
+ $event->getNewValue() === '') {
+ return;
+ }
+
+ $room = $event->getRoom();
+ /** @var self $listener */
+ $listener = \OC::$server->get(self::class);
+
+ $listener->sendSystemMessage($room, 'conversation_renamed', [
+ 'newName' => $event->getNewValue(),
+ 'oldName' => $event->getOldValue(),
+ ]);
+ }
+
+ public static function sendSystemMessageAboutRoomDescriptionChanges(ModifyRoomEvent $event): void {
+ $room = $event->getRoom();
+ /** @var self $listener */
+ $listener = \OC::$server->get(self::class);
+
+ if ($event->getNewValue() !== '') {
+ $listener->sendSystemMessage($room, 'description_set', [
+ 'newDescription' => $event->getNewValue(),
]);
- });
- $dispatcher->addListener(Room::EVENT_AFTER_DESCRIPTION_SET, static function (ModifyRoomEvent $event) {
- $room = $event->getRoom();
+ } else {
+ $listener->sendSystemMessage($room, 'description_removed');
+ }
+ }
+
+ public static function sendSystemMessageAboutRoomPassword(ModifyRoomEvent $event): void {
+ $room = $event->getRoom();
+ /** @var self $listener */
+ $listener = \OC::$server->get(self::class);
+
+ if ($event->getNewValue() !== '') {
+ $listener->sendSystemMessage($room, 'password_set');
+ } else {
+ $listener->sendSystemMessage($room, 'password_removed');
+ }
+ }
+
+ public static function sendSystemGuestPermissionsMessage(ModifyRoomEvent $event): void {
+ $room = $event->getRoom();
+
+ if ($event->getOldValue() === Room::TYPE_ONE_TO_ONE) {
+ return;
+ }
+
+ if ($event->getNewValue() === Room::TYPE_PUBLIC) {
/** @var self $listener */
$listener = \OC::$server->get(self::class);
-
- if ($event->getNewValue() !== '') {
- $listener->sendSystemMessage($room, 'description_set', [
- 'newDescription' => $event->getNewValue(),
- ]);
- } else {
- $listener->sendSystemMessage($room, 'description_removed');
- }
- });
- $dispatcher->addListener(Room::EVENT_AFTER_PASSWORD_SET, static function (ModifyRoomEvent $event) {
- $room = $event->getRoom();
+ $listener->sendSystemMessage($room, 'guests_allowed');
+ } elseif ($event->getNewValue() === Room::TYPE_GROUP) {
/** @var self $listener */
$listener = \OC::$server->get(self::class);
+ $listener->sendSystemMessage($room, 'guests_disallowed');
+ }
+ }
- if ($event->getNewValue() !== '') {
- $listener->sendSystemMessage($room, 'password_set');
- } else {
- $listener->sendSystemMessage($room, 'password_removed');
- }
- });
- $dispatcher->addListener(Room::EVENT_AFTER_TYPE_SET, static function (ModifyRoomEvent $event) {
- $room = $event->getRoom();
+ public static function sendSystemReadOnlyMessage(ModifyRoomEvent $event): void {
+ $room = $event->getRoom();
- if ($event->getOldValue() === Room::TYPE_ONE_TO_ONE) {
- return;
- }
+ if ($room->getType() === Room::TYPE_CHANGELOG) {
+ return;
+ }
- if ($event->getNewValue() === Room::TYPE_PUBLIC) {
- /** @var self $listener */
- $listener = \OC::$server->get(self::class);
- $listener->sendSystemMessage($room, 'guests_allowed');
- } elseif ($event->getNewValue() === Room::TYPE_GROUP) {
- /** @var self $listener */
- $listener = \OC::$server->get(self::class);
- $listener->sendSystemMessage($room, 'guests_disallowed');
- }
- });
- $dispatcher->addListener(Room::EVENT_AFTER_READONLY_SET, static function (ModifyRoomEvent $event) {
- $room = $event->getRoom();
+ /** @var self $listener */
+ $listener = \OC::$server->get(self::class);
- if ($room->getType() === Room::TYPE_CHANGELOG) {
- return;
- }
+ if ($event->getNewValue() === Room::READ_ONLY) {
+ $listener->sendSystemMessage($room, 'read_only');
+ } elseif ($event->getNewValue() === Room::READ_WRITE) {
+ $listener->sendSystemMessage($room, 'read_only_off');
+ }
+ }
- /** @var self $listener */
- $listener = \OC::$server->get(self::class);
+ public static function sendSystemListableMessage(ModifyRoomEvent $event): void {
+ $room = $event->getRoom();
- if ($event->getNewValue() === Room::READ_ONLY) {
- $listener->sendSystemMessage($room, 'read_only');
- } elseif ($event->getNewValue() === Room::READ_WRITE) {
- $listener->sendSystemMessage($room, 'read_only_off');
- }
- });
- $dispatcher->addListener(Room::EVENT_AFTER_LISTABLE_SET, static function (ModifyRoomEvent $event) {
- $room = $event->getRoom();
+ /** @var self $listener */
+ $listener = \OC::$server->get(self::class);
- /** @var self $listener */
- $listener = \OC::$server->get(self::class);
+ if ($event->getNewValue() === Room::LISTABLE_NONE) {
+ $listener->sendSystemMessage($room, 'listable_none');
+ } elseif ($event->getNewValue() === Room::LISTABLE_USERS) {
+ $listener->sendSystemMessage($room, 'listable_users');
+ } elseif ($event->getNewValue() === Room::LISTABLE_ALL) {
+ $listener->sendSystemMessage($room, 'listable_all');
+ }
+ }
- if ($event->getNewValue() === Room::LISTABLE_NONE) {
- $listener->sendSystemMessage($room, 'listable_none');
- } elseif ($event->getNewValue() === Room::LISTABLE_USERS) {
- $listener->sendSystemMessage($room, 'listable_users');
- } elseif ($event->getNewValue() === Room::LISTABLE_ALL) {
- $listener->sendSystemMessage($room, 'listable_all');
- }
- });
- $dispatcher->addListener(Room::EVENT_AFTER_LOBBY_STATE_SET, static function (ModifyLobbyEvent $event) {
- if ($event->getNewValue() === $event->getOldValue()) {
- return;
- }
+ public static function sendSystemLobbyMessage(ModifyLobbyEvent $event): void {
+ if ($event->getNewValue() === $event->getOldValue()) {
+ return;
+ }
- $room = $event->getRoom();
+ $room = $event->getRoom();
- /** @var self $listener */
- $listener = \OC::$server->get(self::class);
+ /** @var self $listener */
+ $listener = \OC::$server->get(self::class);
- if ($event->isTimerReached()) {
- $listener->sendSystemMessage($room, 'lobby_timer_reached');
- } elseif ($event->getNewValue() === Webinary::LOBBY_NONE) {
- $listener->sendSystemMessage($room, 'lobby_none');
- } elseif ($event->getNewValue() === Webinary::LOBBY_NON_MODERATORS) {
- $listener->sendSystemMessage($room, 'lobby_non_moderators');
- }
- });
+ if ($event->isTimerReached()) {
+ $listener->sendSystemMessage($room, 'lobby_timer_reached');
+ } elseif ($event->getNewValue() === Webinary::LOBBY_NONE) {
+ $listener->sendSystemMessage($room, 'lobby_none');
+ } elseif ($event->getNewValue() === Webinary::LOBBY_NON_MODERATORS) {
+ $listener->sendSystemMessage($room, 'lobby_non_moderators');
+ }
+ }
- $dispatcher->addListener(Room::EVENT_AFTER_USERS_ADD, static function (AddParticipantsEvent $event) {
- $room = $event->getRoom();
- if ($room->getType() === Room::TYPE_ONE_TO_ONE) {
- return;
- }
+ public static function addSystemMessageUserAdded(AddParticipantsEvent $event): void {
+ $room = $event->getRoom();
+ if ($room->getType() === Room::TYPE_ONE_TO_ONE) {
+ return;
+ }
- /** @var self $listener */
- $listener = \OC::$server->get(self::class);
+ /** @var self $listener */
+ $listener = \OC::$server->get(self::class);
+
+ $participants = $event->getParticipants();
- $participants = $event->getParticipants();
-
- foreach ($participants as $participant) {
- if ($participant['actorType'] !== 'users') {
- continue;
- }
-
- $participantType = null;
- if (isset($participant['participantType'])) {
- $participantType = $participant['participantType'];
- }
-
- $userJoinedFileRoom = $room->getObjectType() === 'file' && $participantType !== Participant::USER_SELF_JOINED;
-
- // add a message "X joined the conversation", whenever user $userId:
- if (
- // - has joined a file room but not through a public link
- $userJoinedFileRoom
- // - has been added by another user (and not when creating a conversation)
- || $listener->getUserId() !== $participant['actorId']
- // - has joined a listable room on their own
- || $participantType === Participant::USER) {
- $comment = $listener->sendSystemMessage(
- $room,
- 'user_added',
- ['user' => $participant['actorId']],
- null,
- $event->shouldSkipLastMessageUpdate()
- );
-
- $event->setLastMessage($comment);
- }
+ foreach ($participants as $participant) {
+ if ($participant['actorType'] !== 'users') {
+ continue;
}
- });
- $dispatcher->addListener(Room::EVENT_AFTER_USER_REMOVE, static function (RemoveUserEvent $event) {
- $room = $event->getRoom();
- if ($room->getType() === Room::TYPE_ONE_TO_ONE) {
- return;
+ $participantType = null;
+ if (isset($participant['participantType'])) {
+ $participantType = $participant['participantType'];
}
- if ($event->getReason() === Room::PARTICIPANT_LEFT
- && $event->getParticipant()->getAttendee()->getParticipantType() === Participant::USER_SELF_JOINED) {
- // Self-joined user closes the tab/window or leaves via the menu
- return;
+ $userJoinedFileRoom = $room->getObjectType() === 'file' && $participantType !== Participant::USER_SELF_JOINED;
+
+ // add a message "X joined the conversation", whenever user $userId:
+ if (
+ // - has joined a file room but not through a public link
+ $userJoinedFileRoom
+ // - has been added by another user (and not when creating a conversation)
+ || $listener->getUserId() !== $participant['actorId']
+ // - has joined a listable room on their own
+ || $participantType === Participant::USER) {
+ $comment = $listener->sendSystemMessage(
+ $room,
+ 'user_added',
+ ['user' => $participant['actorId']],
+ null,
+ $event->shouldSkipLastMessageUpdate()
+ );
+
+ $event->setLastMessage($comment);
}
+ }
+ }
+
+ public static function sendSystemMessageUserRemoved(RemoveUserEvent $event): void {
+ $room = $event->getRoom();
+ if ($room->getType() === Room::TYPE_ONE_TO_ONE) {
+ return;
+ }
+
+ if ($event->getReason() === Room::PARTICIPANT_LEFT
+ && $event->getParticipant()->getAttendee()->getParticipantType() === Participant::USER_SELF_JOINED) {
+ // Self-joined user closes the tab/window or leaves via the menu
+ return;
+ }
+
+ /** @var self $listener */
+ $listener = \OC::$server->get(self::class);
+ $listener->sendSystemMessage($room, 'user_removed', ['user' => $event->getUser()->getUID()]);
+ }
+
+ public static function sendSystemMessageAboutPromoteOrDemoteModerator(ModifyParticipantEvent $event): void {
+ $room = $event->getRoom();
+ $attendee = $event->getParticipant()->getAttendee();
+
+ if ($attendee->getActorType() !== Attendee::ACTOR_USERS && $attendee->getActorType() !== Attendee::ACTOR_GUESTS) {
+ return;
+ }
+
+ if ($event->getNewValue() === Participant::MODERATOR) {
/** @var self $listener */
$listener = \OC::$server->get(self::class);
- $listener->sendSystemMessage($room, 'user_removed', ['user' => $event->getUser()->getUID()]);
- });
- $dispatcher->addListener(Room::EVENT_AFTER_PARTICIPANT_TYPE_SET, static function (ModifyParticipantEvent $event) {
- $room = $event->getRoom();
- $attendee = $event->getParticipant()->getAttendee();
-
- if ($attendee->getActorType() !== Attendee::ACTOR_USERS && $attendee->getActorType() !== Attendee::ACTOR_GUESTS) {
- return;
- }
-
- if ($event->getNewValue() === Participant::MODERATOR) {
+ $listener->sendSystemMessage($room, 'moderator_promoted', ['user' => $attendee->getActorId()]);
+ } elseif ($event->getNewValue() === Participant::USER) {
+ if ($event->getOldValue() === Participant::USER_SELF_JOINED) {
/** @var self $listener */
$listener = \OC::$server->get(self::class);
- $listener->sendSystemMessage($room, 'moderator_promoted', ['user' => $attendee->getActorId()]);
- } elseif ($event->getNewValue() === Participant::USER) {
- if ($event->getOldValue() === Participant::USER_SELF_JOINED) {
- /** @var self $listener */
- $listener = \OC::$server->get(self::class);
- $listener->sendSystemMessage($room, 'user_added', ['user' => $attendee->getActorId()]);
- } else {
- /** @var self $listener */
- $listener = \OC::$server->get(self::class);
- $listener->sendSystemMessage($room, 'moderator_demoted', ['user' => $attendee->getActorId()]);
- }
- } elseif ($event->getNewValue() === Participant::GUEST_MODERATOR) {
- /** @var self $listener */
- $listener = \OC::$server->get(self::class);
- $listener->sendSystemMessage($room, 'guest_moderator_promoted', ['session' => $attendee->getActorId()]);
- } elseif ($event->getNewValue() === Participant::GUEST) {
+ $listener->sendSystemMessage($room, 'user_added', ['user' => $attendee->getActorId()]);
+ } else {
/** @var self $listener */
$listener = \OC::$server->get(self::class);
- $listener->sendSystemMessage($room, 'guest_moderator_demoted', ['session' => $attendee->getActorId()]);
+ $listener->sendSystemMessage($room, 'moderator_demoted', ['user' => $attendee->getActorId()]);
}
- });
- $listener = function (GenericEvent $event): void {
- /** @var IShare $share */
- $share = $event->getSubject();
-
- if ($share->getShareType() !== IShare::TYPE_ROOM) {
- return;
- }
-
+ } elseif ($event->getNewValue() === Participant::GUEST_MODERATOR) {
/** @var self $listener */
$listener = \OC::$server->get(self::class);
+ $listener->sendSystemMessage($room, 'guest_moderator_promoted', ['session' => $attendee->getActorId()]);
+ } elseif ($event->getNewValue() === Participant::GUEST) {
+ /** @var self $listener */
+ $listener = \OC::$server->get(self::class);
+ $listener->sendSystemMessage($room, 'guest_moderator_demoted', ['session' => $attendee->getActorId()]);
+ }
+ }
- /** @var Manager $manager */
- $manager = \OC::$server->get(Manager::class);
+ /**
+ * @param GenericEvent|Event $event
+ * @return void
+ */
+ public static function fixMimeTypeOfVoiceMessage($event): void {
+ /** @var IShare $share */
+ $share = $event->getSubject();
- $room = $manager->getRoomByToken($share->getSharedWith());
- $metaData = \OC::$server->getRequest()->getParam('talkMetaData') ?? '';
- $metaData = json_decode($metaData, true);
- $metaData = is_array($metaData) ? $metaData : [];
+ if ($share->getShareType() !== IShare::TYPE_ROOM) {
+ return;
+ }
+
+ /** @var self $listener */
+ $listener = \OC::$server->get(self::class);
+
+ /** @var Manager $manager */
+ $manager = \OC::$server->get(Manager::class);
- if (isset($metaData['messageType']) && $metaData['messageType'] === 'voice-message') {
- if ($share->getNode()->getMimeType() !== 'audio/mpeg'
- && $share->getNode()->getMimeType() !== 'audio/wav') {
- unset($metaData['messageType']);
- }
+ $room = $manager->getRoomByToken($share->getSharedWith());
+ $metaData = \OC::$server->getRequest()->getParam('talkMetaData') ?? '';
+ $metaData = json_decode($metaData, true);
+ $metaData = is_array($metaData) ? $metaData : [];
+
+ if (isset($metaData['messageType']) && $metaData['messageType'] === 'voice-message') {
+ if ($share->getNode()->getMimeType() !== 'audio/mpeg'
+ && $share->getNode()->getMimeType() !== 'audio/wav') {
+ unset($metaData['messageType']);
}
- $metaData['mimeType'] = $share->getNode()->getMimeType();
-
- $listener->sendSystemMessage($room, 'file_shared', ['share' => $share->getId(), 'metaData' => $metaData]);
- };
- /**
- * @psalm-suppress UndefinedClass
- */
- $dispatcher->addListener('OCP\Share::postShare', $listener);
- $dispatcher->addListener(RoomShareProvider::class . '::' . 'share_file_again', $listener);
+ }
+ $metaData['mimeType'] = $share->getNode()->getMimeType();
+
+ $listener->sendSystemMessage($room, 'file_shared', ['share' => $share->getId(), 'metaData' => $metaData]);
}
public function handle(Event $event): void {
diff --git a/lib/Collaboration/Collaborators/RoomPlugin.php b/lib/Collaboration/Collaborators/RoomPlugin.php
index ea9e92d8e..cc03429fe 100644
--- a/lib/Collaboration/Collaborators/RoomPlugin.php
+++ b/lib/Collaboration/Collaborators/RoomPlugin.php
@@ -25,6 +25,8 @@ declare(strict_types=1);
namespace OCA\Talk\Collaboration\Collaborators;
use OCA\Talk\Manager;
+use OCA\Talk\Model\Attendee;
+use OCA\Talk\Participant;
use OCA\Talk\Room;
use OCP\Collaboration\Collaborators\ISearchPlugin;
use OCP\Collaboration\Collaborators\ISearchResult;
@@ -62,6 +64,12 @@ class RoomPlugin implements ISearchPlugin {
continue;
}
+ $participant = $room->getParticipant($userId, false);
+ if (!$participant instanceof Participant || !($participant->getPermissions() & Attendee::PERMISSIONS_CHAT)) {
+ // No chat permissions is like read-only
+ continue;
+ }
+
if (stripos($room->getDisplayName($userId), $search) !== false) {
$item = $this->roomToSearchResultItem($room, $userId);
diff --git a/lib/Command/ActiveCalls.php b/lib/Command/ActiveCalls.php
index a4ddf508d..48d75c15a 100644
--- a/lib/Command/ActiveCalls.php
+++ b/lib/Command/ActiveCalls.php
@@ -39,9 +39,11 @@ class ActiveCalls extends Base {
}
protected function configure(): void {
+ parent::configure();
+
$this
->setName('talk:active-calls')
- ->setDescription('Allows you to check if calls are currently in process');
+ ->setDescription('Allows you to check if calls are currently in process') ;
}
protected function execute(InputInterface $input, OutputInterface $output): int {
@@ -56,7 +58,12 @@ class ActiveCalls extends Base {
$result->closeCursor();
if ($numCalls === 0) {
- $output->writeln('<info>No calls in progress</info>');
+ if ($input->getOption('output') === 'plain') {
+ $output->writeln('<info>No calls in progress</info>');
+ } else {
+ $data = ['calls' => 0, 'participants' => 0];
+ $this->writeArrayInOutputFormat($input, $output, $data);
+ }
return 0;
}
@@ -70,7 +77,13 @@ class ActiveCalls extends Base {
$numParticipants = (int) $result->fetchColumn();
$result->closeCursor();
- $output->writeln(sprintf('<error>There are currently %1$d calls in progress with %2$d participants</error>', $numCalls, $numParticipants));
+
+ if ($input->getOption('output') === 'plain') {
+ $output->writeln(sprintf('<error>There are currently %1$d calls in progress with %2$d participants</error>', $numCalls, $numParticipants));
+ } else {
+ $data = ['calls' => $numCalls, 'participants' => $numParticipants];
+ $this->writeArrayInOutputFormat($input, $output, $data);
+ }
return 1;
}
}
diff --git a/lib/Controller/ChatController.php b/lib/Controller/ChatController.php
index 932c6c5ef..8e508d0b0 100644
--- a/lib/Controller/ChatController.php
+++ b/lib/Controller/ChatController.php
@@ -180,6 +180,7 @@ class ChatController extends AEnvironmentAwareController {
* @PublicPage
* @RequireParticipant
* @RequireReadWriteConversation
+ * @RequirePermissions(permissions=chat)
* @RequireModeratorOrNoLobby
*
* Sends a new chat message to the given room.
@@ -235,6 +236,7 @@ class ChatController extends AEnvironmentAwareController {
* @PublicPage
* @RequireParticipant
* @RequireReadWriteConversation
+ * @RequirePermissions(permissions=chat)
* @RequireModeratorOrNoLobby
*
* Sends a rich-object to the given room.
@@ -575,6 +577,7 @@ class ChatController extends AEnvironmentAwareController {
* @NoAdminRequired
* @RequireParticipant
* @RequireReadWriteConversation
+ * @RequirePermissions(permissions=chat)
* @RequireModeratorOrNoLobby
*
* @param int $messageId
@@ -725,7 +728,6 @@ class ChatController extends AEnvironmentAwareController {
/**
* @PublicPage
* @RequireParticipant
- * @RequireReadWriteConversation
* @RequireModeratorOrNoLobby
*
* @param int $limit
@@ -778,7 +780,6 @@ class ChatController extends AEnvironmentAwareController {
/**
* @PublicPage
* @RequireParticipant
- * @RequireReadWriteConversation
* @RequireModeratorOrNoLobby
*
* @param string $objectType
@@ -827,6 +828,7 @@ class ChatController extends AEnvironmentAwareController {
* @PublicPage
* @RequireParticipant
* @RequireReadWriteConversation
+ * @RequirePermissions(permissions=chat)
* @RequireModeratorOrNoLobby
*
* @param string $search
diff --git a/lib/Controller/ReactionController.php b/lib/Controller/ReactionController.php
index d55be4dbc..d99553914 100644
--- a/lib/Controller/ReactionController.php
+++ b/lib/Controller/ReactionController.php
@@ -45,9 +45,10 @@ class ReactionController extends AEnvironmentAwareController {
}
/**
- * @NoAdminRequired
+ * @PublicPage
* @RequireParticipant
* @RequireReadWriteConversation
+ * @RequirePermissions(permissions=chat)
* @RequireModeratorOrNoLobby
*
* @param int $messageId for reaction
@@ -75,9 +76,10 @@ class ReactionController extends AEnvironmentAwareController {
}
/**
- * @NoAdminRequired
+ * @PublicPage
* @RequireParticipant
* @RequireReadWriteConversation
+ * @RequirePermissions(permissions=chat)
* @RequireModeratorOrNoLobby
*
* @param int $messageId for reaction
@@ -103,9 +105,8 @@ class ReactionController extends AEnvironmentAwareController {
}
/**
- * @NoAdminRequired
+ * @PublicPage
* @RequireParticipant
- * @RequireReadWriteConversation
* @RequireModeratorOrNoLobby
*
* @param int $messageId for reaction
diff --git a/lib/Events/AlreadySharedEvent.php b/lib/Events/AlreadySharedEvent.php
new file mode 100644
index 000000000..665e65a03
--- /dev/null
+++ b/lib/Events/AlreadySharedEvent.php
@@ -0,0 +1,45 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2022, Vitor Mattos <vitor@php.rio>
+ *
+ * @author Vitor Mattos <vitor@php.rio>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OCA\Talk\Events;
+
+use OCP\EventDispatcher\Event;
+
+class AlreadySharedEvent extends Event {
+ private $subject;
+ public function __construct($subject = null) {
+ $this->subject = $subject;
+ }
+
+ /**
+ * Getter for subject property.
+ *
+ * @return mixed
+ */
+ public function getSubject() {
+ return $this->subject;
+ }
+}
diff --git a/lib/Exceptions/PermissionsException.php b/lib/Exceptions/PermissionsException.php
new file mode 100644
index 000000000..3677eb0d0
--- /dev/null
+++ b/lib/Exceptions/PermissionsException.php
@@ -0,0 +1,28 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2020 Julien Veyssier <eneiluj@posteo.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OCA\Talk\Exceptions;
+
+class PermissionsException extends \Exception {
+}
diff --git a/lib/Listener/RestrictStartingCalls.php b/lib/Listener/RestrictStartingCalls.php
index ed041ef0c..59e95639c 100644
--- a/lib/Listener/RestrictStartingCalls.php
+++ b/lib/Listener/RestrictStartingCalls.php
@@ -42,18 +42,16 @@ class RestrictStartingCalls {
}
public static function register(IEventDispatcher $dispatcher): void {
- $dispatcher->addListener(Room::EVENT_BEFORE_SESSION_JOIN_CALL, static function (ModifyParticipantEvent $event) {
- /** @var self $listener */
- $listener = \OC::$server->get(self::class);
- $listener->checkStartCallPermissions($event);
- }, 1000);
+ $dispatcher->addListener(Room::EVENT_BEFORE_SESSION_JOIN_CALL, [self::class, 'checkStartCallPermissions'], 1000);
}
/**
* @param ModifyParticipantEvent $event
* @throws ForbiddenException
*/
- public function checkStartCallPermissions(ModifyParticipantEvent $event): void {
+ public static function checkStartCallPermissions(ModifyParticipantEvent $event): void {
+ /** @var self $listener */
+ $listener = \OC::$server->get(self::class);
$room = $event->getRoom();
$participant = $event->getParticipant();
@@ -63,7 +61,7 @@ class RestrictStartingCalls {
return;
}
- if (!$participant->canStartCall($this->config) && !$this->participantService->hasActiveSessionsInCall($room)) {
+ if (!$participant->canStartCall($listener->config) && !$listener->participantService->hasActiveSessionsInCall($room)) {
throw new ForbiddenException('Can not start a call');
}
}
diff --git a/lib/Middleware/InjectionMiddleware.php b/lib/Middleware/InjectionMiddleware.php
index 18d98b1ac..0671aac8c 100644
--- a/lib/Middleware/InjectionMiddleware.php
+++ b/lib/Middleware/InjectionMiddleware.php
@@ -25,6 +25,7 @@ namespace OCA\Talk\Middleware;
use OCA\Talk\Controller\AEnvironmentAwareController;
use OCA\Talk\Exceptions\ParticipantNotFoundException;
+use OCA\Talk\Exceptions\PermissionsException;
use OCA\Talk\Exceptions\RoomNotFoundException;
use OCA\Talk\Manager;
use OCA\Talk\Middleware\Exceptions\LobbyException;
@@ -108,6 +109,11 @@ class InjectionMiddleware extends Middleware {
if ($this->reflector->hasAnnotation('RequireModeratorOrNoLobby')) {
$this->checkLobbyState($controller);
}
+
+ $requiredPermissions = $this->reflector->getAnnotationParameter('RequirePermissions', 'permissions');
+ if ($requiredPermissions) {
+ $this->checkPermissions($controller, $requiredPermissions);
+ }
}
/**
@@ -190,6 +196,24 @@ class InjectionMiddleware extends Middleware {
/**
* @param AEnvironmentAwareController $controller
+ * @throws PermissionsException
+ */
+ protected function checkPermissions(AEnvironmentAwareController $controller, string $permissions): void {
+ $textPermissions = explode(',', $permissions);
+ $participant = $controller->getParticipant();
+ if (!$participant instanceof Participant) {
+ throw new PermissionsException();
+ }
+
+ foreach ($textPermissions as $textPermission) {
+ if ($textPermission === 'chat' && !($participant->getPermissions() & Attendee::PERMISSIONS_CHAT)) {
+ throw new PermissionsException();
+ }
+ }
+ }
+
+ /**
+ * @param AEnvironmentAwareController $controller
* @throws LobbyException
*/
protected function checkLobbyState(AEnvironmentAwareController $controller): void {
@@ -238,7 +262,8 @@ class InjectionMiddleware extends Middleware {
}
if ($exception instanceof NotAModeratorException ||
- $exception instanceof ReadOnlyException) {
+ $exception instanceof ReadOnlyException ||
+ $exception instanceof PermissionsException) {
if ($controller instanceof OCSController) {
throw new OCSException('', Http::STATUS_FORBIDDEN);
}
diff --git a/lib/Migration/Version15000Date20220427183026.php b/lib/Migration/Version15000Date20220427183026.php
new file mode 100644
index 000000000..37c0d3a50
--- /dev/null
+++ b/lib/Migration/Version15000Date20220427183026.php
@@ -0,0 +1,113 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2022 Joas Schilling <coding@schilljs.com>
+ *
+ * @author Joas Schilling <coding@schilljs.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Talk\Migration;
+
+use Closure;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\IDBConnection;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version15000Date20220427183026 extends SimpleMigrationStep {
+ protected IDBConnection $connection;
+
+ public function __construct(IDBConnection $connection) {
+ $this->connection = $connection;
+ }
+
+ /**
+ * Update existing permissions by adding the chat permissions when set to none default
+ *
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `\OCP\DB\ISchemaWrapper`
+ * @param array $options
+ */
+ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
+ $update = $this->connection->getQueryBuilder();
+ $update->update('talk_rooms')
+ ->set('default_permissions', $update->func()->add(
+ 'default_permissions',
+ $update->createNamedParameter(128, IQueryBuilder::PARAM_INT) // Attendee::PERMISSION_CHAT
+ ))
+ ->where($update->expr()->neq('default_permissions', $update->createNamedParameter(0, IQueryBuilder::PARAM_INT))) // Attendee::PERMISSIONS_DEFAULT
+ ->andWhere(
+ $update->expr()->neq(
+ $update->expr()->castColumn(
+ $update->expr()->bitwiseAnd(
+ 'default_permissions',
+ 128 // Attendee::PERMISSION_CHAT
+ ),
+ IQueryBuilder::PARAM_INT
+ ),
+ $update->createNamedParameter(128, IQueryBuilder::PARAM_INT) // Attendee::PERMISSION_CHAT
+ )
+ );
+ $update->executeStatement();
+
+ $update = $this->connection->getQueryBuilder();
+ $update->update('talk_rooms')
+ ->set('call_permissions', $update->func()->add(
+ 'call_permissions',
+ $update->createNamedParameter(128, IQueryBuilder::PARAM_INT) // Attendee::PERMISSION_CHAT
+ ))
+ ->where($update->expr()->neq('call_permissions', $update->createNamedParameter(0, IQueryBuilder::PARAM_INT))) // Attendee::PERMISSIONS_DEFAULT
+ ->andWhere(
+ $update->expr()->neq(
+ $update->expr()->castColumn(
+ $update->expr()->bitwiseAnd(
+ 'call_permissions',
+ 128 // Attendee::PERMISSION_CHAT
+ ),
+ IQueryBuilder::PARAM_INT
+ ),
+ $update->createNamedParameter(128, IQueryBuilder::PARAM_INT) // Attendee::PERMISSION_CHAT
+ )
+ );
+ $update->executeStatement();
+
+ $update = $this->connection->getQueryBuilder();
+ $update->update('talk_attendees')
+ ->set('permissions', $update->func()->add(
+ 'permissions',
+ $update->createNamedParameter(128, IQueryBuilder::PARAM_INT) // Attendee::PERMISSION_CHAT
+ ))
+ ->where($update->expr()->neq('permissions', $update->createNamedParameter(0, IQueryBuilder::PARAM_INT))) // Attendee::PERMISSIONS_DEFAULT
+ ->andWhere(
+ $update->expr()->neq(
+ $update->expr()->castColumn(
+ $update->expr()->bitwiseAnd(
+ 'permissions',
+ 128 // Attendee::PERMISSION_CHAT
+ ),
+ IQueryBuilder::PARAM_INT
+ ),
+ $update->createNamedParameter(128, IQueryBuilder::PARAM_INT) // Attendee::PERMISSION_CHAT
+ )
+ );
+ $update->executeStatement();
+ }
+}
diff --git a/lib/Model/Attendee.php b/lib/Model/Attendee.php
index 32c03f666..cabda580f 100644
--- a/lib/Model/Attendee.php
+++ b/lib/Model/Attendee.php
@@ -78,6 +78,7 @@ class Attendee extends Entity {
public const PERMISSIONS_PUBLISH_AUDIO = 16;
public const PERMISSIONS_PUBLISH_VIDEO = 32;
public const PERMISSIONS_PUBLISH_SCREEN = 64;
+ public const PERMISSIONS_CHAT = 128;
public const PERMISSIONS_MAX_DEFAULT = // Max int (when all permissions are granted as default)
self::PERMISSIONS_CALL_START
| self::PERMISSIONS_CALL_JOIN
@@ -85,6 +86,7 @@ class Attendee extends Entity {
| self::PERMISSIONS_PUBLISH_AUDIO
| self::PERMISSIONS_PUBLISH_VIDEO
| self::PERMISSIONS_PUBLISH_SCREEN
+ | self::PERMISSIONS_CHAT
;
public const PERMISSIONS_MAX_CUSTOM = self::PERMISSIONS_MAX_DEFAULT | self::PERMISSIONS_CUSTOM; // Max int (when all permissions are granted as custom)
diff --git a/lib/Share/RoomShareProvider.php b/lib/Share/RoomShareProvider.php
index 28dbfef15..24b1aa82d 100644
--- a/lib/Share/RoomShareProvider.php
+++ b/lib/Share/RoomShareProvider.php
@@ -29,11 +29,12 @@ declare(strict_types=1);
namespace OCA\Talk\Share;
use OC\Files\Cache\Cache;
+use OCA\Talk\Events\AlreadySharedEvent;
use OCA\Talk\Events\RoomEvent;
use OCA\Talk\Exceptions\ParticipantNotFoundException;
use OCA\Talk\Exceptions\RoomNotFoundException;
use OCA\Talk\Manager;
-use OCA\Talk\Participant;
+use OCA\Talk\Model\Attendee;
use OCA\Talk\Room;
use OCA\Talk\Service\ParticipantService;
use OCP\AppFramework\Utility\ITimeFactory;
@@ -50,8 +51,6 @@ use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager as IShareManager;
use OCP\Share\IShare;
use OCP\Share\IShareProvider;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-use Symfony\Component\EventDispatcher\GenericEvent;
/**
* Share provider for room shares.
@@ -71,10 +70,12 @@ class RoomShareProvider implements IShareProvider {
public const TALK_FOLDER = '/Talk';
public const TALK_FOLDER_PLACEHOLDER = '/{TALK_PLACEHOLDER}';
+ public const EVENT_SHARE_FILE_AGAIN = self::class . '::shareFileAgain';
+
private IDBConnection $dbConnection;
private ISecureRandom $secureRandom;
private IShareManager $shareManager;
- private EventDispatcherInterface $dispatcher;
+ private IEventDispatcher $dispatcher;
private Manager $manager;
private ParticipantService $participantService;
protected ITimeFactory $timeFactory;
@@ -85,7 +86,7 @@ class RoomShareProvider implements IShareProvider {
IDBConnection $connection,
ISecureRandom $secureRandom,
IShareManager $shareManager,
- EventDispatcherInterface $dispatcher,
+ IEventDispatcher $dispatcher,
Manager $manager,
ParticipantService $participantService,
ITimeFactory $timeFactory,
@@ -142,18 +143,23 @@ class RoomShareProvider implements IShareProvider {
}
try {
- $room->getParticipant($share->getSharedBy(), false);
+ $participant = $room->getParticipant($share->getSharedBy(), false);
} catch (ParticipantNotFoundException $e) {
// If the sharer is not a participant of the room even if the room
// exists the error is still "Room not found".
throw new GenericShareException('Room not found', $this->l->t('Conversation not found'), 404);
}
+ if (!($participant->getPermissions() & Attendee::PERMISSIONS_CHAT)) {
+ // No chat permissions is like read-only
+ throw new GenericShareException('Room not found', $this->l->t('Conversation not found'), 404);
+ }
+
$existingShares = $this->getSharesByPath($share->getNode());
foreach ($existingShares as $existingShare) {
if ($existingShare->getSharedWith() === $share->getSharedWith()) {
// FIXME Should be moved away from GenericEvent as soon as OCP\Share20\IManager did move too
- $this->dispatcher->dispatch(self::class . '::' . 'share_file_again', new GenericEvent($existingShare));
+ $this->dispatcher->dispatch(self::EVENT_SHARE_FILE_AGAIN, new AlreadySharedEvent($existingShare));
throw new GenericShareException('Already shared', $this->l->t('Path is already shared with this room'), 403);
}
}
diff --git a/lib/Signaling/Listener.php b/lib/Signaling/Listener.php
index 26af22371..899932108 100644
--- a/lib/Signaling/Listener.php
+++ b/lib/Signaling/Listener.php
@@ -54,312 +54,315 @@ class Listener {
}
protected static function registerInternalSignaling(IEventDispatcher $dispatcher): void {
- $listener = static function (RoomEvent $event): void {
- if (!self::isUsingInternalSignaling()) {
- return;
- }
+ $dispatcher->addListener(Room::EVENT_AFTER_ROOM_CONNECT, [self::class, 'refreshParticipantListUsingRoomEvent']);
+ $dispatcher->addListener(Room::EVENT_AFTER_GUEST_CONNECT, [self::class, 'refreshParticipantListUsingRoomEvent']);
+ $dispatcher->addListener(Room::EVENT_AFTER_SESSION_JOIN_CALL, [self::class, 'refreshParticipantListUsingRoomEvent']);
+ $dispatcher->addListener(Room::EVENT_AFTER_SESSION_UPDATE_CALL_FLAGS, [self::class, 'refreshParticipantListUsingRoomEvent']);
+ $dispatcher->addListener(Room::EVENT_AFTER_SESSION_LEAVE_CALL, [self::class, 'refreshParticipantListUsingRoomEvent']);
+ $dispatcher->addListener(Room::EVENT_AFTER_PERMISSIONS_SET, [self::class, 'refreshParticipantListUsingRoomEvent']);
+ $dispatcher->addListener(GuestManager::EVENT_AFTER_NAME_UPDATE, [self::class, 'refreshParticipantListUsingRoomEvent']);
+ $dispatcher->addListener(Room::EVENT_BEFORE_ROOM_DELETE, [self::class, 'refreshParticipantListUsingRoomEvent']);
+
+ $dispatcher->addListener(Room::EVENT_BEFORE_USER_REMOVE, [self::class, 'refreshParticipantListUsingParticipantEvent']);
+ $dispatcher->addListener(Room::EVENT_BEFORE_PARTICIPANT_REMOVE, [self::class, 'refreshParticipantListUsingParticipantEvent']);
+ $dispatcher->addListener(Room::EVENT_BEFORE_ROOM_DISCONNECT, [self::class, 'refreshParticipantListUsingParticipantEvent']);
+ $dispatcher->addListener(Room::EVENT_AFTER_PARTICIPANT_PERMISSIONS_SET, [self::class, 'refreshParticipantListUsingParticipantEvent']);
+ }
- /** @var Messages $messages */
- $messages = \OC::$server->get(Messages::class);
- $messages->addMessageForAllParticipants($event->getRoom(), 'refresh-participant-list');
- };
- $dispatcher->addListener(Room::EVENT_AFTER_ROOM_CONNECT, $listener);
- $dispatcher->addListener(Room::EVENT_AFTER_GUEST_CONNECT, $listener);
- $dispatcher->addListener(Room::EVENT_AFTER_SESSION_JOIN_CALL, $listener);
- $dispatcher->addListener(Room::EVENT_AFTER_SESSION_UPDATE_CALL_FLAGS, $listener);
- $dispatcher->addListener(Room::EVENT_AFTER_SESSION_LEAVE_CALL, $listener);
- $dispatcher->addListener(Room::EVENT_AFTER_PERMISSIONS_SET, $listener);
- $dispatcher->addListener(GuestManager::EVENT_AFTER_NAME_UPDATE, $listener);
-
- $listener = static function (ParticipantEvent $event): void {
- if (!self::isUsingInternalSignaling()) {
- return;
- }
+ protected static function registerExternalSignaling(IEventDispatcher $dispatcher): void {
+ $dispatcher->addListener(Room::EVENT_AFTER_USERS_ADD, [self::class, 'notifyAfterUsersAdd']);
+ $dispatcher->addListener(Room::EVENT_AFTER_NAME_SET, [self::class, 'notifyAfterRoomSettingsChanged']);
+ $dispatcher->addListener(Room::EVENT_AFTER_DESCRIPTION_SET, [self::class, 'notifyAfterRoomSettingsChanged']);
+ $dispatcher->addListener(Room::EVENT_AFTER_PASSWORD_SET, [self::class, 'notifyAfterRoomSettingsChanged']);
+ $dispatcher->addListener(Room::EVENT_AFTER_TYPE_SET, [self::class, 'notifyAfterRoomSettingsChanged']);
+ $dispatcher->addListener(Room::EVENT_AFTER_READONLY_SET, [self::class, 'notifyAfterRoomSettingsChanged']);
+ $dispatcher->addListener(Room::EVENT_AFTER_LISTABLE_SET, [self::class, 'notifyAfterRoomSettingsChanged']);
+ $dispatcher->addListener(Room::EVENT_AFTER_LOBBY_STATE_SET, [self::class, 'notifyAfterRoomSettingsChanged']);
+ $dispatcher->addListener(Room::EVENT_AFTER_SIP_ENABLED_SET, [self::class, 'notifyAfterRoomSettingsChanged']);
+ // TODO remove handler with "roomModified" in favour of handler with
+ // "participantsModified" once the clients no longer expect a
+ // "roomModified" message for participant type changes.
+ $dispatcher->addListener(Room::EVENT_AFTER_PARTICIPANT_TYPE_SET, [self::class, 'notifyAfterRoomSettingsChanged']);
+ $dispatcher->addListener(Room::EVENT_AFTER_PARTICIPANT_TYPE_SET, [self::class, 'notifyAfterParticipantTypeAndPermissionsSet']);
+ $dispatcher->addListener(Room::EVENT_AFTER_PARTICIPANT_PERMISSIONS_SET, [self::class, 'notifyAfterParticipantTypeAndPermissionsSet']);
+ $dispatcher->addListener(Room::EVENT_AFTER_PERMISSIONS_SET, [self::class, 'notifyAfterPermissionSet']);
+ $dispatcher->addListener(Room::EVENT_BEFORE_ROOM_DELETE, [self::class, 'notifyBeforeRoomDeleted']);
+ $dispatcher->addListener(Room::EVENT_AFTER_USER_REMOVE, [self::class, 'notifyAfterUserRemoved']);
+ $dispatcher->addListener(Room::EVENT_AFTER_PARTICIPANT_REMOVE, [self::class, 'notifyAfterParticipantRemoved']);
+ $dispatcher->addListener(Room::EVENT_AFTER_ROOM_DISCONNECT, [self::class, 'notifyAfterRoomDisconected']);
+ $dispatcher->addListener(Room::EVENT_AFTER_SESSION_JOIN_CALL, [self::class, 'notifyAfterJoinUpdateAndLeave']);
+ $dispatcher->addListener(Room::EVENT_AFTER_SESSION_UPDATE_CALL_FLAGS, [self::class, 'notifyAfterJoinUpdateAndLeave']);
+ $dispatcher->addListener(Room::EVENT_AFTER_SESSION_LEAVE_CALL, [self::class, 'notifyAfterJoinUpdateAndLeave']);
+ $dispatcher->addListener(Room::EVENT_AFTER_END_CALL_FOR_EVERYONE, [self::class, 'sendEndCallForEveryone']);
+ $dispatcher->addListener(Room::EVENT_AFTER_GUESTS_CLEAN, [self::class, 'notifyParticipantsAfterGuestClean']);
+ $dispatcher->addListener(GuestManager::EVENT_AFTER_NAME_UPDATE, [self::class, 'notifyParticipantsAfterNameUpdated']);
+ $dispatcher->addListener(ChatManager::EVENT_AFTER_MESSAGE_SEND, [self::class, 'notifyUsersViaExternalSignalingToRefreshTheChat']);
+ $dispatcher->addListener(ChatManager::EVENT_AFTER_SYSTEM_MESSAGE_SEND, [self::class, 'notifyUsersViaExternalSignalingToRefreshTheChat']);
+ $dispatcher->addListener(ChatManager::EVENT_AFTER_MULTIPLE_SYSTEM_MESSAGE_SEND, [self::class, 'notifyUsersViaExternalSignalingToRefreshTheChat']);
+ }
- $room = $event->getRoom();
-
- /** @var Messages $messages */
- $messages = \OC::$server->get(Messages::class);
- $messages->addMessageForAllParticipants($room, 'refresh-participant-list');
- };
- $dispatcher->addListener(Room::EVENT_BEFORE_USER_REMOVE, $listener);
- $dispatcher->addListener(Room::EVENT_BEFORE_PARTICIPANT_REMOVE, $listener);
- $dispatcher->addListener(Room::EVENT_BEFORE_ROOM_DISCONNECT, $listener);
- $dispatcher->addListener(Room::EVENT_AFTER_PARTICIPANT_PERMISSIONS_SET, $listener);
-
- $listener = static function (RoomEvent $event): void {
- $room = $event->getRoom();
- if (!self::isUsingInternalSignaling()) {
- return;
- }
+ public static function refreshParticipantListUsingRoomEvent(RoomEvent $event): void {
+ if (!self::isUsingInternalSignaling()) {
+ return;
+ }
- /** @var Messages $messages */
- $messages = \OC::$server->get(Messages::class);
- $messages->addMessageForAllParticipants($room, 'refresh-participant-list');
- };
- $dispatcher->addListener(Room::EVENT_BEFORE_ROOM_DELETE, $listener);
+ /** @var Messages $messages */
+ $messages = \OC::$server->get(Messages::class);
+ $messages->addMessageForAllParticipants($event->getRoom(), 'refresh-participant-list');
}
- protected static function registerExternalSignaling(IEventDispatcher $dispatcher): void {
- $dispatcher->addListener(Room::EVENT_AFTER_USERS_ADD, static function (AddParticipantsEvent $event) {
- if (self::isUsingInternalSignaling()) {
- return;
- }
+ public static function refreshParticipantListUsingParticipantEvent(ParticipantEvent $event): void {
+ if (!self::isUsingInternalSignaling()) {
+ return;
+ }
- /** @var BackendNotifier $notifier */
- $notifier = \OC::$server->get(BackendNotifier::class);
+ /** @var Messages $messages */
+ $messages = \OC::$server->get(Messages::class);
+ $messages->addMessageForAllParticipants($event->getRoom(), 'refresh-participant-list');
+ }
- $notifier->roomInvited($event->getRoom(), $event->getParticipants());
- });
- $listener = static function (RoomEvent $event): void {
- if (self::isUsingInternalSignaling()) {
- return;
- }
+ public static function notifyAfterUsersAdd(AddParticipantsEvent $event): void {
+ if (self::isUsingInternalSignaling()) {
+ return;
+ }
- /** @var BackendNotifier $notifier */
- $notifier = \OC::$server->get(BackendNotifier::class);
-
- $notifier->roomModified($event->getRoom());
- };
- $dispatcher->addListener(Room::EVENT_AFTER_NAME_SET, $listener);
- $dispatcher->addListener(Room::EVENT_AFTER_DESCRIPTION_SET, $listener);
- $dispatcher->addListener(Room::EVENT_AFTER_PASSWORD_SET, $listener);
- $dispatcher->addListener(Room::EVENT_AFTER_TYPE_SET, $listener);
- $dispatcher->addListener(Room::EVENT_AFTER_READONLY_SET, $listener);
- $dispatcher->addListener(Room::EVENT_AFTER_LISTABLE_SET, $listener);
- $dispatcher->addListener(Room::EVENT_AFTER_LOBBY_STATE_SET, $listener);
- $dispatcher->addListener(Room::EVENT_AFTER_SIP_ENABLED_SET, $listener);
- // TODO remove handler with "roomModified" in favour of handler with
- // "participantsModified" once the clients no longer expect a
- // "roomModified" message for participant type changes.
- $dispatcher->addListener(Room::EVENT_AFTER_PARTICIPANT_TYPE_SET, $listener);
+ /** @var BackendNotifier $notifier */
+ $notifier = \OC::$server->get(BackendNotifier::class);
- $listener = static function (ModifyParticipantEvent $event): void {
- if (self::isUsingInternalSignaling()) {
- return;
- }
+ $notifier->roomInvited($event->getRoom(), $event->getParticipants());
+ }
- /** @var BackendNotifier $notifier */
- $notifier = \OC::$server->get(BackendNotifier::class);
+ public static function notifyAfterRoomSettingsChanged(RoomEvent $event): void {
+ if (self::isUsingInternalSignaling()) {
+ return;
+ }
- $sessionIds = [];
- // If the participant is not active in the room the "participants"
- // request will be sent anyway, although with an empty "changed"
- // property.
+ /** @var BackendNotifier $notifier */
+ $notifier = \OC::$server->get(BackendNotifier::class);
- /** @var SessionService $sessionService */
- $sessionService = \OC::$server->get(SessionService::class);
- $sessions = $sessionService->getAllSessionsForAttendee($event->getParticipant()->getAttendee());
- foreach ($sessions as $session) {
- $sessionIds[] = $session->getSessionId();
- }
+ $notifier->roomModified($event->getRoom());
+ }
- $notifier->participantsModified($event->getRoom(), $sessionIds);
- };
- $dispatcher->addListener(Room::EVENT_AFTER_PARTICIPANT_TYPE_SET, $listener);
- $dispatcher->addListener(Room::EVENT_AFTER_PARTICIPANT_PERMISSIONS_SET, $listener);
+ public static function notifyAfterParticipantTypeAndPermissionsSet(ModifyParticipantEvent $event): void {
+ if (self::isUsingInternalSignaling()) {
+ return;
+ }
- $dispatcher->addListener(Room::EVENT_AFTER_PERMISSIONS_SET, static function (RoomEvent $event) {
- if (self::isUsingInternalSignaling()) {
- return;
- }
+ /** @var BackendNotifier $notifier */
+ $notifier = \OC::$server->get(BackendNotifier::class);
- /** @var BackendNotifier $notifier */
- $notifier = \OC::$server->get(BackendNotifier::class);
-
- $sessionIds = [];
-
- // Setting the room permissions resets the permissions of all
- // participants, even those with custom attendee permissions.
-
- // FIXME This approach does not scale, as the update message for all
- // the sessions in a conversation can exceed the allowed size of the
- // request in conversations with a large number of participants.
- // However, note that a single message with the general permissions
- // to be set on all participants can not be sent either, as the
- // general permissions could be overriden by custom attendee
- // permissions in specific participants.
-
- /** @var ParticipantService $participantService */
- $participantService = \OC::$server->get(ParticipantService::class);
- $participants = $participantService->getSessionsAndParticipantsForRoom($event->getRoom());
- foreach ($participants as $participant) {
- $session = $participant->getSession();
- if ($session) {
- $sessionIds[] = $session->getSessionId();
- }
- }
+ $sessionIds = [];
+ // If the participant is not active in the room the "participants"
+ // request will be sent anyway, although with an empty "changed"
+ // property.
- $notifier->participantsModified($event->getRoom(), $sessionIds);
- });
+ /** @var SessionService $sessionService */
+ $sessionService = \OC::$server->get(SessionService::class);
+ $sessions = $sessionService->getAllSessionsForAttendee($event->getParticipant()->getAttendee());
+ foreach ($sessions as $session) {
+ $sessionIds[] = $session->getSessionId();
+ }
- $dispatcher->addListener(Room::EVENT_BEFORE_ROOM_DELETE, static function (RoomEvent $event) {
- if (self::isUsingInternalSignaling()) {
- return;
- }
+ $notifier->participantsModified($event->getRoom(), $sessionIds);
+ }
- /** @var BackendNotifier $notifier */
- $notifier = \OC::$server->get(BackendNotifier::class);
- /** @var ParticipantService $participantService */
- $participantService = \OC::$server->get(ParticipantService::class);
-
- $room = $event->getRoom();
- $notifier->roomDeleted($room, $participantService->getParticipantUserIds($room));
- });
- $dispatcher->addListener(Room::EVENT_AFTER_USER_REMOVE, static function (RemoveUserEvent $event) {
- if (self::isUsingInternalSignaling()) {
- return;
- }
+ public static function notifyAfterPermissionSet(RoomEvent $event): void {
+ if (self::isUsingInternalSignaling()) {
+ return;
+ }
- /** @var BackendNotifier $notifier */
- $notifier = \OC::$server->get(BackendNotifier::class);
+ /** @var BackendNotifier $notifier */
+ $notifier = \OC::$server->get(BackendNotifier::class);
- $notifier->roomsDisinvited($event->getRoom(), [$event->getUser()->getUID()]);
- });
- $dispatcher->addListener(Room::EVENT_AFTER_PARTICIPANT_REMOVE, static function (RemoveParticipantEvent $event) {
- if (self::isUsingInternalSignaling()) {
- return;
+ $sessionIds = [];
+
+ // Setting the room permissions resets the permissions of all
+ // participants, even those with custom attendee permissions.
+
+ // FIXME This approach does not scale, as the update message for all
+ // the sessions in a conversation can exceed the allowed size of the
+ // request in conversations with a large number of participants.
+ // However, note that a single message with the general permissions
+ // to be set on all participants can not be sent either, as the
+ // general permissions could be overriden by custom attendee
+ // permissions in specific participants.
+
+ /** @var ParticipantService $participantService */
+ $participantService = \OC::$server->get(ParticipantService::class);
+ $participants = $participantService->getSessionsAndParticipantsForRoom($event->getRoom());
+ foreach ($participants as $participant) {
+ $session = $participant->getSession();
+ if ($session) {
+ $sessionIds[] = $session->getSessionId();
}
+ }
- /** @var BackendNotifier $notifier */
- $notifier = \OC::$server->get(BackendNotifier::class);
+ $notifier->participantsModified($event->getRoom(), $sessionIds);
+ }
- $sessionIds = [];
+ public static function notifyBeforeRoomDeleted(RoomEvent $event): void {
+ if (self::isUsingInternalSignaling()) {
+ return;
+ }
- /** @var SessionService $sessionService */
- $sessionService = \OC::$server->get(SessionService::class);
- $sessions = $sessionService->getAllSessionsForAttendee($event->getParticipant()->getAttendee());
- foreach ($sessions as $session) {
- $sessionIds[] = $session->getSessionId();
- }
+ /** @var BackendNotifier $notifier */
+ $notifier = \OC::$server->get(BackendNotifier::class);
+ /** @var ParticipantService $participantService */
+ $participantService = \OC::$server->get(ParticipantService::class);
- if ($event->getParticipant()->getSession()) {
- $sessionIds[] = $event->getParticipant()->getSession()->getSessionId();
- $notifier->roomSessionsRemoved($event->getRoom(), $sessionIds);
- }
+ $room = $event->getRoom();
+ $notifier->roomDeleted($room, $participantService->getParticipantUserIds($room));
+ }
- if (!empty($sessionIds)) {
- $notifier->roomSessionsRemoved($event->getRoom(), $sessionIds);
- }
- });
- $dispatcher->addListener(Room::EVENT_AFTER_ROOM_DISCONNECT, static function (ParticipantEvent $event) {
- if (self::isUsingInternalSignaling()) {
- return;
- }
+ public static function notifyAfterUserRemoved(RemoveUserEvent $event): void {
+ if (self::isUsingInternalSignaling()) {
+ return;
+ }
- /** @var BackendNotifier $notifier */
- $notifier = \OC::$server->get(BackendNotifier::class);
-
- $sessionIds = [];
- if ($event->getParticipant()->getSession()) {
- // Only for guests and self-joined users disconnecting is "leaving" and therefor should trigger a disinvite
- $attendeeParticipantType = $event->getParticipant()->getAttendee()->getParticipantType();
- if ($attendeeParticipantType === Participant::GUEST
- || $attendeeParticipantType === Participant::GUEST_MODERATOR) {
- $sessionIds[] = $event->getParticipant()->getSession()->getSessionId();
- $notifier->roomSessionsRemoved($event->getRoom(), $sessionIds);
- }
- if ($attendeeParticipantType === Participant::USER_SELF_JOINED) {
- $notifier->roomsDisinvited($event->getRoom(), [$event->getParticipant()->getAttendee()->getActorId()]);
- }
- }
- });
+ /** @var BackendNotifier $notifier */
+ $notifier = \OC::$server->get(BackendNotifier::class);
- $listener = static function (ModifyParticipantEvent $event): void {
- if (self::isUsingInternalSignaling()) {
- return;
- }
+ $notifier->roomsDisinvited($event->getRoom(), [$event->getUser()->getUID()]);
+ }
- if ($event instanceof ModifyEveryoneEvent) {
- // If everyone is disconnected, we will not do O(n) requests.
- // Instead, the listener of Room::EVENT_AFTER_END_CALL_FOR_EVERYONE
- // will send all sessions to the HPB with 1 request.
- return;
- }
+ public static function notifyAfterParticipantRemoved(RemoveParticipantEvent $event): void {
+ if (self::isUsingInternalSignaling()) {
+ return;
+ }
+
+ /** @var BackendNotifier $notifier */
+ $notifier = \OC::$server->get(BackendNotifier::class);
- /** @var BackendNotifier $notifier */
- $notifier = \OC::$server->get(BackendNotifier::class);
+ $sessionIds = [];
- $sessionIds = [];
+ /** @var SessionService $sessionService */
+ $sessionService = \OC::$server->get(SessionService::class);
+ $sessions = $sessionService->getAllSessionsForAttendee($event->getParticipant()->getAttendee());
+ foreach ($sessions as $session) {
+ $sessionIds[] = $session->getSessionId();
+ }
- /** @var SessionService $sessionService */
- $sessionService = \OC::$server->get(SessionService::class);
- $sessions = $sessionService->getAllSessionsForAttendee($event->getParticipant()->getAttendee());
- foreach ($sessions as $session) {
- $sessionIds[] = $session->getSessionId();
- }
+ if ($event->getParticipant()->getSession()) {
+ $sessionIds[] = $event->getParticipant()->getSession()->getSessionId();
+ $notifier->roomSessionsRemoved($event->getRoom(), $sessionIds);
+ }
- if (!empty($sessionIds)) {
- $notifier->roomInCallChanged(
- $event->getRoom(),
- $event->getNewValue(),
- $sessionIds
- );
+ if (!empty($sessionIds)) {
+ $notifier->roomSessionsRemoved($event->getRoom(), $sessionIds);
+ }
+ }
+
+ public static function notifyAfterRoomDisconected(ParticipantEvent $event): void {
+ if (self::isUsingInternalSignaling()) {
+ return;
+ }
+
+ /** @var BackendNotifier $notifier */
+ $notifier = \OC::$server->get(BackendNotifier::class);
+
+ $sessionIds = [];
+ if ($event->getParticipant()->getSession()) {
+ // Only for guests and self-joined users disconnecting is "leaving" and therefor should trigger a disinvite
+ $attendeeParticipantType = $event->getParticipant()->getAttendee()->getParticipantType();
+ if ($attendeeParticipantType === Participant::GUEST
+ || $attendeeParticipantType === Participant::GUEST_MODERATOR) {
+ $sessionIds[] = $event->getParticipant()->getSession()->getSessionId();
+ $notifier->roomSessionsRemoved($event->getRoom(), $sessionIds);
}
- };
- $dispatcher->addListener(Room::EVENT_AFTER_SESSION_JOIN_CALL, $listener);
- $dispatcher->addListener(Room::EVENT_AFTER_SESSION_UPDATE_CALL_FLAGS, $listener);
- $dispatcher->addListener(Room::EVENT_AFTER_SESSION_LEAVE_CALL, $listener);
-
- $dispatcher->addListener(Room::EVENT_AFTER_END_CALL_FOR_EVERYONE, static function (EndCallForEveryoneEvent $event): void {
- if (self::isUsingInternalSignaling()) {
- return;
+ if ($attendeeParticipantType === Participant::USER_SELF_JOINED) {
+ $notifier->roomsDisinvited($event->getRoom(), [$event->getParticipant()->getAttendee()->getActorId()]);
}
+ }
+ }
- $sessionIds = $event->getSessionIds();
+ public static function notifyAfterJoinUpdateAndLeave(ModifyParticipantEvent $event): void {
+ if (self::isUsingInternalSignaling()) {
+ return;
+ }
- if (empty($sessionIds)) {
- return;
- }
+ if ($event instanceof ModifyEveryoneEvent) {
+ // If everyone is disconnected, we will not do O(n) requests.
+ // Instead, the listener of Room::EVENT_AFTER_END_CALL_FOR_EVERYONE
+ // will send all sessions to the HPB with 1 request.
+ return;
+ }
+
+ /** @var BackendNotifier $notifier */
+ $notifier = \OC::$server->get(BackendNotifier::class);
+
+ $sessionIds = [];
- /** @var BackendNotifier $notifier */
- $notifier = \OC::$server->get(BackendNotifier::class);
+ /** @var SessionService $sessionService */
+ $sessionService = \OC::$server->get(SessionService::class);
+ $sessions = $sessionService->getAllSessionsForAttendee($event->getParticipant()->getAttendee());
+ foreach ($sessions as $session) {
+ $sessionIds[] = $session->getSessionId();
+ }
+ if (!empty($sessionIds)) {
$notifier->roomInCallChanged(
$event->getRoom(),
$event->getNewValue(),
$sessionIds
);
- });
+ }
+ }
- $dispatcher->addListener(Room::EVENT_AFTER_GUESTS_CLEAN, static function (RoomEvent $event) {
- if (self::isUsingInternalSignaling()) {
- return;
- }
+ public static function sendEndCallForEveryone(EndCallForEveryoneEvent $event): void {
+ if (self::isUsingInternalSignaling()) {
+ return;
+ }
- /** @var BackendNotifier $notifier */
- $notifier = \OC::$server->get(BackendNotifier::class);
+ $sessionIds = $event->getSessionIds();
- // TODO: The list of removed session ids should be passed through the event
- // so the signaling server can optimize forwarding the message.
- $sessionIds = [];
- $notifier->participantsModified($event->getRoom(), $sessionIds);
- });
- $dispatcher->addListener(GuestManager::EVENT_AFTER_NAME_UPDATE, static function (ModifyParticipantEvent $event) {
- if (self::isUsingInternalSignaling()) {
- return;
- }
+ if (empty($sessionIds)) {
+ return;
+ }
- /** @var BackendNotifier $notifier */
- $notifier = \OC::$server->get(BackendNotifier::class);
+ /** @var BackendNotifier $notifier */
+ $notifier = \OC::$server->get(BackendNotifier::class);
- $sessionIds = [];
+ $notifier->roomInCallChanged(
+ $event->getRoom(),
+ $event->getNewValue(),
+ $sessionIds
+ );
+ }
- /** @var SessionService $sessionService */
- $sessionService = \OC::$server->get(SessionService::class);
- $sessions = $sessionService->getAllSessionsForAttendee($event->getParticipant()->getAttendee());
- foreach ($sessions as $session) {
- $sessionIds[] = $session->getSessionId();
- }
+ public static function notifyParticipantsAfterGuestClean(RoomEvent $event): void {
+ if (self::isUsingInternalSignaling()) {
+ return;
+ }
- if (!empty($sessionIds)) {
- $notifier->participantsModified($event->getRoom(), $sessionIds);
- }
- });
+ /** @var BackendNotifier $notifier */
+ $notifier = \OC::$server->get(BackendNotifier::class);
- $dispatcher->addListener(ChatManager::EVENT_AFTER_MESSAGE_SEND, [self::class, 'notifyUsersViaExternalSignalingToRefreshTheChat']);
- $dispatcher->addListener(ChatManager::EVENT_AFTER_SYSTEM_MESSAGE_SEND, [self::class, 'notifyUsersViaExternalSignalingToRefreshTheChat']);
- $dispatcher->addListener(ChatManager::EVENT_AFTER_MULTIPLE_SYSTEM_MESSAGE_SEND, [self::class, 'notifyUsersViaExternalSignalingToRefreshTheChat']);
+ // TODO: The list of removed session ids should be passed through the event
+ // so the signaling server can optimize forwarding the message.
+ $sessionIds = [];
+ $notifier->participantsModified($event->getRoom(), $sessionIds);
+ }
+
+ public static function notifyParticipantsAfterNameUpdated(ModifyParticipantEvent $event): void {
+ if (self::isUsingInternalSignaling()) {
+ return;
+ }
+
+ /** @var BackendNotifier $notifier */
+ $notifier = \OC::$server->get(BackendNotifier::class);
+
+ $sessionIds = [];
+
+ /** @var SessionService $sessionService */
+ $sessionService = \OC::$server->get(SessionService::class);
+ $sessions = $sessionService->getAllSessionsForAttendee($event->getParticipant()->getAttendee());
+ foreach ($sessions as $session) {
+ $sessionIds[] = $session->getSessionId();
+ }
+
+ if (!empty($sessionIds)) {
+ $notifier->participantsModified($event->getRoom(), $sessionIds);
+ }
}
public static function notifyUsersViaExternalSignalingToRefreshTheChat(ChatEvent $event): void {
diff --git a/lib/Status/Listener.php b/lib/Status/Listener.php
index 4bf4e7ffe..80b8722aa 100644
--- a/lib/Status/Listener.php
+++ b/lib/Status/Listener.php
@@ -43,32 +43,24 @@ class Listener {
}
public static function register(IEventDispatcher $dispatcher): void {
- $dispatcher->addListener(Room::EVENT_BEFORE_SESSION_JOIN_CALL, static function (ModifyParticipantEvent $event) {
- /** @var self $listener */
- $listener = \OC::$server->get(self::class);
- $listener->setUserStatus($event);
- });
+ $dispatcher->addListener(Room::EVENT_BEFORE_SESSION_JOIN_CALL, [self::class, 'setUserStatus']);
- $dispatcher->addListener(Room::EVENT_AFTER_SESSION_LEAVE_CALL, static function (ModifyParticipantEvent $event) {
- /** @var self $listener */
- $listener = \OC::$server->get(self::class);
- $listener->revertUserStatus($event);
- });
+ $dispatcher->addListener(Room::EVENT_AFTER_SESSION_LEAVE_CALL, [self::class, 'revertUserStatus']);
- $dispatcher->addListener(Room::EVENT_AFTER_END_CALL_FOR_EVERYONE, static function (EndCallForEveryoneEvent $event) {
- /** @var self $listener */
- $listener = \OC::$server->get(self::class);
- $listener->revertUserStatusOnEndCallForEveryone($event);
- });
+ $dispatcher->addListener(Room::EVENT_AFTER_END_CALL_FOR_EVERYONE, [self::class, 'revertUserStatusOnEndCallForEveryone']);
}
- public function setUserStatus(ModifyParticipantEvent $event): void {
+ public static function setUserStatus(ModifyParticipantEvent $event): void {
+ /** @var self $listener */
+ $listener = \OC::$server->get(self::class);
if ($event->getParticipant()->getAttendee()->getActorType() === Attendee::ACTOR_USERS) {
- $this->statusManager->setUserStatus($event->getParticipant()->getAttendee()->getActorId(), 'call', IUserStatus::AWAY, true);
+ $listener->statusManager->setUserStatus($event->getParticipant()->getAttendee()->getActorId(), 'call', IUserStatus::AWAY, true);
}
}
- public function revertUserStatus(ModifyParticipantEvent $event): void {
+ public static function revertUserStatus(ModifyParticipantEvent $event): void {
+ /** @var self $listener */
+ $listener = \OC::$server->get(self::class);
if ($event instanceof ModifyEveryoneEvent) {
// Do not revert the status with 3 queries per user.
// We will update it in one go at the end.
@@ -76,14 +68,16 @@ class Listener {
}
if ($event->getParticipant()->getAttendee()->getActorType() === Attendee::ACTOR_USERS) {
- $this->statusManager->revertUserStatus($event->getParticipant()->getAttendee()->getActorId(), 'call', IUserStatus::AWAY);
+ $listener->statusManager->revertUserStatus($event->getParticipant()->getAttendee()->getActorId(), 'call', IUserStatus::AWAY);
}
}
- public function revertUserStatusOnEndCallForEveryone(EndCallForEveryoneEvent $event): void {
+ public static function revertUserStatusOnEndCallForEveryone(EndCallForEveryoneEvent $event): void {
+ /** @var self $listener */
+ $listener = \OC::$server->get(self::class);
$userIds = $event->getUserIds();
if (!empty($userIds)) {
- $this->statusManager->revertMultipleUserStatus($userIds, 'call', IUserStatus::AWAY);
+ $listener->statusManager->revertMultipleUserStatus($userIds, 'call', IUserStatus::AWAY);
}
}
}
diff --git a/package-lock.json b/package-lock.json
index 003ab7be4..9bff9427a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,7 +18,7 @@
"@nextcloud/event-bus": "^2.1.1",
"@nextcloud/initial-state": "^1.2.1",
"@nextcloud/l10n": "^1.4.1",
- "@nextcloud/moment": "^1.2.0",
+ "@nextcloud/moment": "^1.2.1",
"@nextcloud/paths": "^2.1.0",
"@nextcloud/router": "^2.0.0",
"@nextcloud/vue": "^5.3.0",
@@ -54,7 +54,7 @@
"vue-shortkey": "^3.1.7",
"vue2-leaflet": "^2.7.1",
"vuex": "^3.6.2",
- "webdav": "^4.8.0",
+ "webdav": "^4.9.0",
"webrtc-adapter": "^8.1.1",
"webrtcsupport": "^2.2.0",
"wildemitter": "^1.2.1",
@@ -2744,14 +2744,14 @@
}
},
"node_modules/@nextcloud/moment": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@nextcloud/moment/-/moment-1.2.0.tgz",
- "integrity": "sha512-HOnZqoYQg0eOQW369s5v7jZWmRNYCsadHnVjN+DSXQQ1n4fHKmr0EkdOFHJu1Br5Rd6Fxi4wRw7E7pD1CVZmgA==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@nextcloud/moment/-/moment-1.2.1.tgz",
+ "integrity": "sha512-v/yfrZ4Jo8YM1v0DLXKjRLwKOhzE4Y6DcgyZAM1vJ5jOMvkHpICuTDJRw8oOtrr/1H6FqI6EMZcYogeGD+rwSA==",
"dependencies": {
- "@nextcloud/l10n": "1.4.1",
- "core-js": "3.18.2",
+ "@nextcloud/l10n": "^1.4.1",
+ "core-js": "^3.21.1",
"jed": "^1.1.1",
- "moment": "2.29.1",
+ "moment": "^2.29.2",
"node-gettext": "^3.0.0"
}
},
@@ -6224,11 +6224,11 @@
"dev": true
},
"node_modules/axios": {
- "version": "0.24.0",
- "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
- "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
+ "version": "0.26.1",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
+ "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
"dependencies": {
- "follow-redirects": "^1.14.4"
+ "follow-redirects": "^1.14.8"
}
},
"node_modules/babel-code-frame": {
@@ -8306,9 +8306,9 @@
}
},
"node_modules/core-js": {
- "version": "3.18.2",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.18.2.tgz",
- "integrity": "sha512-zNhPOUoSgoizoSQFdX1MeZO16ORRb9FFQLts8gSYbZU5FcgXhp24iMWMxnOQo5uIaIG7/6FA/IqJPwev1o9ZXQ==",
+ "version": "3.22.2",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.22.2.tgz",
+ "integrity": "sha512-Z5I2vzDnEIqO2YhELVMFcL1An2CIsFe9Q7byZhs8c/QxummxZlAHw33TUHbIte987LkisOgL0LwQ1P9D6VISnA==",
"hasInstallScript": true,
"funding": {
"type": "opencollective",
@@ -16273,9 +16273,9 @@
"dev": true
},
"node_modules/moment": {
- "version": "2.29.1",
- "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
- "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==",
+ "version": "2.29.3",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz",
+ "integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==",
"engines": {
"node": "*"
}
@@ -22353,22 +22353,41 @@
}
},
"node_modules/webdav": {
- "version": "4.8.0",
- "resolved": "https://registry.npmjs.org/webdav/-/webdav-4.8.0.tgz",
- "integrity": "sha512-CVJvxu0attEfoQUKraDiNh3uMjNPNl+BY0pbcKbyc/X+8IXDnqAT4tT4Ge12w+j49fYuVpFVkpEGwBZabv7Uhw==",
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/webdav/-/webdav-4.9.0.tgz",
+ "integrity": "sha512-pMuRtZcjBk3i6q1iY5wBHdablKftoBfhrQEWWEejSh2LXgd0J6VE5V0c1tUlMrFHaVDx8iCoB9kupNzy8SMC4A==",
"dependencies": {
- "axios": "^0.24.0",
+ "axios": "^0.26.1",
"base-64": "^1.0.0",
"fast-xml-parser": "^3.19.0",
"he": "^1.2.0",
"hot-patcher": "^0.5.0",
"layerr": "^0.1.2",
"md5": "^2.3.0",
- "minimatch": "^3.0.4",
+ "minimatch": "^5.0.1",
"nested-property": "^4.0.0",
"path-posix": "^1.0.0",
"url-join": "^4.0.1",
- "url-parse": "^1.5.3"
+ "url-parse": "^1.5.10"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/webdav/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/webdav/node_modules/minimatch": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
+ "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=10"
@@ -26525,14 +26544,14 @@
}
},
"@nextcloud/moment": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@nextcloud/moment/-/moment-1.2.0.tgz",
- "integrity": "sha512-HOnZqoYQg0eOQW369s5v7jZWmRNYCsadHnVjN+DSXQQ1n4fHKmr0EkdOFHJu1Br5Rd6Fxi4wRw7E7pD1CVZmgA==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@nextcloud/moment/-/moment-1.2.1.tgz",
+ "integrity": "sha512-v/yfrZ4Jo8YM1v0DLXKjRLwKOhzE4Y6DcgyZAM1vJ5jOMvkHpICuTDJRw8oOtrr/1H6FqI6EMZcYogeGD+rwSA==",
"requires": {
- "@nextcloud/l10n": "1.4.1",
- "core-js": "3.18.2",
+ "@nextcloud/l10n": "^1.4.1",
+ "core-js": "^3.21.1",
"jed": "^1.1.1",
- "moment": "2.29.1",
+ "moment": "^2.29.2",
"node-gettext": "^3.0.0"
}
},
@@ -29234,11 +29253,11 @@
"dev": true
},
"axios": {
- "version": "0.24.0",
- "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
- "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
+ "version": "0.26.1",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
+ "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
"requires": {
- "follow-redirects": "^1.14.4"
+ "follow-redirects": "^1.14.8"
}
},
"babel-code-frame": {
@@ -30948,9 +30967,9 @@
}
},
"core-js": {
- "version": "3.18.2",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.18.2.tgz",
- "integrity": "sha512-zNhPOUoSgoizoSQFdX1MeZO16ORRb9FFQLts8gSYbZU5FcgXhp24iMWMxnOQo5uIaIG7/6FA/IqJPwev1o9ZXQ=="
+ "version": "3.22.2",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.22.2.tgz",
+ "integrity": "sha512-Z5I2vzDnEIqO2YhELVMFcL1An2CIsFe9Q7byZhs8c/QxummxZlAHw33TUHbIte987LkisOgL0LwQ1P9D6VISnA=="
},
"core-js-compat": {
"version": "3.13.0",
@@ -37168,9 +37187,9 @@
"dev": true
},
"moment": {
- "version": "2.29.1",
- "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
- "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
+ "version": "2.29.3",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz",
+ "integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw=="
},
"mrmime": {
"version": "1.0.0",
@@ -42075,22 +42094,40 @@
}
},
"webdav": {
- "version": "4.8.0",
- "resolved": "https://registry.npmjs.org/webdav/-/webdav-4.8.0.tgz",
- "integrity": "sha512-CVJvxu0attEfoQUKraDiNh3uMjNPNl+BY0pbcKbyc/X+8IXDnqAT4tT4Ge12w+j49fYuVpFVkpEGwBZabv7Uhw==",
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/webdav/-/webdav-4.9.0.tgz",
+ "integrity": "sha512-pMuRtZcjBk3i6q1iY5wBHdablKftoBfhrQEWWEejSh2LXgd0J6VE5V0c1tUlMrFHaVDx8iCoB9kupNzy8SMC4A==",
"requires": {
- "axios": "^0.24.0",
+ "axios": "^0.26.1",
"base-64": "^1.0.0",
"fast-xml-parser": "^3.19.0",
"he": "^1.2.0",
"hot-patcher": "^0.5.0",
"layerr": "^0.1.2",
"md5": "^2.3.0",
- "minimatch": "^3.0.4",
+ "minimatch": "^5.0.1",
"nested-property": "^4.0.0",
"path-posix": "^1.0.0",
"url-join": "^4.0.1",
- "url-parse": "^1.5.3"
+ "url-parse": "^1.5.10"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "requires": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
+ "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
+ "requires": {
+ "brace-expansion": "^2.0.1"
+ }
+ }
}
},
"webidl-conversions": {
diff --git a/package.json b/package.json
index bc85e426d..2aa060a86 100644
--- a/package.json
+++ b/package.json
@@ -26,7 +26,7 @@
"@nextcloud/event-bus": "^2.1.1",
"@nextcloud/initial-state": "^1.2.1",
"@nextcloud/l10n": "^1.4.1",
- "@nextcloud/moment": "^1.2.0",
+ "@nextcloud/moment": "^1.2.1",
"@nextcloud/paths": "^2.1.0",
"@nextcloud/router": "^2.0.0",
"@nextcloud/vue": "^5.3.0",
@@ -62,7 +62,7 @@
"vue-shortkey": "^3.1.7",
"vue2-leaflet": "^2.7.1",
"vuex": "^3.6.2",
- "webdav": "^4.8.0",
+ "webdav": "^4.9.0",
"webrtc-adapter": "^8.1.1",
"webrtcsupport": "^2.2.0",
"wildemitter": "^1.2.1",
diff --git a/src/__mocks__/attachmediastream.js b/src/__mocks__/attachmediastream.js
new file mode 100644
index 000000000..74178ed27
--- /dev/null
+++ b/src/__mocks__/attachmediastream.js
@@ -0,0 +1,17 @@
+/**
+ * Basic "attachmediastream" implementation without using "webrtc-adapter", as
+ * "browserDetails" is null in unit tests.
+ *
+ * @param {MediaStream} stream the stream to attach
+ * @param {HTMLElement} element the element to attach the stream to
+ * @param {object} options ignored
+ */
+export default function(stream, element, options) {
+ if (!element) {
+ element = document.createElement(options.audio ? 'audio' : 'video')
+ }
+
+ element.srcObject = stream
+
+ return element
+}
diff --git a/src/components/AvatarWrapper/AvatarWrapper.vue b/src/components/AvatarWrapper/AvatarWrapper.vue
index 9f66580ad..ce9bcf668 100644
--- a/src/components/AvatarWrapper/AvatarWrapper.vue
+++ b/src/components/AvatarWrapper/AvatarWrapper.vue
@@ -139,18 +139,11 @@ export default {
}
.offline .avatar-wrapper .avatardiv {
- background: rgba(255, 255, 255, .4) !important;
+ background: rgba(var(--color-main-background-rgb), .4) !important;
::v-deep > img {
opacity: .4 !important;
}
}
-body.theme--dark {
- .offline .avatar-wrapper .avatardiv {
- background: rgba(0, 0, 0, .4) !important;
- margin-top: -4px;
- }
-}
-
</style>
diff --git a/src/components/CallView/CallView.vue b/src/components/CallView/CallView.vue
index 98b87f163..b34a17707 100644
--- a/src/components/CallView/CallView.vue
+++ b/src/components/CallView/CallView.vue
@@ -141,6 +141,7 @@ import { loadState } from '@nextcloud/initial-state'
import Grid from './Grid/Grid'
import { SIMULCAST } from '../../constants'
import { localMediaModel, localCallParticipantModel, callParticipantCollection } from '../../utils/webrtc/index'
+import RemoteVideoBlocker from '../../utils/webrtc/RemoteVideoBlocker'
import { fetchPeers } from '../../services/callsService'
import { showMessage } from '@nextcloud/dialogs'
import EmptyCallView from './shared/EmptyCallView'
@@ -212,7 +213,7 @@ export default {
callParticipantModelsWithVideo() {
return this.callParticipantModels.filter(callParticipantModel => {
return callParticipantModel.attributes.videoAvailable
- && this.sharedDatas[callParticipantModel.attributes.peerId].videoEnabled
+ && this.sharedDatas[callParticipantModel.attributes.peerId].remoteVideoBlocker.isVideoEnabled()
&& (typeof callParticipantModel.attributes.stream === 'object')
})
},
@@ -404,7 +405,6 @@ export default {
callParticipantCollection.on('remove', this._lowerHandWhenParticipantLeaves)
- subscribe('talk:video:toggled', this.handleToggleVideo)
subscribe('switch-screen-to-id', this._switchScreenToId)
},
beforeDestroy() {
@@ -412,7 +412,6 @@ export default {
callParticipantCollection.off('remove', this._lowerHandWhenParticipantLeaves)
- unsubscribe('talk:video:toggled', this.handleToggleVideo)
unsubscribe('switch-screen-to-id', this._switchScreenToId)
},
methods: {
@@ -454,7 +453,7 @@ export default {
addedModels.forEach(addedModel => {
const sharedData = {
promoted: false,
- videoEnabled: true,
+ remoteVideoBlocker: new RemoteVideoBlocker(addedModel),
screenVisible: false,
}
@@ -646,11 +645,6 @@ export default {
}
}, 1500),
- // Toggles videos on and off
- handleToggleVideo({ peerId, value }) {
- this.sharedDatas[peerId].videoEnabled = value
- },
-
adjustSimulcastQuality() {
this.callParticipantModels.forEach(callParticipantModel => {
this.adjustSimulcastQualityForParticipant(callParticipantModel)
diff --git a/src/components/CallView/Grid/Grid.vue b/src/components/CallView/Grid/Grid.vue
index c7fe91e73..63496c808 100644
--- a/src/components/CallView/Grid/Grid.vue
+++ b/src/components/CallView/Grid/Grid.vue
@@ -584,7 +584,11 @@ export default {
placeholderSharedData() {
return {
- videoEnabled: true,
+ videoEnabled: {
+ isVideoEnabled() {
+ return true
+ },
+ },
screenVisible: false,
}
},
diff --git a/src/components/CallView/shared/Video.spec.js b/src/components/CallView/shared/Video.spec.js
new file mode 100644
index 000000000..224a96521
--- /dev/null
+++ b/src/components/CallView/shared/Video.spec.js
@@ -0,0 +1,1147 @@
+/**
+ *
+ * @copyright Copyright (c) 2022, Daniel Calviño Sánchez (danxuliu@gmail.com)
+ *
+ * @license AGPL-3.0-or-later
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+import Vuex from 'vuex'
+import { createLocalVue, shallowMount } from '@vue/test-utils'
+import { cloneDeep } from 'lodash'
+import storeConfig from '../../../store/storeConfig'
+
+import EmitterMixin from '../../../utils/EmitterMixin'
+import CallParticipantModel from '../../../utils/webrtc/models/CallParticipantModel'
+
+import Video from './Video'
+
+describe('Video.vue', () => {
+ let localVue
+ let store
+ let testStoreConfig
+
+ let callParticipantModel
+
+ function PeerMock() {
+ this._superEmitterMixin()
+
+ this.id = 'theId'
+ this.nick = 'The nick'
+ this.pc = {
+ connectionState: 'new',
+ iceConnectionState: 'new',
+ signalingState: 'stable',
+ }
+ }
+ PeerMock.prototype._setIceConnectionState = function(iceConnectionState) {
+ this.pc.iceConnectionState = iceConnectionState
+ this._trigger('extendedIceConnectionStateChange', [iceConnectionState])
+ }
+ PeerMock.prototype._setSignalingState = function(signalingState) {
+ this.pc.signalingState = signalingState
+ this._trigger('signalingStateChange', [signalingState])
+ }
+ EmitterMixin.apply(PeerMock.prototype)
+ // Override _trigger from EmitterMixin, as it includes "this" as the first
+ // argument.
+ PeerMock.prototype._trigger = function(event, args) {
+ let handlers = this._handlers[event]
+ if (!handlers) {
+ return
+ }
+
+ if (!args) {
+ args = []
+ }
+
+ handlers = handlers.slice(0)
+ for (let i = 0; i < handlers.length; i++) {
+ const handler = handlers[i]
+ handler.apply(handler, args)
+ }
+ }
+
+ beforeEach(() => {
+ localVue = createLocalVue()
+ localVue.use(Vuex)
+
+ testStoreConfig = cloneDeep(storeConfig)
+ store = new Vuex.Store(testStoreConfig)
+
+ const webRtcMock = {
+ on: jest.fn(),
+ off: jest.fn(),
+ }
+ callParticipantModel = new CallParticipantModel({
+ peerId: 'theId',
+ webRtc: webRtcMock,
+ })
+ })
+
+ describe('connection state feedback', () => {
+ const connectionMessage = {
+ NOT_ESTABLISHED: 'Connection could not be established. Trying again …',
+ NOT_ESTABLISHED_NOT_RETRYING: 'Connection could not be established …',
+ LOST: 'Connection lost. Trying to reconnect …',
+ LOST_NOT_RETRYING: 'Connection was lost and could not be re-established …',
+ PROBLEMS: 'Connection problems …',
+ NONE: null,
+ }
+
+ let wrapper
+
+ // "setupWrapper()" needs to be called right before checking the wrapper
+ // to ensure that the component state is updated. If the wrapper is
+ // created at the beginning of each test "await Vue.nextTick()" would
+ // need to be called instead (and for that the tests would need to be
+ // async).
+ function setupWrapper() {
+ wrapper = shallowMount(Video, {
+ localVue,
+ store,
+ propsData: {
+ model: callParticipantModel,
+ token: 'theToken',
+ sharedData: {
+ remoteVideoBlocker: {
+ increaseVisibleCounter: jest.fn(),
+ },
+ promoted: false,
+ },
+ },
+ })
+ }
+
+ function assertConnectionMessageLabel(expectedText) {
+ const connectionMessageLabel = wrapper.find('.connection-message')
+ if (expectedText) {
+ expect(connectionMessageLabel.exists()).toBe(true)
+ expect(connectionMessageLabel.text()).toBe(expectedText)
+ } else {
+ expect(connectionMessageLabel.exists()).toBe(false)
+ }
+ }
+
+ function assertLoadingIconIsShown(expected) {
+ const loadingIcon = wrapper.find('.icon-loading')
+ expect(loadingIcon.exists()).toBe(expected)
+ }
+
+ function assertNotConnected(expected) {
+ const notConnected = wrapper.find('.not-connected')
+ expect(notConnected.exists()).toBe(expected)
+ }
+
+ test('participant just created', () => {
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NONE)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('no peer', () => {
+ callParticipantModel.setPeer(null)
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NONE)
+ assertLoadingIconIsShown(false)
+ assertNotConnected(false)
+ })
+
+ describe('original connection', () => {
+ let peerMock
+
+ beforeEach(() => {
+ peerMock = new PeerMock()
+ callParticipantModel.setPeer(peerMock)
+ })
+
+ test('peer just set', () => {
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NONE)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('sending offer', () => {
+ peerMock._setSignalingState('have-local-offer')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NONE)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('receiving offer', () => {
+ peerMock._setSignalingState('have-remote-offer')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NONE)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('negotiation finished', () => {
+ peerMock._setSignalingState('have-remote-offer')
+ peerMock._setSignalingState('stable')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NONE)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('establishing connection', () => {
+ peerMock._setSignalingState('have-remote-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NONE)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('connection established', () => {
+ peerMock._setSignalingState('have-remote-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('connected')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NONE)
+ assertLoadingIconIsShown(false)
+ assertNotConnected(false)
+ })
+
+ test('connection established (completed)', () => {
+ peerMock._setSignalingState('have-remote-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('connected')
+ peerMock._setIceConnectionState('completed')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NONE)
+ assertLoadingIconIsShown(false)
+ assertNotConnected(false)
+ })
+
+ test('disconnected', () => {
+ peerMock._setSignalingState('have-remote-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('connected')
+ peerMock._setIceConnectionState('disconnected')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.PROBLEMS)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('disconnected without ever connecting', () => {
+ peerMock._setSignalingState('have-remote-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('disconnected')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.PROBLEMS)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('disconnected long', () => {
+ peerMock._setSignalingState('have-remote-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('connected')
+ peerMock._setIceConnectionState('disconnected')
+ // Custom event emitted when there is no HPB and the connection
+ // has been disconnected for a few seconds
+ peerMock._trigger('extendedIceConnectionStateChange', ['disconnected-long'])
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.PROBLEMS)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('disconnected long without ever connecting', () => {
+ peerMock._setSignalingState('have-remote-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('disconnected')
+ // Custom event emitted when there is no HPB and the connection
+ // has been disconnected for a few seconds
+ peerMock._trigger('extendedIceConnectionStateChange', ['disconnected-long'])
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.PROBLEMS)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('failed', () => {
+ peerMock._setSignalingState('have-remote-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('connected')
+ peerMock._setIceConnectionState('disconnected')
+ peerMock._setIceConnectionState('failed')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.LOST)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('failed without ever connecting', () => {
+ peerMock._setSignalingState('have-remote-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('failed')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NOT_ESTABLISHED)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+ })
+
+ describe('renegotiation', () => {
+ let peerMock
+
+ beforeEach(() => {
+ peerMock = new PeerMock()
+ callParticipantModel.setPeer(peerMock)
+ })
+
+ test('started after connection established', () => {
+ peerMock._setSignalingState('have-remote-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('connected')
+ peerMock._setSignalingState('have-remote-offer')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NONE)
+ assertLoadingIconIsShown(false)
+ assertNotConnected(false)
+ })
+
+ test('started after connection established and then finished', () => {
+ peerMock._setSignalingState('have-remote-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('connected')
+ peerMock._setSignalingState('have-remote-offer')
+ peerMock._setSignalingState('stable')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NONE)
+ assertLoadingIconIsShown(false)
+ assertNotConnected(false)
+ })
+
+ test('started before disconnected', () => {
+ peerMock._setSignalingState('have-remote-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('connected')
+ peerMock._setSignalingState('have-remote-offer')
+ peerMock._setIceConnectionState('disconnected')
+
+ setupWrapper()
+
+ // FIXME The message should be "PROBLEMS" rather than "LOST", as
+ // the negotiation is not caused by the disconnection itself.
+ // However it does not seem to be an easy way to do it right
+ // now.
+ assertConnectionMessageLabel(connectionMessage.LOST)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('started before disconnected and then finished', () => {
+ peerMock._setSignalingState('have-remote-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('connected')
+ peerMock._setSignalingState('have-remote-offer')
+ peerMock._setIceConnectionState('disconnected')
+ peerMock._setSignalingState('stable')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.PROBLEMS)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('started after disconnected', () => {
+ peerMock._setSignalingState('have-remote-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('connected')
+ peerMock._setIceConnectionState('disconnected')
+ peerMock._setSignalingState('have-remote-offer')
+
+ setupWrapper()
+
+ // FIXME The message should be "PROBLEMS" rather than "LOST", as
+ // the negotiation is not caused by the disconnection itself.
+ // However it does not seem to be an easy way to do it right
+ // now.
+ assertConnectionMessageLabel(connectionMessage.LOST)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('started after disconnected and then finished', () => {
+ peerMock._setSignalingState('have-remote-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('connected')
+ peerMock._setIceConnectionState('disconnected')
+ peerMock._setSignalingState('have-remote-offer')
+ peerMock._setSignalingState('stable')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.PROBLEMS)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+ })
+
+ describe('reconnection after no original connection', () => {
+ let newPeerMock
+
+ beforeEach(() => {
+ callParticipantModel.setPeer(null)
+
+ newPeerMock = new PeerMock()
+ callParticipantModel.setPeer(newPeerMock)
+ })
+
+ test('peer just set', () => {
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NONE)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('sending offer', () => {
+ newPeerMock._setSignalingState('have-local-offer')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NONE)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('receiving offer', () => {
+ newPeerMock._setSignalingState('have-remote-offer')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NONE)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('negotiation finished', () => {
+ newPeerMock._setSignalingState('have-remote-offer')
+ newPeerMock._setSignalingState('stable')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NONE)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('establishing connection', () => {
+ newPeerMock._setSignalingState('have-remote-offer')
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NONE)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('connection established', () => {
+ newPeerMock._setSignalingState('have-remote-offer')
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+ newPeerMock._setIceConnectionState('connected')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NONE)
+ assertLoadingIconIsShown(false)
+ assertNotConnected(false)
+ })
+
+ test('connection established (completed)', () => {
+ newPeerMock._setSignalingState('have-remote-offer')
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+ newPeerMock._setIceConnectionState('connected')
+ newPeerMock._setIceConnectionState('completed')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NONE)
+ assertLoadingIconIsShown(false)
+ assertNotConnected(false)
+ })
+
+ test('disconnected', () => {
+ newPeerMock._setSignalingState('have-remote-offer')
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+ newPeerMock._setIceConnectionState('connected')
+ newPeerMock._setIceConnectionState('disconnected')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.PROBLEMS)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('disconnected without ever connecting', () => {
+ newPeerMock._setSignalingState('have-remote-offer')
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+ newPeerMock._setIceConnectionState('disconnected')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.PROBLEMS)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('disconnected long', () => {
+ newPeerMock._setSignalingState('have-remote-offer')
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+ newPeerMock._setIceConnectionState('connected')
+ newPeerMock._setIceConnectionState('disconnected')
+ // Custom event emitted when there is no HPB and the connection
+ // has been disconnected for a few seconds
+ newPeerMock._trigger('extendedIceConnectionStateChange', ['disconnected-long'])
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.PROBLEMS)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('disconnected long without ever connecting', () => {
+ newPeerMock._setSignalingState('have-remote-offer')
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+ newPeerMock._setIceConnectionState('disconnected')
+ // Custom event emitted when there is no HPB and the connection
+ // has been disconnected for a few seconds
+ newPeerMock._trigger('extendedIceConnectionStateChange', ['disconnected-long'])
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.PROBLEMS)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('failed', () => {
+ newPeerMock._setSignalingState('have-remote-offer')
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+ newPeerMock._setIceConnectionState('connected')
+ newPeerMock._setIceConnectionState('disconnected')
+ newPeerMock._setIceConnectionState('failed')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.LOST)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('failed without ever connecting', () => {
+ newPeerMock._setSignalingState('have-remote-offer')
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+ newPeerMock._setIceConnectionState('failed')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NOT_ESTABLISHED)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+ })
+
+ describe('reconnection', () => {
+ let peerMock
+ let newPeerMock
+
+ beforeEach(() => {
+ peerMock = new PeerMock()
+ callParticipantModel.setPeer(peerMock)
+
+ newPeerMock = new PeerMock()
+ })
+
+ describe('without having been connected', () => {
+ beforeEach(() => {
+ peerMock._setSignalingState('have-remote-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('failed')
+
+ callParticipantModel.setPeer(newPeerMock)
+ })
+
+ test('peer just set', () => {
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NOT_ESTABLISHED)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('sending offer', () => {
+ newPeerMock._setSignalingState('have-local-offer')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NOT_ESTABLISHED)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('receiving offer', () => {
+ newPeerMock._setSignalingState('have-remote-offer')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NOT_ESTABLISHED)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('negotiation finished', () => {
+ newPeerMock._setSignalingState('have-remote-offer')
+ newPeerMock._setSignalingState('stable')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NOT_ESTABLISHED)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('establishing connection', () => {
+ newPeerMock._setSignalingState('have-remote-offer')
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NOT_ESTABLISHED)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('connection established', () => {
+ newPeerMock._setSignalingState('have-remote-offer')
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+ newPeerMock._setIceConnectionState('connected')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NONE)
+ assertLoadingIconIsShown(false)
+ assertNotConnected(false)
+ })
+
+ test('disconnected', () => {
+ newPeerMock._setSignalingState('have-remote-offer')
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+ newPeerMock._setIceConnectionState('connected')
+ newPeerMock._setIceConnectionState('disconnected')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.PROBLEMS)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('disconnected without ever connecting', () => {
+ newPeerMock._setSignalingState('have-remote-offer')
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+ newPeerMock._setIceConnectionState('disconnected')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NOT_ESTABLISHED)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('disconnected long', () => {
+ newPeerMock._setSignalingState('have-remote-offer')
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+ newPeerMock._setIceConnectionState('connected')
+ newPeerMock._setIceConnectionState('disconnected')
+ // Custom event emitted when there is no HPB and the
+ // connection has been disconnected for a few seconds
+ newPeerMock._trigger('extendedIceConnectionStateChange', ['disconnected-long'])
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.PROBLEMS)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('disconnected long without ever connecting', () => {
+ newPeerMock._setSignalingState('have-remote-offer')
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+ newPeerMock._setIceConnectionState('disconnected')
+ // Custom event emitted when there is no HPB and the
+ // connection has been disconnected for a few seconds
+ newPeerMock._trigger('extendedIceConnectionStateChange', ['disconnected-long'])
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NOT_ESTABLISHED)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('failed', () => {
+ newPeerMock._setSignalingState('have-remote-offer')
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+ newPeerMock._setIceConnectionState('connected')
+ newPeerMock._setIceConnectionState('disconnected')
+ newPeerMock._setIceConnectionState('failed')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.LOST)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('failed without ever connecting', () => {
+ newPeerMock._setSignalingState('have-remote-offer')
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+ newPeerMock._setIceConnectionState('failed')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NOT_ESTABLISHED)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('failed without ever connecting and not retrying', () => {
+ newPeerMock._setSignalingState('have-remote-offer')
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+ newPeerMock._setIceConnectionState('failed')
+ // Custom event emitted when there is no HPB and the
+ // connection has failed several times in a row
+ newPeerMock._trigger('extendedIceConnectionStateChange', ['failed-no-restart'])
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NOT_ESTABLISHED_NOT_RETRYING)
+ assertLoadingIconIsShown(false)
+ assertNotConnected(true)
+ })
+ })
+
+ describe('after having been connected', () => {
+ beforeEach(() => {
+ peerMock._setSignalingState('have-remote-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('connected')
+ })
+
+ describe('without HPB', () => {
+ test('ICE restarted after disconnected long (no HPB)', () => {
+ peerMock._setIceConnectionState('disconnected')
+ // Custom event emitted when there is no HPB and the connection
+ // has been disconnected for a few seconds
+ peerMock._trigger('extendedIceConnectionStateChange', ['disconnected-long'])
+ peerMock._setSignalingState('have-local-offer')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.LOST)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('ICE restarted after failed (no HPB)', () => {
+ peerMock._setIceConnectionState('disconnected')
+ peerMock._setIceConnectionState('failed')
+ peerMock._setSignalingState('have-local-offer')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.LOST)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('negotiation finished', () => {
+ peerMock._setIceConnectionState('disconnected')
+ peerMock._setIceConnectionState('failed')
+ peerMock._setSignalingState('have-local-offer')
+ peerMock._setSignalingState('stable')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.LOST)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('establishing connection', () => {
+ peerMock._setIceConnectionState('disconnected')
+ peerMock._setIceConnectionState('failed')
+ peerMock._setSignalingState('have-local-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.LOST)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('connection established', () => {
+ peerMock._setIceConnectionState('disconnected')
+ peerMock._setIceConnectionState('failed')
+ peerMock._setSignalingState('have-local-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('connected')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NONE)
+ assertLoadingIconIsShown(false)
+ assertNotConnected(false)
+ })
+
+ test('connection established (completed)', () => {
+ peerMock._setIceConnectionState('disconnected')
+ peerMock._setIceConnectionState('failed')
+ peerMock._setSignalingState('have-local-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('connected')
+ peerMock._setIceConnectionState('completed')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NONE)
+ assertLoadingIconIsShown(false)
+ assertNotConnected(false)
+ })
+
+ test('disconnected', () => {
+ peerMock._setIceConnectionState('disconnected')
+ peerMock._setIceConnectionState('failed')
+ peerMock._setSignalingState('have-local-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('connected')
+ peerMock._setIceConnectionState('disconnected')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.PROBLEMS)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('disconnected without connecting the second time', () => {
+ peerMock._setIceConnectionState('disconnected')
+ peerMock._setIceConnectionState('failed')
+ peerMock._setSignalingState('have-local-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('disconnected')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.LOST)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('disconnected long', () => {
+ peerMock._setIceConnectionState('disconnected')
+ peerMock._setIceConnectionState('failed')
+ peerMock._setSignalingState('have-local-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('connected')
+ peerMock._setIceConnectionState('disconnected')
+ // Custom event emitted when there is no HPB and the connection
+ // has been disconnected for a few seconds
+ peerMock._trigger('extendedIceConnectionStateChange', ['disconnected-long'])
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.PROBLEMS)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('disconnected long without connecting the second time', () => {
+ peerMock._setIceConnectionState('disconnected')
+ peerMock._setIceConnectionState('failed')
+ peerMock._setSignalingState('have-local-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('disconnected')
+ // Custom event emitted when there is no HPB and the connection
+ // has been disconnected for a few seconds
+ peerMock._trigger('extendedIceConnectionStateChange', ['disconnected-long'])
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.LOST)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('failed', () => {
+ peerMock._setIceConnectionState('disconnected')
+ peerMock._setIceConnectionState('failed')
+ peerMock._setSignalingState('have-local-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('connected')
+ peerMock._setIceConnectionState('disconnected')
+ peerMock._setIceConnectionState('failed')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.LOST)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('failed without connecting the second time', () => {
+ peerMock._setIceConnectionState('disconnected')
+ peerMock._setIceConnectionState('failed')
+ peerMock._setSignalingState('have-local-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('failed')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.LOST)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('failed without connecting the second time and not retrying', () => {
+ peerMock._setIceConnectionState('disconnected')
+ peerMock._setIceConnectionState('failed')
+ peerMock._setSignalingState('have-local-offer')
+ peerMock._setSignalingState('stable')
+ peerMock._setIceConnectionState('checking')
+ peerMock._setIceConnectionState('failed')
+ // Custom event emitted when there is no HPB and the
+ // connection has failed several times in a row
+ peerMock._trigger('extendedIceConnectionStateChange', ['failed-no-restart'])
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.LOST_NOT_RETRYING)
+ assertLoadingIconIsShown(false)
+ assertNotConnected(true)
+ })
+ })
+
+ describe('with HPB', () => {
+ beforeEach(() => {
+ peerMock._setIceConnectionState('disconnected')
+ peerMock._setIceConnectionState('failed')
+
+ callParticipantModel.setPeer(newPeerMock)
+
+ newPeerMock._setSignalingState('have-remote-offer')
+ })
+
+ test('requested renegotiation after connection failed', () => {
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.LOST)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('negotiation finished', () => {
+ newPeerMock._setSignalingState('stable')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.LOST)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('establishing connection', () => {
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.LOST)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('connection established', () => {
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+ newPeerMock._setIceConnectionState('connected')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NONE)
+ assertLoadingIconIsShown(false)
+ assertNotConnected(false)
+ })
+
+ test('connection established (completed)', () => {
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+ newPeerMock._setIceConnectionState('connected')
+ newPeerMock._setIceConnectionState('completed')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.NONE)
+ assertLoadingIconIsShown(false)
+ assertNotConnected(false)
+ })
+
+ test('disconnected', () => {
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+ newPeerMock._setIceConnectionState('connected')
+ newPeerMock._setIceConnectionState('disconnected')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.PROBLEMS)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('disconnected without connecting the second time', () => {
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+ newPeerMock._setIceConnectionState('disconnected')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.LOST)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('failed', () => {
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+ newPeerMock._setIceConnectionState('connected')
+ newPeerMock._setIceConnectionState('disconnected')
+ newPeerMock._setIceConnectionState('failed')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.LOST)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+
+ test('failed without connecting the second time', () => {
+ newPeerMock._setSignalingState('stable')
+ newPeerMock._setIceConnectionState('checking')
+ newPeerMock._setIceConnectionState('failed')
+
+ setupWrapper()
+
+ assertConnectionMessageLabel(connectionMessage.LOST)
+ assertLoadingIconIsShown(true)
+ assertNotConnected(true)
+ })
+ })
+ })
+ })
+ })
+})
diff --git a/src/components/CallView/shared/Video.vue b/src/components/CallView/shared/Video.vue
index 617417a01..e1d792bec 100644
--- a/src/components/CallView/shared/Video.vue
+++ b/src/components/CallView/shared/Video.vue
@@ -180,12 +180,12 @@ export default {
return this.model.attributes.connectedAtLeastOnce
},
- isNotConnected() {
- return this.model.attributes.connectionState !== ConnectionState.CONNECTED && this.model.attributes.connectionState !== ConnectionState.COMPLETED
+ isConnected() {
+ return this.model.attributes.connectionState === ConnectionState.CONNECTED || this.model.attributes.connectionState === ConnectionState.COMPLETED
},
isLoading() {
- return this.isNotConnected && this.model.attributes.connectionState !== ConnectionState.FAILED_NO_RESTART
+ return !this.isConnected && this.model.attributes.connectionState !== ConnectionState.FAILED_NO_RESTART
},
isDisconnected() {
@@ -204,11 +204,15 @@ export default {
* received yet). Similarly both "negotiating" and "connecting" need to
* be checked, as the negotiation will start before the connection
* attempt is started.
+ *
+ * If the negotiation is done while there is still a connection it is
+ * not regarded as reconnecting, as in that case it is a renegotiation
+ * to update the current connection.
*/
isReconnecting() {
return this.model.attributes.connectionState === ConnectionState.FAILED
|| (!this.model.attributes.initialConnection
- && (this.model.attributes.negotiating || this.model.attributes.connecting))
+ && ((this.model.attributes.negotiating && !this.isConnected) || this.model.attributes.connecting))
},
isNoLongerTryingToReconnect() {
@@ -242,7 +246,7 @@ export default {
containerClass() {
return {
'videoContainer-dummy': this.placeholderForPromoted,
- 'not-connected': !this.placeholderForPromoted && this.isNotConnected,
+ 'not-connected': !this.placeholderForPromoted && !this.isConnected,
speaking: !this.placeholderForPromoted && this.model.attributes.speaking,
promoted: !this.placeholderForPromoted && this.sharedData.promoted && !this.isGrid,
'video-container-grid': this.isGrid,
@@ -371,7 +375,7 @@ export default {
},
hasVideo() {
- return this.model.attributes.videoAvailable && this.sharedData.videoEnabled && (typeof this.model.attributes.stream === 'object')
+ return !this.model.attributes.videoBlocked && this.model.attributes.videoAvailable && this.sharedData.remoteVideoBlocker.isVideoEnabled() && (typeof this.model.attributes.stream === 'object')
},
hasSelectedVideo() {
@@ -469,10 +473,16 @@ export default {
},
mounted() {
+ this.sharedData.remoteVideoBlocker.increaseVisibleCounter()
+
// Set initial state
this._setStream(this.model.attributes.stream)
},
+ destroyed() {
+ this.sharedData.remoteVideoBlocker.decreaseVisibleCounter()
+ },
+
methods: {
_setStream(stream) {
diff --git a/src/components/CallView/shared/VideoBottomBar.vue b/src/components/CallView/shared/VideoBottomBar.vue
index ca4b23477..74baadec2 100644
--- a/src/components/CallView/shared/VideoBottomBar.vue
+++ b/src/components/CallView/shared/VideoBottomBar.vue
@@ -200,11 +200,11 @@ export default {
},
showVideoButton() {
- return this.sharedData.videoEnabled
+ return this.sharedData.remoteVideoBlocker.isVideoEnabled()
},
videoButtonTooltip() {
- if (this.sharedData.videoEnabled) {
+ if (this.sharedData.remoteVideoBlocker.isVideoEnabled()) {
return t('spreed', 'Disable video')
}
@@ -220,7 +220,7 @@ export default {
},
showNameIndicator() {
- return !this.model.attributes.videoAvailable || !this.sharedData.videoEnabled || this.showVideoOverlay || this.isSelected || this.isPromoted || this.isSpeaking
+ return !this.model.attributes.videoAvailable || !this.sharedData.remoteVideoBlocker.isVideoEnabled() || this.showVideoOverlay || this.isSelected || this.isPromoted || this.isSpeaking
},
boldenNameIndicator() {
@@ -255,10 +255,7 @@ export default {
},
toggleVideo() {
- emit('talk:video:toggled', {
- peerId: this.model.attributes.peerId,
- value: !this.sharedData.videoEnabled,
- })
+ this.sharedData.remoteVideoBlocker.setVideoEnabled(!this.sharedData.remoteVideoBlocker.isVideoEnabled())
},
switchToScreen() {
diff --git a/src/components/ConversationSettings/Matterbridge/BridgePart.vue b/src/components/ConversationSettings/Matterbridge/BridgePart.vue
index 295d7fec6..19dde6197 100644
--- a/src/components/ConversationSettings/Matterbridge/BridgePart.vue
+++ b/src/components/ConversationSettings/Matterbridge/BridgePart.vue
@@ -204,14 +204,10 @@ h3 {
margin: 14px 10px 0 14px;
width: 16px;
height: 16px;
+ filter: var(--background-invert-if-dark);
}
}
-body.theme--dark .icon-service {
- -webkit-filter: invert(1);
- filter: invert(1);
-}
-
input {
background-size: 16px;
background-position: 14px;
diff --git a/src/components/ConversationSettings/Matterbridge/MatterbridgeSettings.vue b/src/components/ConversationSettings/Matterbridge/MatterbridgeSettings.vue
index 76e5e89cb..68ea5e038 100644
--- a/src/components/ConversationSettings/Matterbridge/MatterbridgeSettings.vue
+++ b/src/components/ConversationSettings/Matterbridge/MatterbridgeSettings.vue
@@ -630,11 +630,7 @@ export default {
width: 16px !important;
height: 16px !important;
margin-right: 10px;
-}
-
-body.theme--dark .icon-multiselect-service {
- -webkit-filter: invert(1);
- filter: invert(1);
+ filter: var(--background-invert-if-dark);
}
.matterbridge-settings {
diff --git a/src/components/MessagesList/MessagesGroup/Message/Message.spec.js b/src/components/MessagesList/MessagesGroup/Message/Message.spec.js
index 69cd66042..d9ec96590 100644
--- a/src/components/MessagesList/MessagesGroup/Message/Message.spec.js
+++ b/src/components/MessagesList/MessagesGroup/Message/Message.spec.js
@@ -3,7 +3,7 @@ import { createLocalVue, mount, shallowMount } from '@vue/test-utils'
import { cloneDeep } from 'lodash'
import { EventBus } from '../../../../services/EventBus'
import storeConfig from '../../../../store/storeConfig'
-import { CONVERSATION, ATTENDEE } from '../../../../constants'
+import { CONVERSATION, ATTENDEE, PARTICIPANT } from '../../../../constants'
// Components
import Check from 'vue-material-design-icons/Check'
@@ -50,6 +50,7 @@ describe('Message.vue', () => {
lastCommonReadMessage: 0,
type: CONVERSATION.TYPE.GROUP,
readOnly: CONVERSATION.STATE.READ_WRITE,
+ permissions: PARTICIPANT.PERMISSIONS.MAX_DEFAULT,
}
testStoreConfig = cloneDeep(storeConfig)
@@ -806,6 +807,38 @@ describe('Message.vue', () => {
expect(reactionButtons.wrappers[1].text()).toBe('👍 7')
})
+ test('shows reaction buttons with the right emoji count but without emoji placeholder when no chat permission', () => {
+ const conversationProps = {
+ token: TOKEN,
+ lastCommonReadMessage: 0,
+ type: CONVERSATION.TYPE.GROUP,
+ readOnly: CONVERSATION.STATE.READ_WRITE,
+ permissions: PARTICIPANT.PERMISSIONS.MAX_DEFAULT - PARTICIPANT.PERMISSIONS.CHAT,
+ }
+ testStoreConfig.modules.conversationsStore.getters.conversation
+ = jest.fn().mockReturnValue((token) => conversationProps)
+ store = new Store(testStoreConfig)
+
+ const wrapper = shallowMount(Message, {
+ localVue,
+ store,
+ propsData: messageProps,
+ })
+
+ const reactionsBar = wrapper.find('.message-body__reactions')
+
+ // Array of buttons
+ const reactionButtons = reactionsBar.findAll('.reaction-button')
+
+ // Number of buttons, 2 passed into the getter and 1 is the emoji
+ // picker
+ expect(reactionButtons.length).toBe(2)
+
+ // Text of the buttons
+ expect(reactionButtons.wrappers[0].text()).toBe('❤️ 1')
+ expect(reactionButtons.wrappers[1].text()).toBe('👍 7')
+ })
+
test('dispatches store action upon picking an emoji from the emojipicker', () => {
const addReactionToMessageAction = jest.fn()
const userHasReactedGetter = jest.fn().mockReturnValue(() => false)
diff --git a/src/components/MessagesList/MessagesGroup/Message/Message.vue b/src/components/MessagesList/MessagesGroup/Message/Message.vue
index 612b8de11..4769068ec 100644
--- a/src/components/MessagesList/MessagesGroup/Message/Message.vue
+++ b/src/components/MessagesList/MessagesGroup/Message/Message.vue
@@ -136,7 +136,10 @@ the main body of the message as well as a quote.
</Popover>
<!-- More reactions picker -->
- <EmojiPicker :per-line="5" :container="`#message_${id}`" @select="handleReactionClick">
+ <EmojiPicker v-if="canReact"
+ :per-line="5"
+ :container="`#message_${id}`"
+ @select="handleReactionClick">
<button class="reaction-button">
<EmoticonOutline :size="15" />
</button>
@@ -190,6 +193,7 @@ import EmojiPicker from '@nextcloud/vue/dist/Components/EmojiPicker'
import EmoticonOutline from 'vue-material-design-icons/EmoticonOutline.vue'
import Popover from '@nextcloud/vue/dist/Components/Popover'
import { showError, showSuccess, showWarning, TOAST_DEFAULT_TIMEOUT } from '@nextcloud/dialogs'
+import { ATTENDEE, CONVERSATION, PARTICIPANT } from '../../../../constants'
export default {
name: 'Message',
@@ -571,6 +575,10 @@ export default {
return this.$store.getters.hasReactions(this.token, this.id)
},
+ canReact() {
+ return this.conversation.readOnly !== CONVERSATION.STATE.READ_ONLY && (this.conversation.permissions & PARTICIPANT.PERMISSIONS.CHAT) !== 0
+ },
+
simpleReactions() {
return this.messageObject.reactions
},
@@ -656,7 +664,8 @@ export default {
async getReactions() {
if (this.detailedReactionsLoading) {
- // FIXME not sure how to await the other execution
+ // A parallel request is already doing this
+ return
}
try {
@@ -677,6 +686,11 @@ export default {
},
async handleReactionClick(clickedEmoji) {
+ if (!this.canReact) {
+ showError(t('spreed', 'No permission to post reactions in this conversation'))
+ return
+ }
+
// Check if current user has already added this reaction to the message
if (!this.userHasReacted(clickedEmoji)) {
this.$store.dispatch('addReactionToMessage', {
@@ -738,12 +752,26 @@ export default {
&& list[item].actorId === this.$store.getters.getActorId()) {
summary.unshift(t('spreed', 'You'))
} else {
- summary.push(list[item].actorDisplayName)
+ summary.push(this.getDisplayNameForReaction(list[item]))
}
}
return summary.join(', ')
},
+
+ getDisplayNameForReaction(reaction) {
+ const displayName = reaction.actorDisplayName.trim()
+
+ if (reaction.actorType === ATTENDEE.ACTOR_TYPE.GUESTS) {
+ return this.$store.getters.getGuestNameWithGuestSuffix(this.token, reaction.actorId)
+ }
+
+ if (displayName === '') {
+ return t('spreed', 'Deleted user')
+ }
+
+ return displayName
+ },
},
}
</script>
diff --git a/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.vue b/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.vue
index fb0c58d48..db707c06f 100644
--- a/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.vue
+++ b/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.vue
@@ -326,7 +326,7 @@ export default {
},
acceptsReactions() {
- return !this.isConversationReadOnly && !this.isDeletedMessage
+ return !this.isConversationReadOnly && !this.isDeletedMessage && (this.conversation.permissions & PARTICIPANT.PERMISSIONS.CHAT) !== 0
},
messageActions() {
@@ -475,7 +475,7 @@ export default {
.message-buttons-bar {
display: flex;
right: 14px;
- bottom: -4px;
+ top: -16px;
position: absolute;
background-color: var(--color-main-background);
border-radius: calc($clickable-area / 2);
diff --git a/src/components/MessagesList/MessagesGroup/Message/MessagePart/Contact.vue b/src/components/MessagesList/MessagesGroup/Message/MessagePart/Contact.vue
index a894954db..a3d5a17ff 100644
--- a/src/components/MessagesList/MessagesGroup/Message/MessagePart/Contact.vue
+++ b/src/components/MessagesList/MessagesGroup/Message/MessagePart/Contact.vue
@@ -100,6 +100,7 @@ export default {
.contact {
display: flex;
transition: box-shadow 0.1s ease-in-out;
+ border: 1px solid var(--color-border);
box-shadow: 0 0 2px 0 var(--color-box-shadow);
border-radius: var(--border-radius-large);
font-size: 100%;
@@ -137,11 +138,6 @@ export default {
margin-left: 12px;
}
}
-
- // Dark theme
- body.dark &, body.theme--dark & {
- border: 2px solid var(--color-border);
- }
}
.icon-contacts {
diff --git a/src/components/MessagesList/MessagesGroup/Message/MessagePart/DeckCard.vue b/src/components/MessagesList/MessagesGroup/Message/MessagePart/DeckCard.vue
index 658de473f..2531f594e 100644
--- a/src/components/MessagesList/MessagesGroup/Message/MessagePart/DeckCard.vue
+++ b/src/components/MessagesList/MessagesGroup/Message/MessagePart/DeckCard.vue
@@ -99,6 +99,7 @@ export default {
.deck-card {
display: flex;
transition: box-shadow 0.1s ease-in-out;
+ border: 1px solid var(--color-border);
box-shadow: 0 0 2px 0 var(--color-box-shadow);
border-radius: var(--border-radius-large);
font-size: 100%;
@@ -132,10 +133,6 @@ export default {
white-space: nowrap;
text-overflow: ellipsis;
}
- // Dark theme
- body.dark &, body.theme--dark & {
- border: 2px solid var(--color-border);
- }
}
.icon-deck {
diff --git a/src/components/NewMessageForm/NewMessageForm.vue b/src/components/NewMessageForm/NewMessageForm.vue
index 0942e316e..cbf96fc9a 100644
--- a/src/components/NewMessageForm/NewMessageForm.vue
+++ b/src/components/NewMessageForm/NewMessageForm.vue
@@ -132,7 +132,7 @@ import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
import EmojiPicker from '@nextcloud/vue/dist/Components/EmojiPicker'
import { EventBus } from '../../services/EventBus'
import { shareFile } from '../../services/filesSharingServices'
-import { CONVERSATION } from '../../constants'
+import { CONVERSATION, PARTICIPANT } from '../../constants'
import Paperclip from 'vue-material-design-icons/Paperclip'
import EmoticonOutline from 'vue-material-design-icons/EmoticonOutline'
import Send from 'vue-material-design-icons/Send'
@@ -197,13 +197,19 @@ export default {
return this.conversation.readOnly === CONVERSATION.STATE.READ_ONLY
},
+ noChatPermission() {
+ return (this.conversation.permissions & PARTICIPANT.PERMISSIONS.CHAT) === 0
+ },
+
disabled() {
- return this.isReadOnly || !this.currentConversationIsJoined || this.isRecordingAudio
+ return this.isReadOnly || this.noChatPermission || this.isReadOnly || !this.currentConversationIsJoined || this.isRecordingAudio
},
placeholderText() {
if (this.isReadOnly) {
return t('spreed', 'This conversation has been locked')
+ } else if (this.noChatPermission) {
+ return t('spreed', 'No permission to post messages in this conversation')
} else if (!this.currentConversationIsJoined) {
return t('spreed', 'Joining conversation …')
} else {
diff --git a/src/components/PermissionsEditor/PermissionsEditor.vue b/src/components/PermissionsEditor/PermissionsEditor.vue
index c3fe2e9be..137d36909 100644
--- a/src/components/PermissionsEditor/PermissionsEditor.vue
+++ b/src/components/PermissionsEditor/PermissionsEditor.vue
@@ -37,6 +37,11 @@
class="checkbox">
{{ t('spreed', 'Skip the lobby') }}
</CheckboxRadioSwitch>
+ <CheckboxRadioSwitch ref="chatMessagesAndReactions"
+ :checked.sync="chatMessagesAndReactions"
+ class="checkbox">
+ {{ t('spreed', 'Can post messages and reactions') }}
+ </CheckboxRadioSwitch>
<CheckboxRadioSwitch ref="publishAudio"
:checked.sync="publishAudio"
class="checkbox">
@@ -125,6 +130,8 @@ export default {
callStart: false,
// Permission to bypass the lobby
lobbyIgnore: false,
+ // Permission to post messages and reactions
+ chatMessagesAndReactions: false,
// Permission to enable the microphone
publishAudio: false,
// Permission to enable the camera
@@ -164,6 +171,7 @@ export default {
formPermissions() {
return (this.callStart ? PERMISSIONS.CALL_START : 0)
| (this.lobbyIgnore ? PERMISSIONS.LOBBY_IGNORE : 0)
+ | (this.chatMessagesAndReactions ? PERMISSIONS.CHAT : 0)
| (this.publishAudio ? PERMISSIONS.PUBLISH_AUDIO : 0)
| (this.publishVideo ? PERMISSIONS.PUBLISH_VIDEO : 0)
| (this.publishScreen ? PERMISSIONS.PUBLISH_SCREEN : 0)
@@ -177,6 +185,7 @@ export default {
submitButtonDisabled() {
return (!!(this.permissionsWithDefault & PERMISSIONS.CALL_START)) === this.callStart
&& !!(this.permissionsWithDefault & PERMISSIONS.LOBBY_IGNORE) === this.lobbyIgnore
+ && !!(this.permissionsWithDefault & PERMISSIONS.CHAT) === this.chatMessagesAndReactions
&& !!(this.permissionsWithDefault & PERMISSIONS.PUBLISH_AUDIO) === this.publishAudio
&& !!(this.permissionsWithDefault & PERMISSIONS.PUBLISH_VIDEO) === this.publishVideo
&& !!(this.permissionsWithDefault & PERMISSIONS.PUBLISH_SCREEN) === this.publishScreen
@@ -197,6 +206,7 @@ export default {
writePermissionsToComponent(permissions) {
permissions & PERMISSIONS.CALL_START ? this.callStart = true : this.callStart = false
permissions & PERMISSIONS.LOBBY_IGNORE ? this.lobbyIgnore = true : this.lobbyIgnore = false
+ permissions & PERMISSIONS.CHAT ? this.chatMessagesAndReactions = true : this.chatMessagesAndReactions = false
permissions & PERMISSIONS.PUBLISH_AUDIO ? this.publishAudio = true : this.publishAudio = false
permissions & PERMISSIONS.PUBLISH_VIDEO ? this.publishVideo = true : this.publishVideo = false
permissions & PERMISSIONS.PUBLISH_SCREEN ? this.publishScreen = true : this.publishScreen = false
diff --git a/src/components/RightSidebar/SharedItems/SharedItemsBrowser/SharedItemsBrowser.vue b/src/components/RightSidebar/SharedItems/SharedItemsBrowser/SharedItemsBrowser.vue
index 1783ed2bc..bdfa465f3 100644
--- a/src/components/RightSidebar/SharedItems/SharedItemsBrowser/SharedItemsBrowser.vue
+++ b/src/components/RightSidebar/SharedItems/SharedItemsBrowser/SharedItemsBrowser.vue
@@ -20,7 +20,7 @@
-->
<template>
- <Modal size="large" v-on="$listeners">
+ <Modal size="large" :container="container" v-on="$listeners">
<div class="shared-items-browser">
<div class="shared-items-browser__navigation">
<template v-for="type in sharedItemsOrder">
@@ -87,6 +87,10 @@ export default {
token() {
return this.$store.getters.getToken()
},
+
+ container() {
+ return this.$store.getters.getMainContainerSelector()
+ },
},
watch: {
diff --git a/src/components/SettingsDialog/SettingsDialog.vue b/src/components/SettingsDialog/SettingsDialog.vue
index 6dc398a98..e5f695f36 100644
--- a/src/components/SettingsDialog/SettingsDialog.vue
+++ b/src/components/SettingsDialog/SettingsDialog.vue
@@ -62,6 +62,13 @@
@change="togglePlaySounds">
<label for="play_sounds">{{ t('spreed', 'Play sounds when participants join or leave a call') }}</label>
<em>{{ t('spreed', 'Sounds can currently not be played in Safari browser and iPad and iPhone devices due to technical restrictions by the manufacturer.') }}</em>
+
+ <a :href="settingsUrl"
+ target="_blank"
+ rel="noreferrer nofollow"
+ class="external">
+ {{ t('spreed', 'Sounds for chat and call notifications can be adjusted in the personal settings.') }} ↗
+ </a>
</AppSettingsSection>
<AppSettingsSection :title="t('spreed', 'Keyboard shortcuts')">
<em>{{ t('spreed', 'Speed up your Talk experience with these quick shortcuts.') }}</em>
@@ -125,6 +132,7 @@
</template>
<script>
+import { generateUrl } from '@nextcloud/router'
import { getFilePickerBuilder, showError, showSuccess } from '@nextcloud/dialogs'
import { PRIVACY } from '../../constants'
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
@@ -180,6 +188,10 @@ export default {
readStatusPrivacy() {
return this.$store.getters.getReadStatusPrivacy()
},
+
+ settingsUrl() {
+ return generateUrl('/settings/user/notifications')
+ },
},
mounted() {
diff --git a/src/constants.js b/src/constants.js
index f9d72765f..c54a6a5a4 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -100,8 +100,9 @@ export const PARTICIPANT = {
PUBLISH_AUDIO: 16,
PUBLISH_VIDEO: 32,
PUBLISH_SCREEN: 64,
- MAX_DEFAULT: 126,
- MAX_CUSTOM: 127,
+ CHAT: 128,
+ MAX_DEFAULT: 254,
+ MAX_CUSTOM: 255,
},
}
export const SHARED_ITEM = {
diff --git a/src/store/guestNameStore.js b/src/store/guestNameStore.js
index 94f39acc5..48da79d9d 100644
--- a/src/store/guestNameStore.js
+++ b/src/store/guestNameStore.js
@@ -39,6 +39,14 @@ const getters = {
}
return t('spreed', 'Guest')
},
+
+ getGuestNameWithGuestSuffix: (state, getters) => (token, actorId) => {
+ const displayName = getters.getGuestName(token, actorId)
+ if (displayName === t('spreed', 'Guest')) {
+ return displayName
+ }
+ return t('spreed', '{guest} (guest)', { guest: displayName })
+ },
}
const mutations = {
diff --git a/src/utils/signaling.js b/src/utils/signaling.js
index 6b6fbab32..c0d1fbb52 100644
--- a/src/utils/signaling.js
+++ b/src/utils/signaling.js
@@ -1292,7 +1292,7 @@ Signaling.Standalone.prototype.processRoomParticipantsEvent = function(data) {
}
}
-Signaling.Standalone.prototype.requestOffer = function(sessionid, roomType) {
+Signaling.Standalone.prototype.requestOffer = function(sessionid, roomType, sid = undefined) {
if (!this.hasFeature('mcu')) {
console.warn("Can't request an offer without a MCU.")
return
@@ -1302,7 +1302,7 @@ Signaling.Standalone.prototype.requestOffer = function(sessionid, roomType) {
// Got a user object.
sessionid = sessionid.sessionId || sessionid.sessionid
}
- console.debug('Request offer from', sessionid)
+ console.debug('Request offer from', sessionid, sid)
this.doSend({
type: 'message',
message: {
@@ -1313,6 +1313,7 @@ Signaling.Standalone.prototype.requestOffer = function(sessionid, roomType) {
data: {
type: 'requestoffer',
roomType,
+ sid,
},
},
})
diff --git a/src/utils/webrtc/RemoteVideoBlocker.js b/src/utils/webrtc/RemoteVideoBlocker.js
new file mode 100644
index 000000000..2a8a65cbd
--- /dev/null
+++ b/src/utils/webrtc/RemoteVideoBlocker.js
@@ -0,0 +1,131 @@
+/**
+ *
+ * @copyright Copyright (c) 2022, Daniel Calviño Sánchez (danxuliu@gmail.com)
+ *
+ * @license AGPL-3.0-or-later
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/**
+ * Helper to block the remote video when not needed.
+ *
+ * A remote video is not needed if the local user explicitly disabled it
+ * (independently of whether the remote user (and thus owner of the remote
+ * video) has it enabled or not) or if it is not visible.
+ *
+ * The remote video is not immediately hidden when no longer visible; a few
+ * seconds are waited to avoid blocking and unblocking on layout changes.
+ *
+ * "increaseVisibleCounter()" can be called several times by the same view, but
+ * "decreaseVisibleCounter()" must have been called a corresponding number of
+ * times once the view is destroyed.
+ *
+ * A single RemoteVideoBlocker is assumed to be associated with its
+ * CallParticipantModel, and it is also assumed to be the only element blocking
+ * and unblocking the video. Otherwise the result is undefined.
+ *
+ * Note that the RemoteVideoBlocker can be used on participants that do not have
+ * a video at all (for example, because they do not have a camera or they do not
+ * have video permissions). In that case the CallParticipantModel will block the
+ * video if needed if it becomes available.
+ *
+ * @param {object} callParticipantModel the model to block/unblock the video on.
+ */
+export default function RemoteVideoBlocker(callParticipantModel) {
+ this._model = callParticipantModel
+
+ // Keep track of the blocked state here, as the Peer object may not block
+ // the video if some features are missing, and even if the video is blocked
+ // the attribute will not be updated right away but once the renegotiation
+ // is done.
+ this._blocked = false
+
+ this._enabled = true
+ this._visibleCounter = 1
+
+ this._blockVideoTimeout = null
+
+ // Block by default if not shown after creation.
+ this.decreaseVisibleCounter()
+}
+
+RemoteVideoBlocker.prototype = {
+
+ isVideoEnabled() {
+ return this._enabled
+ },
+
+ setVideoEnabled(enabled) {
+ this._enabled = enabled
+
+ const hadBlockVideoTimeout = this._blockVideoTimeout
+
+ clearTimeout(this._blockVideoTimeout)
+ this._blockVideoTimeout = null
+
+ if (!this._visibleCounter && !hadBlockVideoTimeout) {
+ return
+ }
+
+ this._setVideoBlocked(!enabled)
+ },
+
+ increaseVisibleCounter() {
+ this._visibleCounter++
+
+ clearTimeout(this._blockVideoTimeout)
+ this._blockVideoTimeout = null
+
+ if (!this._enabled) {
+ return
+ }
+
+ this._setVideoBlocked(false)
+ },
+
+ decreaseVisibleCounter() {
+ if (this._visibleCounter <= 0) {
+ console.error('Visible counter decreased when not visible')
+
+ return
+ }
+
+ this._visibleCounter--
+
+ if (this._visibleCounter > 0 || !this._enabled) {
+ return
+ }
+
+ clearTimeout(this._blockVideoTimeout)
+
+ this._blockVideoTimeout = setTimeout(() => {
+ this._setVideoBlocked(true)
+
+ this._blockVideoTimeout = null
+ }, 5000)
+ },
+
+ _setVideoBlocked(blocked) {
+ if (this._blocked === blocked) {
+ return
+ }
+
+ this._blocked = blocked
+
+ this._model.setVideoBlocked(blocked)
+ },
+
+}
diff --git a/src/utils/webrtc/RemoteVideoBlocker.spec.js b/src/utils/webrtc/RemoteVideoBlocker.spec.js
new file mode 100644
index 000000000..700b2918b
--- /dev/null
+++ b/src/utils/webrtc/RemoteVideoBlocker.spec.js
@@ -0,0 +1,336 @@
+/**
+ *
+ * @copyright Copyright (c) 2022, Daniel Calviño Sánchez (danxuliu@gmail.com)
+ *
+ * @license AGPL-3.0-or-later
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+import RemoteVideoBlocker from './RemoteVideoBlocker'
+
+describe('RemoteVideoBlocker', () => {
+ let callParticipantModel
+ let remoteVideoBlocker
+
+ beforeEach(() => {
+ jest.useFakeTimers()
+
+ callParticipantModel = {
+ setVideoBlocked: jest.fn()
+ }
+
+ remoteVideoBlocker = new RemoteVideoBlocker(callParticipantModel)
+ })
+
+ test('blocks the video by default if not shown in some seconds', () => {
+ jest.advanceTimersByTime(4000)
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(0)
+
+ jest.advanceTimersByTime(1000)
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(1)
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledWith(true)
+
+ expect(remoteVideoBlocker.isVideoEnabled()).toBe(true)
+ })
+
+ describe('set video enabled', () => {
+ test('immediately blocks the video', () => {
+ remoteVideoBlocker.increaseVisibleCounter()
+
+ remoteVideoBlocker.setVideoEnabled(false)
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(1)
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledWith(true)
+
+ expect(remoteVideoBlocker.isVideoEnabled()).toBe(false)
+ })
+ test('immediately unblocks the video', () => {
+ remoteVideoBlocker.increaseVisibleCounter()
+
+ remoteVideoBlocker.setVideoEnabled(false)
+ remoteVideoBlocker.setVideoEnabled(true)
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(2)
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenNthCalledWith(2, false)
+
+ expect(remoteVideoBlocker.isVideoEnabled()).toBe(true)
+ })
+ })
+
+ describe('set video visible', () => {
+ test('does nothing if shown', () => {
+ remoteVideoBlocker.increaseVisibleCounter()
+
+ jest.runAllTimers()
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(0)
+ expect(remoteVideoBlocker._visibleCounter).toBe(1)
+
+ expect(remoteVideoBlocker.isVideoEnabled()).toBe(true)
+ })
+
+ test('does nothing if hidden without showing first', () => {
+ jest.runAllTimers()
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(1)
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledWith(true)
+
+ remoteVideoBlocker.decreaseVisibleCounter()
+
+ jest.runAllTimers()
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(1)
+ expect(remoteVideoBlocker._visibleCounter).toBe(0)
+
+ expect(remoteVideoBlocker.isVideoEnabled()).toBe(true)
+ })
+
+ test('blocks the video after some seconds when hidden', () => {
+ remoteVideoBlocker.increaseVisibleCounter()
+ remoteVideoBlocker.decreaseVisibleCounter()
+
+ jest.advanceTimersByTime(4000)
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(0)
+
+ jest.advanceTimersByTime(1000)
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(1)
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledWith(true)
+ expect(remoteVideoBlocker._visibleCounter).toBe(0)
+
+ expect(remoteVideoBlocker.isVideoEnabled()).toBe(true)
+ })
+
+ test('does nothing if shown again before blocking', () => {
+ remoteVideoBlocker.increaseVisibleCounter()
+ remoteVideoBlocker.decreaseVisibleCounter()
+
+ jest.advanceTimersByTime(4000)
+
+ remoteVideoBlocker.increaseVisibleCounter()
+
+ jest.runAllTimers()
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(0)
+ expect(remoteVideoBlocker._visibleCounter).toBe(1)
+
+ expect(remoteVideoBlocker.isVideoEnabled()).toBe(true)
+ })
+
+ test('immediately unblocks the video after showing', () => {
+ remoteVideoBlocker.increaseVisibleCounter()
+ remoteVideoBlocker.decreaseVisibleCounter()
+
+ jest.runAllTimers()
+
+ remoteVideoBlocker.increaseVisibleCounter()
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(2)
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenNthCalledWith(2, false)
+ expect(remoteVideoBlocker._visibleCounter).toBe(1)
+
+ expect(remoteVideoBlocker.isVideoEnabled()).toBe(true)
+ })
+
+ test('does nothing if not fully hidden', () => {
+ remoteVideoBlocker.increaseVisibleCounter()
+ remoteVideoBlocker.increaseVisibleCounter()
+ remoteVideoBlocker.increaseVisibleCounter()
+ remoteVideoBlocker.decreaseVisibleCounter()
+ remoteVideoBlocker.decreaseVisibleCounter()
+ remoteVideoBlocker.increaseVisibleCounter()
+ remoteVideoBlocker.increaseVisibleCounter()
+ remoteVideoBlocker.decreaseVisibleCounter()
+
+ jest.runAllTimers()
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(0)
+ expect(remoteVideoBlocker._visibleCounter).toBe(2)
+
+ expect(remoteVideoBlocker.isVideoEnabled()).toBe(true)
+ })
+ })
+
+ describe('set video enabled and visible', () => {
+ test('immediately blocks the video if disabled when visible', () => {
+ remoteVideoBlocker.increaseVisibleCounter()
+ remoteVideoBlocker.increaseVisibleCounter()
+ remoteVideoBlocker.increaseVisibleCounter()
+
+ remoteVideoBlocker.setVideoEnabled(false)
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(1)
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledWith(true)
+
+ jest.runAllTimers()
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(1)
+
+ expect(remoteVideoBlocker.isVideoEnabled()).toBe(false)
+ })
+
+ test('immediately blocks the video if disabled before blocking after hidden', () => {
+ remoteVideoBlocker.increaseVisibleCounter()
+ remoteVideoBlocker.decreaseVisibleCounter()
+
+ jest.advanceTimersByTime(4000)
+
+ remoteVideoBlocker.setVideoEnabled(false)
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(1)
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledWith(true)
+
+ jest.runAllTimers()
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(1)
+
+ expect(remoteVideoBlocker.isVideoEnabled()).toBe(false)
+ })
+
+ test('blocks the video after some seconds if hidden when enabled', () => {
+ remoteVideoBlocker.setVideoEnabled(true)
+
+ remoteVideoBlocker.increaseVisibleCounter()
+ remoteVideoBlocker.decreaseVisibleCounter()
+
+ jest.advanceTimersByTime(4000)
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(0)
+
+ jest.advanceTimersByTime(1000)
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(1)
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledWith(true)
+
+ expect(remoteVideoBlocker.isVideoEnabled()).toBe(true)
+ })
+
+ test('does nothing if disabled when hidden', () => {
+ remoteVideoBlocker.increaseVisibleCounter()
+ remoteVideoBlocker.decreaseVisibleCounter()
+
+ jest.runAllTimers()
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(1)
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledWith(true)
+
+ remoteVideoBlocker.setVideoEnabled(false)
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(1)
+
+ expect(remoteVideoBlocker.isVideoEnabled()).toBe(false)
+ })
+
+ test('does nothing if enabled when hidden', () => {
+ remoteVideoBlocker.increaseVisibleCounter()
+ remoteVideoBlocker.decreaseVisibleCounter()
+
+ jest.runAllTimers()
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(1)
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledWith(true)
+
+ remoteVideoBlocker.setVideoEnabled(false)
+ remoteVideoBlocker.setVideoEnabled(true)
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(1)
+
+ expect(remoteVideoBlocker.isVideoEnabled()).toBe(true)
+ })
+
+ test('does nothing if hidden when disabled', () => {
+ remoteVideoBlocker.setVideoEnabled(false)
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(1)
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledWith(true)
+
+ remoteVideoBlocker.increaseVisibleCounter()
+ remoteVideoBlocker.decreaseVisibleCounter()
+
+ jest.runAllTimers()
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(1)
+
+ expect(remoteVideoBlocker.isVideoEnabled()).toBe(false)
+ })
+
+ test('does nothing if shown when disabled', () => {
+ remoteVideoBlocker.setVideoEnabled(false)
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(1)
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledWith(true)
+
+ remoteVideoBlocker.increaseVisibleCounter()
+ remoteVideoBlocker.decreaseVisibleCounter()
+ remoteVideoBlocker.increaseVisibleCounter()
+
+ jest.runAllTimers()
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(1)
+
+ expect(remoteVideoBlocker.isVideoEnabled()).toBe(false)
+ })
+
+ test('immediately unblocks the video if enabled after showing', () => {
+ remoteVideoBlocker.setVideoEnabled(false)
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(1)
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledWith(true)
+
+ remoteVideoBlocker.increaseVisibleCounter()
+ remoteVideoBlocker.decreaseVisibleCounter()
+ remoteVideoBlocker.increaseVisibleCounter()
+ remoteVideoBlocker.increaseVisibleCounter()
+ remoteVideoBlocker.increaseVisibleCounter()
+
+ jest.runAllTimers()
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(1)
+
+ remoteVideoBlocker.setVideoEnabled(true)
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(2)
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenNthCalledWith(2, false)
+
+ expect(remoteVideoBlocker.isVideoEnabled()).toBe(true)
+ })
+
+ test('immediately unblocks the video if shown after enabled', () => {
+ remoteVideoBlocker.increaseVisibleCounter()
+ remoteVideoBlocker.decreaseVisibleCounter()
+
+ jest.runAllTimers()
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(1)
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledWith(true)
+
+ remoteVideoBlocker.setVideoEnabled(false)
+ remoteVideoBlocker.setVideoEnabled(true)
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(1)
+
+ remoteVideoBlocker.increaseVisibleCounter()
+
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenCalledTimes(2)
+ expect(callParticipantModel.setVideoBlocked).toHaveBeenNthCalledWith(2, false)
+
+ expect(remoteVideoBlocker.isVideoEnabled()).toBe(true)
+ })
+ })
+})
diff --git a/src/utils/webrtc/models/CallParticipantModel.js b/src/utils/webrtc/models/CallParticipantModel.js
index 354067454..6362e14e3 100644
--- a/src/utils/webrtc/models/CallParticipantModel.js
+++ b/src/utils/webrtc/models/CallParticipantModel.js
@@ -65,6 +65,9 @@ export default function CallParticipantModel(options) {
audioElement: null,
audioAvailable: undefined,
speaking: undefined,
+ // "videoBlocked" is "true" only if the video is blocked and it would
+ // have been available in the remote peer if not blocked.
+ videoBlocked: undefined,
videoAvailable: undefined,
screen: null,
// The audio element is part of the model to ensure that it can be
@@ -89,6 +92,7 @@ export default function CallParticipantModel(options) {
this._handleSignalingStateChangeBound = this._handleSignalingStateChange.bind(this)
this._handleChannelMessageBound = this._handleChannelMessage.bind(this)
this._handleRaisedHandBound = this._handleRaisedHand.bind(this)
+ this._handleRemoteVideoBlockedBound = this._handleRemoteVideoBlocked.bind(this)
this._webRtc.on('peerStreamAdded', this._handlePeerStreamAddedBound)
this._webRtc.on('peerStreamRemoved', this._handlePeerStreamRemovedBound)
@@ -105,6 +109,7 @@ CallParticipantModel.prototype = {
if (this.get('peer')) {
this.get('peer').off('extendedIceConnectionStateChange', this._handleExtendedIceConnectionStateChangeBound)
this.get('peer').off('signalingStateChange', this._handleSignalingStateChangeBound)
+ this.get('peer').off('remoteVideoBlocked', this._handleRemoteVideoBlockedBound)
}
this._webRtc.off('peerStreamAdded', this._handlePeerStreamAddedBound)
@@ -249,6 +254,7 @@ CallParticipantModel.prototype = {
if (this.get('peer')) {
this.get('peer').off('extendedIceConnectionStateChange', this._handleExtendedIceConnectionStateChangeBound)
this.get('peer').off('signalingStateChange', this._handleSignalingStateChangeBound)
+ this.get('peer').off('remoteVideoBlocked', this._handleRemoteVideoBlockedBound)
}
this.set('peer', peer)
@@ -261,6 +267,7 @@ CallParticipantModel.prototype = {
this.set('audioAvailable', false)
this.set('speaking', false)
this.set('videoAvailable', false)
+ this.set('videoBlocked', false)
return
}
@@ -275,9 +282,19 @@ CallParticipantModel.prototype = {
}
this._handleSignalingStateChange(this.get('peer').pc.signalingState)
this._handlePeerStreamAdded(this.get('peer'))
+ this._handleRemoteVideoBlocked(undefined)
this.get('peer').on('extendedIceConnectionStateChange', this._handleExtendedIceConnectionStateChangeBound)
this.get('peer').on('signalingStateChange', this._handleSignalingStateChangeBound)
+ this.get('peer').on('remoteVideoBlocked', this._handleRemoteVideoBlockedBound)
+
+ // Set expected state in Peer object.
+ if (this._simulcastVideoQuality !== undefined) {
+ this.setSimulcastVideoQuality(this._simulcastVideoQuality)
+ }
+ if (this._videoBlocked !== undefined) {
+ this.setVideoBlocked(this._videoBlocked)
+ }
},
_handleExtendedIceConnectionStateChange(extendedIceConnectionState) {
@@ -363,6 +380,11 @@ CallParticipantModel.prototype = {
// Reset state that depends on the screen Peer object.
this._handlePeerStreamAdded(this.get('screenPeer'))
+
+ // Set expected state in screen Peer object.
+ if (this._simulcastScreenQuality !== undefined) {
+ this.setSimulcastScreenQuality(this._simulcastScreenQuality)
+ }
},
setUserId(userId) {
@@ -373,7 +395,25 @@ CallParticipantModel.prototype = {
this.set('nextcloudSessionId', nextcloudSessionId)
},
+ setVideoBlocked(videoBlocked) {
+ // Store value to be able to apply it again if a new Peer object is set.
+ this._videoBlocked = videoBlocked
+
+ if (!this.get('peer')) {
+ return
+ }
+
+ this.get('peer').setRemoteVideoBlocked(videoBlocked)
+ },
+
+ _handleRemoteVideoBlocked(remoteVideoBlocked) {
+ this.set('videoBlocked', remoteVideoBlocked)
+ },
+
setSimulcastVideoQuality(simulcastVideoQuality) {
+ // Store value to be able to apply it again if a new Peer object is set.
+ this._simulcastVideoQuality = simulcastVideoQuality
+
if (!this.get('peer') || !this.get('peer').enableSimulcast) {
return
}
@@ -383,6 +423,10 @@ CallParticipantModel.prototype = {
},
setSimulcastScreenQuality(simulcastScreenQuality) {
+ // Store value to be able to apply it again if a new screen Peer object
+ // is set.
+ this._simulcastScreenQuality = simulcastScreenQuality
+
if (!this.get('screenPeer') || !this.get('screenPeer').enableSimulcast) {
return
}
diff --git a/src/utils/webrtc/simplewebrtc/peer.js b/src/utils/webrtc/simplewebrtc/peer.js
index 6d64b20a4..6b0a987a8 100644
--- a/src/utils/webrtc/simplewebrtc/peer.js
+++ b/src/utils/webrtc/simplewebrtc/peer.js
@@ -31,6 +31,7 @@ function Peer(options) {
this.oneway = options.oneway || false
this.sharemyscreen = options.sharemyscreen || false
this.stream = options.stream
+ this.receiverOnly = options.receiverOnly
this.sendVideoIfAvailable = options.sendVideoIfAvailable === undefined ? true : options.sendVideoIfAvailable
this.enableDataChannels = options.enableDataChannels === undefined ? this.parent.config.enableDataChannels : options.enableDataChannels
this.enableSimulcast = options.enableSimulcast === undefined ? this.parent.config.enableSimulcast : options.enableSimulcast
@@ -418,12 +419,49 @@ Peer.prototype.offer = function(options) {
Peer.prototype.handleOffer = function(offer) {
this.pc.setRemoteDescription(offer).then(function() {
+ this._blockRemoteVideoIfNeeded()
+
this.answer()
}.bind(this)).catch(function(error) {
console.warn('setRemoteDescription for offer failed: ', error)
})
}
+/**
+ * Blocks remote video based on "_remoteVideoShouldBeBlocked".
+ *
+ * 'remoteVideoBlocked' is emitted if the blocked state changes.
+ *
+ * Currently remote video can be blocked only when the HPB is used, so this
+ * method should be called immediately before creating the answer (the answer
+ * must be created in the same "tick" that this method is called).
+ *
+ * Note that if the transceiver direction changes after creating the answer but
+ * before setting it as the local description the "negotiationneeded" event will
+ * be automatically emitted again.
+ */
+Peer.prototype._blockRemoteVideoIfNeeded = function() {
+ const remoteVideoWasBlocked = this._remoteVideoBlocked
+
+ this._remoteVideoBlocked = undefined
+
+ this.pc.getTransceivers().forEach(transceiver => {
+ if (transceiver.mid === 'video' && !transceiver.stopped) {
+ if (this._remoteVideoShouldBeBlocked) {
+ transceiver.direction = 'inactive'
+
+ this._remoteVideoBlocked = true
+ } else {
+ this._remoteVideoBlocked = false
+ }
+ }
+ })
+
+ if (remoteVideoWasBlocked !== this._remoteVideoBlocked) {
+ this.emit('remoteVideoBlocked', this._remoteVideoBlocked)
+ }
+}
+
Peer.prototype.answer = function() {
this.pc.createAnswer().then(function(answer) {
this.pc.setLocalDescription(answer).then(function() {
@@ -816,6 +854,36 @@ Peer.prototype.handleLocalTrackEnabledChanged = function(track, stream) {
}
}
+Peer.prototype.setRemoteVideoBlocked = function(remoteVideoBlocked) {
+ // If the HPB is not used or if it is used and this is a sender peer the
+ // remote video can not be blocked.
+ // Besides that the remote video is not blocked either if the signaling
+ // server does not support updating the subscribers; in that case a new
+ // connection would need to be established and due to this the audio would
+ // be interrupted during the connection change.
+ if (!this.receiverOnly || !this.parent.config.connection.hasFeature('update-sdp')) {
+ return
+ }
+
+ this._remoteVideoShouldBeBlocked = remoteVideoBlocked
+
+ // The "negotiationneeded" event is emitted if needed based on the direction
+ // changes.
+ // Note that there will be a video transceiver even if the remote
+ // participant is sending a null video track (either because there is a
+ // camera but the video is disabled or because the camera was removed during
+ // the call), so a renegotiation could be needed also in that case.
+ this.pc.getTransceivers().forEach(transceiver => {
+ if (transceiver.mid === 'video' && !transceiver.stopped) {
+ if (remoteVideoBlocked) {
+ transceiver.direction = 'inactive'
+ } else {
+ transceiver.direction = 'recvonly'
+ }
+ }
+ })
+}
+
Peer.prototype.handleRemoteStreamAdded = function(event) {
const self = this
if (this.stream) {
diff --git a/src/utils/webrtc/webrtc.js b/src/utils/webrtc/webrtc.js
index ec2c54d77..c5b97f7ab 100644
--- a/src/utils/webrtc/webrtc.js
+++ b/src/utils/webrtc/webrtc.js
@@ -367,6 +367,10 @@ function usersChanged(signaling, newUsers, disconnectedSessionIds) {
// TODO(jojo): Already create peer object to avoid duplicate offers.
signaling.requestOffer(user, 'video')
+ // Clearing the previous delayedConnectionToPeer should not be
+ // needed here, but just in case.
+ clearInterval(delayedConnectionToPeer[user.sessionId])
+
delayedConnectionToPeer[user.sessionId] = setInterval(function() {
console.debug('No offer received for new peer, request offer again')
@@ -781,6 +785,8 @@ export default function initWebRtc(signaling, _callParticipantCollection, _local
signaling.requestOffer(peer.id, 'video')
+ clearInterval(delayedConnectionToPeer[peer.id])
+
delayedConnectionToPeer[peer.id] = setInterval(function() {
console.debug('No offer received, request offer again', peer)
@@ -926,6 +932,41 @@ export default function initWebRtc(signaling, _callParticipantCollection, _local
*/
function setHandlerForNegotiationNeeded(peer) {
peer.pc.addEventListener('negotiationneeded', function() {
+ // When the HPB is used and the negotiation is needed for a receiver
+ // peer (for example, to block the received video) there is no need
+ // to force a full reconnection, it is enough to reconnect only that
+ // peer.
+ if (signaling.hasFeature('mcu') && peer.id !== signaling.getSessionId()) {
+ // If possible update connection rather than creating a new one.
+ let update = signaling.hasFeature('update-sdp')
+
+ // Create a connection if the current one has failed, as it
+ // would require an ICE restart rather than update to recover.
+ if (update && (peer.pc.iceConnectionState === 'failed' || peer.pc.connectionState === 'failed')) {
+ update = false
+ }
+
+ // If the connection needs to be updated but a new connection
+ // (or another update) is already pending ignore the new update.
+ // If a new connection needs to be created rather than updated
+ // then force it even if there is another one already pending.
+ if (update && delayedConnectionToPeer[peer.id]) {
+ return
+ }
+
+ signaling.requestOffer(peer.id, 'video', update ? peer.sid : undefined)
+
+ clearInterval(delayedConnectionToPeer[peer.id])
+
+ delayedConnectionToPeer[peer.id] = setInterval(function() {
+ console.debug('No offer received, request offer again' + update ? '(update)' : '', peer)
+
+ signaling.requestOffer(peer.id, 'video', update ? peer.sid : undefined)
+ }, 10000)
+
+ return
+ }
+
// Negotiation needed will be first triggered before the connection
// is established, but forcing a reconnection should be done only
// once the connection was established.
diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php
index fd722589a..992fa9ddd 100644
--- a/tests/integration/features/bootstrap/FeatureContext.php
+++ b/tests/integration/features/bootstrap/FeatureContext.php
@@ -63,6 +63,7 @@ class FeatureContext implements Context, SnippetAcceptingContext {
'A' => 16, // PERMISSIONS_PUBLISH_AUDIO
'V' => 32, // PERMISSIONS_PUBLISH_VIDEO
'P' => 64, // PERMISSIONS_PUBLISH_SCREEN
+ 'M' => 128, // PERMISSIONS_CHAT
];
/** @var string */
@@ -2300,6 +2301,7 @@ class FeatureContext implements Context, SnippetAcceptingContext {
$result = array_map(static function ($reaction, $list) use ($expected): array {
$list = array_map(function ($reaction) {
unset($reaction['timestamp']);
+ $reaction['actorId'] = ($reaction['actorType'] === 'guests') ? self::$sessionIdToUser[$reaction['actorId']] : (string) $reaction['actorId'];
return $reaction;
}, $list);
Assert::assertArrayHasKey($reaction, $expected, 'Not expected reaction: ' . $reaction);
diff --git a/tests/integration/features/chat/delete.feature b/tests/integration/features/chat/delete.feature
index 518c0f173..288429771 100644
--- a/tests/integration/features/chat/delete.feature
+++ b/tests/integration/features/chat/delete.feature
@@ -47,6 +47,22 @@ Feature: chat/reply
Then user "participant1" received a system messages in room "group room" to delete "Message 1"
Then user "participant2" received a system messages in room "group room" to delete "Message 1"
+ Scenario: user cannot delete without chat permission
+ Given user "participant1" creates room "group room" (v4)
+ | roomType | 2 |
+ | roomName | room |
+ And user "participant1" adds user "participant2" to room "group room" with 200 (v4)
+ And user "participant2" sends message "Message 1" to room "group room" with 201
+ Then user "participant1" sees the following messages in room "group room" with 200
+ | room | actorType | actorId | actorDisplayName | message | messageParameters | parentMessage |
+ | group room | users | participant2 | participant2-displayname | Message 1 | [] | |
+ # Removing chat permission only
+ Then user "participant1" sets permissions for "participant2" in room "group room" to "CSJLAVP" with 200 (v4)
+ And user "participant2" deletes message "Message 1" from room "group room" with 403
+ Then user "participant1" sees the following messages in room "group room" with 200
+ | room | actorType | actorId | actorDisplayName | message | messageParameters | parentMessage |
+ | group room | users | participant2 | participant2-displayname | Message 1 | [] | |
+
Scenario: moderator deletes other user message
Given user "participant1" creates room "group room" (v4)
| roomType | 2 |
diff --git a/tests/integration/features/chat/file-share.feature b/tests/integration/features/chat/file-share.feature
index cb35a4d16..caae8ffae 100644
--- a/tests/integration/features/chat/file-share.feature
+++ b/tests/integration/features/chat/file-share.feature
@@ -1,6 +1,7 @@
Feature: chat/public
Background:
Given user "participant1" exists
+ Given user "participant2" exists
Scenario: Share a file to a chat
Given user "participant1" creates room "public room" (v4)
@@ -11,6 +12,18 @@ Feature: chat/public
| room | actorType | actorId | actorDisplayName | message | messageParameters |
| public room | users | participant1 | participant1-displayname | {file} | "IGNORE" |
+ Scenario: Can not share a file without chat permission
+ Given user "participant1" creates room "public room" (v4)
+ | roomType | 3 |
+ | roomName | room |
+ And user "participant1" adds user "participant2" to room "public room" with 200 (v4)
+ # Removing chat permission only
+ Then user "participant1" sets permissions for "participant2" in room "public room" to "CSJLAVP" with 200 (v4)
+ When user "participant2" shares "welcome.txt" with room "public room"
+ And the OCS status code should be 404
+ Then user "participant1" sees the following messages in room "public room" with 200
+ | room | actorType | actorId | actorDisplayName | message | messageParameters |
+
Scenario: Delete share a file message from a chat
Given user "participant1" creates room "public room" (v4)
| roomType | 3 |
@@ -23,3 +36,19 @@ Feature: chat/public
Then user "participant1" sees the following messages in room "public room" with 200
| room | actorType | actorId | actorDisplayName | message | messageParameters |
| public room | users | participant1 | participant1-displayname | Message deleted by you | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname"}} |
+
+ Scenario: Can not delete a share file message without chat permission
+ Given user "participant1" creates room "public room" (v4)
+ | roomType | 3 |
+ | roomName | room |
+ And user "participant1" adds user "participant2" to room "public room" with 200 (v4)
+ When user "participant2" shares "welcome.txt" with room "public room"
+ Then user "participant1" sees the following messages in room "public room" with 200
+ | room | actorType | actorId | actorDisplayName | message | messageParameters |
+ | public room | users | participant2 | participant2-displayname | {file} | "IGNORE" |
+ # Removing chat permission only
+ Then user "participant1" sets permissions for "participant2" in room "public room" to "CSJLAVP" with 200 (v4)
+ And user "participant2" deletes message "shared::file::welcome.txt" from room "public room" with 403
+ Then user "participant1" sees the following messages in room "public room" with 200
+ | room | actorType | actorId | actorDisplayName | message | messageParameters |
+ | public room | users | participant2 | participant2-displayname | {file} | "IGNORE" |
diff --git a/tests/integration/features/chat/group.feature b/tests/integration/features/chat/group.feature
index 255258450..9021ce8c9 100644
--- a/tests/integration/features/chat/group.feature
+++ b/tests/integration/features/chat/group.feature
@@ -24,6 +24,18 @@ Feature: chat/group
| room | actorType | actorId | actorDisplayName | message | messageParameters |
| group room | users | participant2 | participant2-displayname | Message 1 | [] |
+ Scenario: invited user can not send without chat permissions
+ Given user "participant1" creates room "group room" (v4)
+ | roomType | 2 |
+ | invite | attendees1 |
+ # Removing chat permission only
+ Then user "participant1" sets permissions for "participant2" in room "group room" to "CSJLAVP" with 200 (v4)
+ When user "participant2" sends message "Message 1" to room "group room" with 403
+ When user "participant1" sends message "Message 2" to room "group room" with 201
+ Then user "participant2" sees the following messages in room "group room" with 200
+ | room | actorType | actorId | actorDisplayName | message | messageParameters |
+ | group room | users | participant1 | participant1-displayname | Message 2 | [] |
+
Scenario: not invited user can not send nor receive chat messages to nor from group room
Given user "participant1" creates room "group room" (v4)
| roomType | 2 |
diff --git a/tests/integration/features/chat/public.feature b/tests/integration/features/chat/public.feature
index d0617b456..7f3391ec9 100644
--- a/tests/integration/features/chat/public.feature
+++ b/tests/integration/features/chat/public.feature
@@ -23,6 +23,19 @@ Feature: chat/public
| room | actorType | actorId | actorDisplayName | message | messageParameters |
| public room | users | participant2 | participant2-displayname | Message 1 | [] |
+ Scenario: invited user can not send without chat permissions
+ Given user "participant1" creates room "public room" (v4)
+ | roomType | 3 |
+ | roomName | room |
+ And user "participant1" adds user "participant2" to room "public room" with 200 (v4)
+ # Removing chat permission only
+ Then user "participant1" sets permissions for "participant2" in room "public room" to "CSJLAVP" with 200 (v4)
+ When user "participant2" sends message "Message 1" to room "public room" with 403
+ When user "participant1" sends message "Message 2" to room "public room" with 201
+ Then user "participant2" sees the following messages in room "public room" with 200
+ | room | actorType | actorId | actorDisplayName | message | messageParameters |
+ | public room | users | participant1 | participant1-displayname | Message 2 | [] |
+
Scenario: not invited but joined user can send and receive chat messages to and from public room
Given user "participant1" creates room "public room" (v4)
| roomType | 3 |
diff --git a/tests/integration/features/chat/rich-object-share.feature b/tests/integration/features/chat/rich-object-share.feature
index 467a72501..7cc083b3a 100644
--- a/tests/integration/features/chat/rich-object-share.feature
+++ b/tests/integration/features/chat/rich-object-share.feature
@@ -1,6 +1,7 @@
Feature: chat/public
Background:
Given user "participant1" exists
+ Given user "participant2" exists
Scenario: Share a rich object to a chat
Given user "participant1" creates room "public room" (v4)
@@ -11,6 +12,17 @@ Feature: chat/public
| room | actorType | actorId | actorDisplayName | message | messageParameters |
| public room | users | participant1 | participant1-displayname | {object} | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname"},"object":{"name":"Another room","call-type":"group","type":"call","id":"R4nd0mT0k3n"}} |
+ Scenario: Can not share without chat permission
+ Given user "participant1" creates room "public room" (v4)
+ | roomType | 3 |
+ | roomName | room |
+ And user "participant1" adds user "participant2" to room "public room" with 200 (v4)
+ # Removing chat permission only
+ Then user "participant1" sets permissions for "participant2" in room "public room" to "CSJLAVP" with 200 (v4)
+ When user "participant2" shares rich-object "call" "R4nd0mT0k3n" '{"name":"Another room","call-type":"group"}' to room "public room" with 403 (v1)
+ Then user "participant1" sees the following messages in room "public room" with 200
+ | room | actorType | actorId | actorDisplayName | message | messageParameters |
+
Scenario: Delete a rich object from a chat
Given user "participant1" creates room "public room" (v4)
| roomType | 3 |
@@ -21,6 +33,19 @@ Feature: chat/public
| room | actorType | actorId | actorDisplayName | message | messageParameters | parentMessage |
| public room | users | participant1 | participant1-displayname | Message deleted by you | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname"}} | |
+ Scenario: Can not delete without chat permission
+ Given user "participant1" creates room "public room" (v4)
+ | roomType | 3 |
+ | roomName | room |
+ And user "participant1" adds user "participant2" to room "public room" with 200 (v4)
+ When user "participant2" shares rich-object "call" "R4nd0mT0k3n" '{"name":"Another room","call-type":"group"}' to room "public room" with 201 (v1)
+ # Removing chat permission only
+ Then user "participant1" sets permissions for "participant2" in room "public room" to "CSJLAVP" with 200 (v4)
+ And user "participant2" deletes message "shared::call::R4nd0mT0k3n" from room "public room" with 403
+ Then user "participant1" sees the following messages in room "public room" with 200
+ | room | actorType | actorId | actorDisplayName | message | messageParameters |
+ | public room | users | participant2 | participant2-displayname | {object} | {"actor":{"type":"user","id":"participant2","name":"participant2-displayname"},"object":{"name":"Another room","call-type":"group","type":"call","id":"R4nd0mT0k3n"}} |
+
Scenario: Share an invalid rich object to a chat
Given user "participant1" creates room "public room" (v4)
| roomType | 3 |
diff --git a/tests/integration/features/conversation-2/set-participant-permissions.feature b/tests/integration/features/conversation-2/set-participant-permissions.feature
index 7f0beb3cd..8126acbb1 100644
--- a/tests/integration/features/conversation-2/set-participant-permissions.feature
+++ b/tests/integration/features/conversation-2/set-participant-permissions.feature
@@ -16,12 +16,12 @@ Feature: set-publishing-permissions
And user "moderator" sets permissions for "moderator" in room "one-to-one room" to "S" with 400 (v4)
Then user "owner" sees the following attendees in room "one-to-one room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
And user "moderator" sees the following attendees in room "one-to-one room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
Scenario: owner can set permissions in group room
Given user "owner" creates room "group room" (v4)
@@ -36,18 +36,18 @@ Feature: set-publishing-permissions
And user "owner" sets permissions for "invited user" in room "group room" to "S" with 200 (v4)
Then user "owner" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CS |
And user "moderator" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CS |
And user "invited user" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CS |
Scenario: moderator can set permissions in group room
@@ -63,18 +63,18 @@ Feature: set-publishing-permissions
And user "owner" sets permissions for "invited user" in room "group room" to "S" with 200 (v4)
Then user "owner" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CS |
And user "moderator" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CS |
And user "invited user" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CS |
Scenario: others can not set permissions in group room
@@ -98,19 +98,19 @@ Feature: set-publishing-permissions
And user "guest not joined" sets permissions for "invited user" in room "group room" to "S" with 404 (v4)
Then user "owner" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
- | users | invited user | SJAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
+ | users | invited user | SJAVPM |
And user "moderator" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
- | users | invited user | SJAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
+ | users | invited user | SJAVPM |
And user "invited user" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
- | users | invited user | SJAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
+ | users | invited user | SJAVPM |
Scenario: owner can set permissions in public room
Given user "owner" creates room "public room" (v4)
@@ -132,43 +132,43 @@ Feature: set-publishing-permissions
And user "owner" sets permissions for "guest" in room "public room" to "S" with 200 (v4)
Then user "owner" sees the following attendees in room "public room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CS |
| users | not invited but joined user | CS |
- | guests | "guest moderator" | SJLAVP |
+ | guests | "guest moderator" | SJLAVPM |
| guests | "guest" | CS |
And user "moderator" sees the following attendees in room "public room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CS |
| users | not invited but joined user | CS |
- | guests | "guest moderator" | SJLAVP |
+ | guests | "guest moderator" | SJLAVPM |
| guests | "guest" | CS |
And user "invited user" sees the following attendees in room "public room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CS |
| users | not invited but joined user | CS |
- | guests | "guest moderator" | SJLAVP |
+ | guests | "guest moderator" | SJLAVPM |
| guests | "guest" | CS |
And user "not invited but joined user" sees the following attendees in room "public room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CS |
| users | not invited but joined user | CS |
- | guests | "guest moderator" | SJLAVP |
+ | guests | "guest moderator" | SJLAVPM |
| guests | "guest" | CS |
And user "guest moderator" sees the following attendees in room "public room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CS |
| users | not invited but joined user | CS |
- | guests | "guest moderator" | SJLAVP |
+ | guests | "guest moderator" | SJLAVPM |
| guests | "guest" | CS |
Scenario: moderator can set permissions in public room
@@ -191,43 +191,43 @@ Feature: set-publishing-permissions
And user "moderator" sets permissions for "guest" in room "public room" to "S" with 200 (v4)
Then user "owner" sees the following attendees in room "public room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CS |
| users | not invited but joined user | CS |
- | guests | "guest moderator" | SJLAVP |
+ | guests | "guest moderator" | SJLAVPM |
| guests | "guest" | CS |
And user "moderator" sees the following attendees in room "public room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CS |
| users | not invited but joined user | CS |
- | guests | "guest moderator" | SJLAVP |
+ | guests | "guest moderator" | SJLAVPM |
| guests | "guest" | CS |
And user "invited user" sees the following attendees in room "public room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CS |
| users | not invited but joined user | CS |
- | guests | "guest moderator" | SJLAVP |
+ | guests | "guest moderator" | SJLAVPM |
| guests | "guest" | CS |
And user "not invited but joined user" sees the following attendees in room "public room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CS |
| users | not invited but joined user | CS |
- | guests | "guest moderator" | SJLAVP |
+ | guests | "guest moderator" | SJLAVPM |
| guests | "guest" | CS |
And user "guest moderator" sees the following attendees in room "public room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CS |
| users | not invited but joined user | CS |
- | guests | "guest moderator" | SJLAVP |
+ | guests | "guest moderator" | SJLAVPM |
| guests | "guest" | CS |
# Guests can not fetch the participant list
@@ -252,43 +252,43 @@ Feature: set-publishing-permissions
And user "guest moderator" sets permissions for "guest" in room "public room" to "S" with 200 (v4)
Then user "owner" sees the following attendees in room "public room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CS |
| users | not invited but joined user | CS |
- | guests | "guest moderator" | SJLAVP |
+ | guests | "guest moderator" | SJLAVPM |
| guests | "guest" | CS |
And user "moderator" sees the following attendees in room "public room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CS |
| users | not invited but joined user | CS |
- | guests | "guest moderator" | SJLAVP |
+ | guests | "guest moderator" | SJLAVPM |
| guests | "guest" | CS |
And user "invited user" sees the following attendees in room "public room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CS |
| users | not invited but joined user | CS |
- | guests | "guest moderator" | SJLAVP |
+ | guests | "guest moderator" | SJLAVPM |
| guests | "guest" | CS |
And user "not invited but joined user" sees the following attendees in room "public room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CS |
| users | not invited but joined user | CS |
- | guests | "guest moderator" | SJLAVP |
+ | guests | "guest moderator" | SJLAVPM |
| guests | "guest" | CS |
And user "guest moderator" sees the following attendees in room "public room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CS |
| users | not invited but joined user | CS |
- | guests | "guest moderator" | SJLAVP |
+ | guests | "guest moderator" | SJLAVPM |
| guests | "guest" | CS |
# Guests can not fetch the participant list
@@ -338,44 +338,44 @@ Feature: set-publishing-permissions
And user "guest not joined" sets permissions for "guest" in room "public room" to "S" with 404 (v4)
Then user "owner" sees the following attendees in room "public room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
- | users | invited user | SJAVP |
- | users | not invited but joined user | SJAVP |
- | guests | "guest moderator" | SJLAVP |
- | guests | "guest" | SJAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
+ | users | invited user | SJAVPM |
+ | users | not invited but joined user | SJAVPM |
+ | guests | "guest moderator" | SJLAVPM |
+ | guests | "guest" | SJAVPM |
And user "moderator" sees the following attendees in room "public room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
- | users | invited user | SJAVP |
- | users | not invited but joined user | SJAVP |
- | guests | "guest moderator" | SJLAVP |
- | guests | "guest" | SJAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
+ | users | invited user | SJAVPM |
+ | users | not invited but joined user | SJAVPM |
+ | guests | "guest moderator" | SJLAVPM |
+ | guests | "guest" | SJAVPM |
And user "invited user" sees the following attendees in room "public room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
- | users | invited user | SJAVP |
- | users | not invited but joined user | SJAVP |
- | guests | "guest moderator" | SJLAVP |
- | guests | "guest" | SJAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
+ | users | invited user | SJAVPM |
+ | users | not invited but joined user | SJAVPM |
+ | guests | "guest moderator" | SJLAVPM |
+ | guests | "guest" | SJAVPM |
And user "not invited but joined user" sees the following attendees in room "public room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
- | users | invited user | SJAVP |
- | users | not invited but joined user | SJAVP |
- | guests | "guest moderator" | SJLAVP |
- | guests | "guest" | SJAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
+ | users | invited user | SJAVPM |
+ | users | not invited but joined user | SJAVPM |
+ | guests | "guest moderator" | SJLAVPM |
+ | guests | "guest" | SJAVPM |
And user "guest moderator" sees the following attendees in room "public room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
- | users | invited user | SJAVP |
- | users | not invited but joined user | SJAVP |
- | guests | "guest moderator" | SJLAVP |
- | guests | "guest" | SJAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
+ | users | invited user | SJAVPM |
+ | users | not invited but joined user | SJAVPM |
+ | guests | "guest moderator" | SJLAVPM |
+ | guests | "guest" | SJAVPM |
# Guests can not fetch the participant list
Scenario: participants can not set permissions in room for a share
@@ -402,14 +402,14 @@ Feature: set-publishing-permissions
And user "guest" sets permissions for "guest" in room "file last share room" to "S" with 403 (v4)
Then user "owner of file" sees the following attendees in room "file last share room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner of file | SJAVP |
- | users | user with access to file | SJAVP |
- | guests | "guest" | SJAVP |
+ | users | owner of file | SJAVPM |
+ | users | user with access to file | SJAVPM |
+ | guests | "guest" | SJAVPM |
And user "user with access to file" sees the following attendees in room "file last share room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner of file | SJAVP |
- | users | user with access to file | SJAVP |
- | guests | "guest" | SJAVP |
+ | users | owner of file | SJAVPM |
+ | users | user with access to file | SJAVPM |
+ | guests | "guest" | SJAVPM |
# This does not make much sense, but there is no real need to block it either.
Scenario: owner can set permissions in a password request room
@@ -427,5 +427,5 @@ Feature: set-publishing-permissions
And user "owner of file" sets permissions for "guest" in room "password request for last share room" to "S" with 200 (v4)
Then user "owner of file" sees the following attendees in room "password request for last share room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner of file | SJLAVP |
+ | users | owner of file | SJLAVPM |
| guests | "guest" | CS |
diff --git a/tests/integration/features/conversation-2/set-permissions.feature b/tests/integration/features/conversation-2/set-permissions.feature
index c04b4f021..6cf6c4919 100644
--- a/tests/integration/features/conversation-2/set-permissions.feature
+++ b/tests/integration/features/conversation-2/set-permissions.feature
@@ -13,26 +13,26 @@ Feature: set-publishing-permissions
And user "owner" adds user "invited user" to room "group room" with 200 (v4)
Then user "owner" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
- | users | invited user | SJAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
+ | users | invited user | SJAVPM |
When user "owner" sets default permissions for room "group room" to "S" with 200 (v4)
Then user "owner" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CS |
When user "moderator" sets default permissions for room "group room" to "AV" with 200 (v4)
Then user "owner" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CAV |
When user "invited user" sets default permissions for room "group room" to "D" with 403 (v4)
Then user "owner" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CAV |
Scenario: Owner and moderators can set call permissions users can not
@@ -47,8 +47,8 @@ Feature: set-publishing-permissions
When user "invited user" sets call permissions for room "group room" to "D" with 403 (v4)
Then user "owner" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | moderator | SJLAVP |
+ | users | owner | SJLAVPM |
+ | users | moderator | SJLAVPM |
| users | invited user | CAV |
Scenario: User setting over call setting over conversation setting over default
@@ -59,33 +59,33 @@ Feature: set-publishing-permissions
When user "owner" sets default permissions for room "group room" to "S" with 200 (v4)
Then user "owner" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
+ | users | owner | SJLAVPM |
| users | invited user | CS |
When user "owner" sets call permissions for room "group room" to "A" with 200 (v4)
Then user "owner" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
+ | users | owner | SJLAVPM |
| users | invited user | CA |
And user "owner" sets permissions for "invited user" in room "group room" to "V" with 200 (v4)
Then user "owner" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
+ | users | owner | SJLAVPM |
| users | invited user | CV |
And user "owner" sets permissions for "invited user" in room "group room" to "D" with 200 (v4)
Then user "owner" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
+ | users | owner | SJLAVPM |
| users | invited user | CA |
When user "owner" sets call permissions for room "group room" to "D" with 200 (v4)
Then user "owner" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
+ | users | owner | SJLAVPM |
| users | invited user | CS |
When user "owner" sets default permissions for room "group room" to "D" with 200 (v4)
Then user "owner" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions |
- | users | owner | SJLAVP |
- | users | invited user | SJAVP |
+ | users | owner | SJLAVPM |
+ | users | invited user | SJAVPM |
@@ -97,12 +97,12 @@ Feature: set-publishing-permissions
And user "owner" sets permissions for "invited user" in room "group room" to "V" with 200 (v4)
And user "owner" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions | attendeePermissions |
- | users | owner | SJLAVP | D |
+ | users | owner | SJLAVPM | D |
| users | invited user | CV | CV |
When user "owner" sets call permissions for room "group room" to "A" with 200 (v4)
Then user "owner" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions | attendeePermissions |
- | users | owner | SJLAVP | D |
+ | users | owner | SJLAVPM | D |
| users | invited user | CA | D |
Scenario: setting default permissions resets participant permissions
@@ -113,12 +113,12 @@ Feature: set-publishing-permissions
And user "owner" sets permissions for "invited user" in room "group room" to "V" with 200 (v4)
And user "owner" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions | attendeePermissions |
- | users | owner | SJLAVP | D |
+ | users | owner | SJLAVPM | D |
| users | invited user | CV | CV |
When user "owner" sets default permissions for room "group room" to "A" with 200 (v4)
Then user "owner" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions | attendeePermissions |
- | users | owner | SJLAVP | D |
+ | users | owner | SJLAVPM | D |
| users | invited user | CA | D |
Scenario: setting default permissions does not reset call permissions
@@ -129,10 +129,10 @@ Feature: set-publishing-permissions
And user "owner" sets call permissions for room "group room" to "V" with 200 (v4)
And user "owner" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions | attendeePermissions |
- | users | owner | SJLAVP | D |
+ | users | owner | SJLAVPM | D |
| users | invited user | CV | D |
When user "owner" sets default permissions for room "group room" to "A" with 200 (v4)
Then user "owner" sees the following attendees in room "group room" with 200 (v4)
| actorType | actorId | permissions | attendeePermissions |
- | users | owner | SJLAVP | D |
+ | users | owner | SJLAVPM | D |
| users | invited user | CV | D |
diff --git a/tests/integration/features/reaction/react.feature b/tests/integration/features/reaction/react.feature
index 7c9613e8b..2bedf4ac1 100644
--- a/tests/integration/features/reaction/react.feature
+++ b/tests/integration/features/reaction/react.feature
@@ -25,17 +25,44 @@ Feature: reaction/react
| users | participant1 | participant1-displayname | 👍 |
| users | participant2 | participant2-displayname | 👍 |
And user "participant1" react with "🚀" on message "Message 1" to room "room" with 201
+ Then user "guest" joins room "room" with 200 (v4)
+ And user "guest" react with "👤" on message "Message 1" to room "room" with 201
Then user "participant1" sees the following messages in room "room" with 200
- | room | actorType | actorId | actorDisplayName | message | messageParameters | reactions | reactionsSelf |
- | room | users | participant1 | participant1-displayname | Message 1 | [] | {"👍":2,"🚀":1} | ["👍","🚀"] |
+ | room | actorType | actorId | actorDisplayName | message | messageParameters | reactions | reactionsSelf |
+ | room | users | participant1 | participant1-displayname | Message 1 | [] | {"👍":2,"👤":1,"🚀":1} | ["👍","🚀"] |
Then user "participant1" sees the following system messages in room "room" with 200
| room | actorType | actorId | actorDisplayName | systemMessage |
+ | room | guests | guest | | reaction |
| room | users | participant1 | participant1-displayname | reaction |
| room | users | participant1 | participant1-displayname | reaction |
| room | users | participant2 | participant2-displayname | reaction |
| room | users | participant1 | participant1-displayname | user_added |
| room | users | participant1 | participant1-displayname | conversation_created |
+ Scenario: React to message fails without chat permission
+ Given user "participant1" creates room "room" (v4)
+ | roomType | 3 |
+ | roomName | room |
+ And user "participant1" adds user "participant2" to room "room" with 200 (v4)
+ And user "participant1" sends message "Message 1" to room "room" with 201
+ And user "participant2" react with "👍" on message "Message 1" to room "room" with 201
+ | actorType | actorId | actorDisplayName | reaction |
+ | users | participant2 | participant2-displayname | 👍 |
+ Then user "participant1" sees the following system messages in room "room" with 200
+ | room | actorType | actorId | actorDisplayName | systemMessage |
+ | room | users | participant2 | participant2-displayname | reaction |
+ | room | users | participant1 | participant1-displayname | user_added |
+ | room | users | participant1 | participant1-displayname | conversation_created |
+ # Removing chat permission only
+ Then user "participant1" sets permissions for "participant2" in room "room" to "CSJLAVP" with 200 (v4)
+ When user "participant2" delete react with "👍" on message "Message 1" to room "room" with 403
+ And user "participant2" react with "💙" on message "Message 1" to room "room" with 403
+ And user "participant1" sees the following system messages in room "room" with 200
+ | room | actorType | actorId | actorDisplayName | systemMessage |
+ | room | users | participant2 | participant2-displayname | reaction |
+ | room | users | participant1 | participant1-displayname | user_added |
+ | room | users | participant1 | participant1-displayname | conversation_created |
+
Scenario: React two times to same message with the same reaction
Given user "participant1" creates room "room" (v4)
| roomType | 3 |
@@ -66,17 +93,24 @@ Feature: reaction/react
And user "participant2" react with "👍" on message "Message 1" to room "room" with 201
| actorType | actorId | actorDisplayName | reaction |
| users | participant2 | participant2-displayname | 👍 |
+ Then user "guest" joins room "room" with 200 (v4)
+ And user "guest" react with "👤" on message "Message 1" to room "room" with 201
Then user "participant1" sees the following messages in room "room" with 200
| room | actorType | actorId | actorDisplayName | message | messageParameters | reactions |
- | room | users | participant1 | participant1-displayname | Message 1 | [] | {"👍":1} |
+ | room | users | participant1 | participant1-displayname | Message 1 | [] | {"👤":1,"👍":1} |
And user "participant2" delete react with "👍" on message "Message 1" to room "room" with 200
| actorType | actorId | actorDisplayName | reaction |
+ | guests | guest | | 👤 |
+ And user "guest" delete react with "👤" on message "Message 1" to room "room" with 200
+ | actorType | actorId | actorDisplayName | reaction |
Then user "participant1" sees the following messages in room "room" with 200
| room | actorType | actorId | actorDisplayName | message | messageParameters | reactions |
| room | users | participant1 | participant1-displayname | Message 1 | [] | [] |
Then user "participant1" sees the following system messages in room "room" with 200
| room | actorType | actorId | actorDisplayName | systemMessage |
+ | room | guests | guest | | reaction_revoked |
| room | users | participant2 | participant2-displayname | reaction_revoked |
+ | room | guests | guest | | reaction_deleted |
| room | users | participant2 | participant2-displayname | reaction_deleted |
| room | users | participant1 | participant1-displayname | user_added |
| room | users | participant1 | participant1-displayname | conversation_created |
diff --git a/tests/php/CapabilitiesTest.php b/tests/php/CapabilitiesTest.php
index 2ebf6b006..b9ade2e0c 100644
--- a/tests/php/CapabilitiesTest.php
+++ b/tests/php/CapabilitiesTest.php
@@ -104,6 +104,7 @@ class CapabilitiesTest extends TestCase {
'conversation-permissions',
'rich-object-list-media',
'rich-object-delete',
+ 'chat-permission',
'reactions',
];
}
diff --git a/tests/php/Collaboration/Collaborators/RoomPluginTest.php b/tests/php/Collaboration/Collaborators/RoomPluginTest.php
index f96d23569..bb116ffd7 100644
--- a/tests/php/Collaboration/Collaborators/RoomPluginTest.php
+++ b/tests/php/Collaboration/Collaborators/RoomPluginTest.php
@@ -27,6 +27,8 @@ namespace OCA\Talk\Tests\php\Collaboration\Collaborators;
use OCA\Talk\Collaboration\Collaborators\RoomPlugin;
use OCA\Talk\Manager;
+use OCA\Talk\Model\Attendee;
+use OCA\Talk\Participant;
use OCA\Talk\Room;
use OCP\Collaboration\Collaborators\ISearchResult;
use OCP\Collaboration\Collaborators\SearchResultType;
@@ -65,8 +67,9 @@ class RoomPluginTest extends TestCase {
$this->plugin = new RoomPlugin($this->manager, $this->userSession);
}
- private function newRoom(int $type, string $token, string $name): Room {
+ private function newRoom(int $type, string $token, string $name, int $permissions = Attendee::PERMISSIONS_MAX_DEFAULT): Room {
$room = $this->createMock(Room::class);
+ $participant = $this->createMock(Participant::class);
$room->expects($this->any())
->method('getType')
@@ -80,6 +83,14 @@ class RoomPluginTest extends TestCase {
->method('getDisplayName')
->willReturn($name);
+ $room->expects($this->any())
+ ->method('getParticipant')
+ ->willReturn($participant);
+
+ $participant->expects($this->any())
+ ->method('getPermissions')
+ ->willReturn($permissions);
+
return $room;
}
@@ -116,6 +127,11 @@ class RoomPluginTest extends TestCase {
$this->newResult('Room name', 'roomToken')
], false],
+ // Chats without chat permission are not returned
+ ['room', 2, 0, [
+ $this->newRoom(Room::TYPE_GROUP, 'roomToken', 'Room name', Attendee::PERMISSIONS_MAX_DEFAULT ^ Attendee::PERMISSIONS_CHAT),
+ ], [], [], false],
+
// Search term with single exact match
['room name', 2, 0, [
$this->newRoom(Room::TYPE_GROUP, 'roomToken', 'Unmatched name'),
diff --git a/tests/php/Listener/RestrictStartingCallsTest.php b/tests/php/Listener/RestrictStartingCallsTest.php
index b94da5a55..2ffbe04ab 100644
--- a/tests/php/Listener/RestrictStartingCallsTest.php
+++ b/tests/php/Listener/RestrictStartingCallsTest.php
@@ -97,7 +97,9 @@ class RestrictStartingCallsTest extends TestCase {
$this->expectException(ForbiddenException::class);
}
+ $this->overwriteService(RestrictStartingCalls::class, $this->listener);
$this->listener->checkStartCallPermissions($event);
+ $this->restoreService(RestrictStartingCalls::class);
if (!$throws) {
self::assertTrue(true);
diff --git a/tests/psalm-baseline.xml b/tests/psalm-baseline.xml
index 1d44ea2cb..ea600086d 100644
--- a/tests/psalm-baseline.xml
+++ b/tests/psalm-baseline.xml
@@ -37,12 +37,6 @@
<code>getById</code>
</UndefinedInterfaceMethod>
</file>
- <file src="lib/Chat/SystemMessage/Listener.php">
- <InvalidArgument occurrences="2">
- <code>$listener</code>
- <code>$listener</code>
- </InvalidArgument>
- </file>
<file src="lib/Command/ActiveCalls.php">
<UndefinedClass occurrences="1">
<code>Base</code>
@@ -175,17 +169,19 @@
</MissingDependency>
</file>
<file src="lib/Controller/SettingsController.php">
+ <InvalidArgument occurrences="1">
+ <code>SharedStorage::class</code>
+ </InvalidArgument>
<MissingDependency occurrences="3">
<code>$this-&gt;rootFolder</code>
<code>IRootFolder</code>
<code>IRootFolder</code>
</MissingDependency>
- <InvalidArgument occurrences="1">
- <code>SharedStorage::class</code>
- </InvalidArgument>
</file>
<file src="lib/Controller/SignalingController.php">
- <UndefinedClass occurrences="1"/>
+ <UndefinedClass occurrences="1">
+ <code>ConnectException</code>
+ </UndefinedClass>
</file>
<file src="lib/Controller/TempAvatarController.php">
<UndefinedClass occurrences="1">
@@ -203,6 +199,9 @@
</UndefinedClass>
</file>
<file src="lib/Files/Util.php">
+ <InvalidArgument occurrences="1">
+ <code>SharedStorage::class</code>
+ </InvalidArgument>
<MissingDependency occurrences="5">
<code>$this-&gt;rootFolder</code>
<code>$this-&gt;rootFolder</code>
@@ -210,16 +209,23 @@
<code>IRootFolder</code>
<code>IRootFolder</code>
</MissingDependency>
- <InvalidArgument occurrences="1">
- <code>SharedStorage::class</code>
- </InvalidArgument>
</file>
<file src="lib/MatterbridgeManager.php">
<UndefinedClass occurrences="7">
+ <code>$this-&gt;tokenProvider</code>
+ <code>$this-&gt;tokenProvider</code>
+ <code>$this-&gt;tokenProvider</code>
+ <code>IAuthTokenProvider</code>
<code>IAuthTokenProvider</code>
<code>IToken</code>
+ <code>IToken</code>
</UndefinedClass>
</file>
+ <file src="lib/Middleware/InjectionMiddleware.php">
+ <UndefinedInterfaceMethod occurrences="1">
+ <code>getAnnotationParameter</code>
+ </UndefinedInterfaceMethod>
+ </file>
<file src="lib/Migration/Version2001Date20170707115443.php">
<InvalidArrayAccess occurrences="1">
<code>$return['num_rooms']</code>
@@ -257,10 +263,6 @@
</UndefinedClass>
</file>
<file src="lib/Share/RoomShareProvider.php">
- <InvalidArgument occurrences="2">
- <code>new GenericEvent($existingShare)</code>
- <code>self::class . '::' . 'share_file_again'</code>
- </InvalidArgument>
<UndefinedClass occurrences="1">
<code>Cache</code>
</UndefinedClass>