diff --git a/CHANGELOG.md b/CHANGELOG.md index 73baac7a..7a460cff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 10.1.2 + +* Fix: clicking on the import custom emojis button, without selected any file, was resulting in a state with all action button disabled. + +## 10.1.1 + +* Fix #436: Saving emojis per batch, to avoid hitting max payload limit. +* Fix: the emojis import function could add more entries than max allowed emoji count. +* Fix #437: removing last line if empty when importing emojis. +* Updated translations: de, sk. + ## 10.1.0 ### New features diff --git a/client/@types/global.d.ts b/client/@types/global.d.ts index dd200ec7..1f15b839 100644 --- a/client/@types/global.d.ts +++ b/client/@types/global.d.ts @@ -83,6 +83,7 @@ declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_BOT_NICKNAME: string declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FOR_MORE_INFO: string declare const LOC_VALIDATION_ERROR: string +declare const LOC_TOO_MANY_ENTRIES: string declare const LOC_INVALID_VALUE: string declare const LOC_INVALID_VALUE_MISSING: string declare const LOC_INVALID_VALUE_WRONG_TYPE: string diff --git a/client/common/configuration/elements/channel-emojis.ts b/client/common/configuration/elements/channel-emojis.ts index afb9e566..4475cc65 100644 --- a/client/common/configuration/elements/channel-emojis.ts +++ b/client/common/configuration/elements/channel-emojis.ts @@ -102,9 +102,13 @@ export class ChannelEmojisElement extends LivechatElement { try { this.actionDisabled = true - await this._channelDetailsService.saveEmojisConfiguration(this.channelId, this.channelEmojisConfiguration.emojis) + this.channelEmojisConfiguration = await this._channelDetailsService.saveEmojisConfiguration( + this.channelId, + this.channelEmojisConfiguration.emojis + ) this.validationError = undefined this.ptNotifier.info(await this.ptTranslate(LOC_SUCCESSFULLY_SAVED)) + this.requestUpdate('channelEmojisConfiguration') this.requestUpdate('_validationError') } catch (error) { this.validationError = undefined @@ -128,7 +132,6 @@ export class ChannelEmojisElement extends LivechatElement { */ public async importEmojis (ev: Event): Promise { ev.preventDefault() - this.actionDisabled = true try { // download a json file: const file = await new Promise((resolve, reject) => { @@ -149,6 +152,8 @@ export class ChannelEmojisElement extends LivechatElement { input.remove() }) + this.actionDisabled = true + const content = await new Promise((resolve, reject) => { const fileReader = new FileReader() fileReader.onerror = reject @@ -170,6 +175,15 @@ export class ChannelEmojisElement extends LivechatElement { if (!Array.isArray(json)) { throw new Error('Invalid data, an array was expected') } + + // Before adding new entries, we check if the last current line is empty, + // and remove it in such case. + // See https://github.com/JohnXLivingston/peertube-plugin-livechat/issues/437 + const last = this.channelEmojisConfiguration?.emojis.customEmojis.slice(-1)[0] + if (last && last.sn === '' && last.url === '') { + this.channelEmojisConfiguration?.emojis.customEmojis.pop() + } + for (const entry of json) { if (typeof entry !== 'object') { throw new Error('Invalid data') diff --git a/client/common/configuration/services/channel-details.ts b/client/common/configuration/services/channel-details.ts index aa3b02b6..d280c235 100644 --- a/client/common/configuration/services/channel-details.ts +++ b/client/common/configuration/services/channel-details.ts @@ -5,10 +5,12 @@ import type { RegisterClientOptions } from '@peertube/peertube-types/client' import type { - ChannelLiveChatInfos, ChannelConfiguration, ChannelConfigurationOptions, ChannelEmojisConfiguration, ChannelEmojis + ChannelLiveChatInfos, ChannelConfiguration, ChannelConfigurationOptions, ChannelEmojisConfiguration, ChannelEmojis, + CustomEmojiDefinition } from 'shared/lib/types' import { ValidationError, ValidationErrorType } from '../../lib/models/validation' import { getBaseRoute } from '../../../utils/uri' +import { maxEmojisPerChannel } from 'shared/lib/emojis' export class ChannelDetailsService { public _registerClientOptions: RegisterClientOptions @@ -179,6 +181,16 @@ export class ChannelDetailsService { public async validateEmojisConfiguration (channelEmojis: ChannelEmojis): Promise { const propertiesError: ValidationError['properties'] = {} + if (channelEmojis.customEmojis.length > maxEmojisPerChannel) { + // This can happen when using the import function. + const validationError = new ValidationError( + 'ChannelEmojisValidationError', + await this._registerClientOptions.peertubeHelpers.translate(LOC_TOO_MANY_ENTRIES), + propertiesError + ) + throw validationError + } + const seen = new Map() for (const [i, e] of channelEmojis.customEmojis.entries()) { propertiesError[`emojis.${i}.sn`] = [] @@ -213,11 +225,57 @@ export class ChannelDetailsService { public async saveEmojisConfiguration ( channelId: number, channelEmojis: ChannelEmojis - ): Promise { + ): Promise { if (!await this.validateEmojisConfiguration(channelEmojis)) { throw new Error('Invalid form data') } + // Note: API request body size is limited to 100Kb (expressjs body-parser defaut limit, and Peertube nginx config). + // So we must send new emojis 1 by 1, to be sure to not reach the limit. + if (!channelEmojis.customEmojis.find(e => e.url.startsWith('data:'))) { + // No new emojis, just saving. + return this._saveEmojisConfiguration(channelId, channelEmojis) + } + + let lastResult: ChannelEmojisConfiguration | undefined + let customEmojis: CustomEmojiDefinition[] = [...channelEmojis.customEmojis] // copy the original array + let i = customEmojis.findIndex(e => e.url.startsWith('data:')) + let watchDog = 0 + while (i >= 0) { + watchDog++ + if (watchDog > channelEmojis.customEmojis.length + 10) { // just to avoid infinite loop + throw new Error('Seems we have sent too many emojis, this was not expected') + } + const data: CustomEmojiDefinition[] = customEmojis.slice(0, i + 1) // all elements until first new file + data.push( + // all remaining elements that where already uploaded (to not loose them): + ...customEmojis.slice(i + 1).filter((e) => !e.url.startsWith('data:')) + ) + lastResult = await this._saveEmojisConfiguration(channelId, { + customEmojis: data + }) + + // Must inject the result in customEmojis + const temp = lastResult.emojis.customEmojis.slice(0, i + 1) // last element should have been replace by a http url + temp.push( + ...customEmojis.slice(i + 1) // remaining elements in the previous array + ) + customEmojis = temp + + // and searching again next new emojis + i = customEmojis.findIndex(e => e.url.startsWith('data:')) + } + if (!lastResult) { + // This should not happen... + throw new Error('Unexpected: no last result') + } + return lastResult + } + + private async _saveEmojisConfiguration ( + channelId: number, + channelEmojis: ChannelEmojis + ): Promise { const response = await fetch( getBaseRoute(this._registerClientOptions) + '/api/configuration/channel/emojis/' + @@ -230,10 +288,9 @@ export class ChannelDetailsService { ) if (!response.ok) { - if (response.status === 404) { - // File does not exist yet, that is a normal use case. - } throw new Error('Can\'t get channel emojis options.') } + + return response.json() } } diff --git a/languages/de.yml b/languages/de.yml index a88c1424..934b0136 100644 --- a/languages/de.yml +++ b/languages/de.yml @@ -511,3 +511,4 @@ token_action_revoke_confirm: Sind Sie sicher, dass Sie diesen Token widerrufen w auth_description: "

Authentifizierung

\n" livechat_token_disabled_label: Livechat-Token deaktivieren share_chat_dock: Dock +token_date: Datum diff --git a/languages/en.yml b/languages/en.yml index 3e7dea32..38f3cb50 100644 --- a/languages/en.yml +++ b/languages/en.yml @@ -455,6 +455,7 @@ invalid_value_wrong_format: "Value is in the wrong format." invalid_value_not_in_range: "Value is not in authorized range." invalid_value_file_too_big: "File size is too big (max size: %s)." invalid_value_duplicate: "Duplicate value" +too_many_entries: "Too many entries" slow_mode_info: "Slow mode is enabled, users can send a message every %1$s seconds." diff --git a/languages/fr.yml b/languages/fr.yml index 11a73280..eab63f8d 100644 --- a/languages/fr.yml +++ b/languages/fr.yml @@ -522,3 +522,4 @@ livechat_token_disabled_description: "Les utilisateur⋅rices peuvent générer pour inclure le tchat dans les docks web dans OBS.\nConsultez la documentation pour plus d'informations.\nVous pouvez désactiver cette fonctionnalité en cochant ce paramètre.\n" +too_many_entries: "Trop d'entrées" diff --git a/languages/sk.yml b/languages/sk.yml new file mode 100644 index 00000000..0bf6b72e --- /dev/null +++ b/languages/sk.yml @@ -0,0 +1,8 @@ +online_help: "Online pomoc" +open_chat: "Otvoriť chat" +open_chat_new_window: "Otvoriť chat na novom okne" +close_chat: "Zavrieť čet" +use_chat: "Použite chat" +use_chat_help: "Ak je povolená, vedľa videa sa zobrazí chat." +share_chat_link: "Zdieľať odkaz na čet" +read_only: "Iba na čítanie" diff --git a/package-lock.json b/package-lock.json index 91457dd9..a0fe6c3f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "peertube-plugin-livechat", - "version": "10.1.0", + "version": "10.1.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "peertube-plugin-livechat", - "version": "10.1.0", + "version": "10.1.2", "license": "AGPL-3.0", "dependencies": { "@xmpp/jid": "^0.13.1", diff --git a/server/lib/routers/api/configuration.ts b/server/lib/routers/api/configuration.ts index ed4e7ce9..43e88be5 100644 --- a/server/lib/routers/api/configuration.ts +++ b/server/lib/routers/api/configuration.ts @@ -168,7 +168,16 @@ async function initConfigurationApiRouter (options: RegisterServerOptions, route await emojis.saveChannelDefinition(channelInfos.id, emojisDefinitionSanitized, bufferInfos) - res.sendStatus(200) + // Reloading data, to send them back to front: + const channelEmojis = + (await emojis.channelCustomEmojisDefinition(channelInfos.id)) ?? + emojis.emptyChannelDefinition() + const result: ChannelEmojisConfiguration = { + channel: channelInfos, + emojis: channelEmojis + } + res.status(200) + res.json(result) } catch (err) { logger.error(err) res.sendStatus(500) diff --git a/shared/lib/emojis.ts b/shared/lib/emojis.ts index 87e6f061..6ea36795 100644 --- a/shared/lib/emojis.ts +++ b/shared/lib/emojis.ts @@ -2,6 +2,9 @@ // // SPDX-License-Identifier: AGPL-3.0-only +// Note: API request body size is limited to 100Kb (expressjs body-parser defaut limit, and Peertube nginx config). +// So we must be sure to never send more than 100Kb. The front end sends new emojis by batch, but maxSize must remain +// as little as possible, so that we never reach 100Kb in JSON/base64 format. export const maxSize: number = 30 * 1024 export const allowedExtensions = ['png', 'jpg', 'jpeg', 'gif'] export const inputFileAccept = ['image/jpg', 'image/png', 'image/gif'] diff --git a/support/documentation/po/livechat.de.po b/support/documentation/po/livechat.de.po index e2d1ba65..5e7182ed 100644 --- a/support/documentation/po/livechat.de.po +++ b/support/documentation/po/livechat.de.po @@ -8,15 +8,17 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "POT-Creation-Date: 2024-06-19 12:16+0200\n" -"PO-Revision-Date: 2024-06-18 20:10+0000\n" -"Last-Translator: Victor Hampel \n" -"Language-Team: German \n" +"PO-Revision-Date: 2024-06-20 11:44+0000\n" +"Last-Translator: Victor Hampel " +"\n" +"Language-Team: German \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Weblate 5.5.5\n" +"X-Generator: Weblate 5.6\n" #. type: Yaml Front Matter Hash Value: description #: support/documentation/content/en/contact/_index.md @@ -1165,6 +1167,17 @@ msgstr "Jetzt können Sie dieses Konto zu Gateways hinzufügen und bestimmte Liv #: support/documentation/content/en/documentation/admin/advanced/matterbridge.md msgid "This documentation use an anonymous account to connect the bridge to the chat. But since the livechat v10.1.0, there is a new way to generate long term authentication token, that allows to connect using your account. This is used for [OBS docks](/peertube-plugin-livechat/documentation/user/obs). Using this feature for other purposes is not documented and not officially supported yet. If you want to use it anyway, you can request a token by calling then `/plugins/livechat/router/api/auth/tokens` endpoint. To get needed headers and request body, just check what happens when you generate a new token for OBS docks." msgstr "" +"In dieser Dokumentation wird ein anonymes Konto verwendet, um die Brücke mit " +"dem Chat zu verbinden. Aber seit dem Livechat v10.1.0 gibt es eine neue " +"Möglichkeit, ein langfristiges Authentifizierungs-Token zu generieren, das " +"es erlaubt, sich mit dem eigenen Konto zu verbinden. Dies wird für [OBS " +"docks](/peertube-plugin-livechat/de/documentation/user/obs) verwendet. Die " +"Verwendung dieser Funktion für andere Zwecke ist nicht dokumentiert und wird " +"noch nicht offiziell unterstützt. Wenn Sie es trotzdem benutzen wollen, " +"können Sie ein Token anfordern, indem Sie den Endpunkt `/plugins/livechat/" +"router/api/auth/tokens` aufrufen. Um die benötigten Header und den Request " +"Body zu erhalten, prüfe einfach, was passiert, wenn du ein neues Token für " +"OBS Docks generierst." #. type: Yaml Front Matter Hash Value: description #: support/documentation/content/en/documentation/admin/advanced/xmpp_clients.md @@ -2508,10 +2521,8 @@ msgstr "OBS Overlay" #. type: Plain text #: support/documentation/content/en/documentation/user/obs.md -#, fuzzy -#| msgid "You can easily include the chat in your stream." msgid "You can easily include the chat in your video stream." -msgstr "Sie können den Chat ganz einfach in Ihren Stream integrieren." +msgstr "Sie können den Chat ganz einfach in Ihren Videostream integrieren." #. type: Plain text #: support/documentation/content/en/documentation/user/obs.md @@ -2574,7 +2585,7 @@ msgstr "Hinweis: Sie können vollständig die Chat-Farben anpassen. Dies ist noc #: support/documentation/content/en/documentation/user/obs.md #, no-wrap msgid "OBS Dock" -msgstr "" +msgstr "OBS Dock" #. type: Plain text #: support/documentation/content/en/documentation/user/obs.md @@ -2592,70 +2603,95 @@ msgstr "Diese Funktion kann von den Administratoren der Instanz deaktiviert werd #: support/documentation/content/en/documentation/user/obs.md msgid "You can use OBS \"Custom browser docks\" to integrate the chat in your OBS while you are streaming. The livechat plugin offers a way to create long term token that can identify you automatically to join the chat, so you don't have to enter your password in OBS." msgstr "" +"Sie können OBS \"Benutzerdefinierte Browser-Docks\" verwenden, um den Chat " +"in OBS zu integrieren, während Sie streamen. Das Livechat-Plugin bietet " +"eine Möglichkeit, ein langfristiges Token zu erstellen, das Sie automatisch " +"identifiziert, um dem Chat beizutreten, so dass Sie Ihr Passwort nicht in " +"OBS eingeben müssen." #. type: Plain text #: support/documentation/content/en/documentation/user/obs.md msgid "To do so, just use the \"{{% livechat_label share_chat_link %}}\", and open the \"{{% livechat_label share_chat_dock %}}\" tab. From there, you can create a new token using the \"+\" button." msgstr "" +"Verwenden Sie dazu einfach \"{{% livechat_label share_chat_link %}}\", und " +"öffnen Sie die Registerkarte \"{{% livechat_label share_chat_dock %}}\". " +"Von dort aus können Sie mit der Schaltfläche \"+\" ein neuen Token erstellen." #. type: Plain text #: support/documentation/content/en/documentation/user/obs.md #: support/documentation/content/en/documentation/user/streamers/basics.md -#, fuzzy -#| msgid "![Share link popup](/peertube-plugin-livechat/images/share_readonly.png?classes=shadow,border&height=200px)" msgid "![Share link popup - dock tab](/peertube-plugin-livechat/images/share_dock.png?classes=shadow,border&height=200px)" -msgstr "![Link Teilen Popup](/peertube-plugin-livechat/images/share_readonly.png?classes=shadow,border&height=200px)" +msgstr "" +"![Link teilen Fenster - Dock Reiter](/peertube-plugin-livechat/images/" +"share_dock.png?classes=shadow,border&height=200px)" #. type: Plain text #: support/documentation/content/en/documentation/user/obs.md msgid "Then, copy the url, and use the \"Docks / Custom browser docks\" menu from your OBS to add a dock with this URL." msgstr "" +"Kopieren Sie dann die URL und verwenden Sie das Menü \"Docks / " +"Benutzerdefinierte Browser-Docks\" in Ihrem OBS, um ein Dock mit dieser URL " +"hinzuzufügen." #. type: Plain text #: support/documentation/content/en/documentation/user/obs.md -#, fuzzy -#| msgid "![Chat menu](/peertube-plugin-livechat/images/top_menu.png?classes=shadow,border&height=200px)" msgid "![OBS - Dock menu](/peertube-plugin-livechat/images/obs_dock_menu.png?classes=shadow,border&height=200px)" -msgstr "![Chat Menü](/peertube-plugin-livechat/images/top_menu.png?classes=shadow,border&height=200px)" +msgstr "" +"![OBS - Dock Menü](/peertube-plugin-livechat/images/obs_dock_menu." +"png?classes=shadow,border&height=200px)" #. type: Plain text #: support/documentation/content/en/documentation/user/obs.md -#, fuzzy -#| msgid "![External login dialog](/peertube-plugin-livechat/images/external_login_dialog.png?classes=shadow,border&height=200px)" msgid "![OBS - Dock dialog](/peertube-plugin-livechat/images/obs_dock_dialog.png?classes=shadow,border&height=200px)" -msgstr "![Externer Anmeldedialog](/peertube-plugin-livechat/images/external_login_dialog.png?classes=shadow,border&height=200px)" +msgstr "" +"![OBS - Dock-Dialog](/peertube-plugin-livechat/images/obs_dock_dialog." +"png?classes=shadow,border&height=200px)" #. type: Plain text #: support/documentation/content/en/documentation/user/obs.md msgid "Once you have done, you will have a new dock connected to the chat with your account." msgstr "" +"Danach haben Sie ein neues Dock, das mit dem Chat und Ihrem Konto verbunden " +"ist." #. type: Plain text #: support/documentation/content/en/documentation/user/obs.md -#, fuzzy -#| msgid "![Share button](/peertube-plugin-livechat/images/share_button.png?classes=shadow,border&height=200px)" msgid "![OBS - Dock](/peertube-plugin-livechat/images/obs_dock.png?classes=shadow,border&height=200px)" -msgstr "![Teilen Schaltfläche](/peertube-plugin-livechat/images/share_button.png?classes=shadow,border&height=200px)" +msgstr "" +"![OBS - Dock](/peertube-plugin-livechat/images/obs_dock." +"png?classes=shadow,border&height=200px)" #. type: Plain text #: support/documentation/content/en/documentation/user/obs.md msgid "Tokens are valid to join any chat room. You don't have to generate separate tokens for each of your rooms. You can also customize the nickame that will be used by changing the `n` parameter in the url." msgstr "" +"Die Token sind für die Teilnahme an jedem Chatraum gültig. Sie müssen nicht " +"für jeden Ihrer Räume ein eigenes Token erstellen. Sie können auch den " +"Spitznamen, der verwendet wird, anpassen, indem Sie den Parameter `n` in der " +"URL ändern." #. type: Plain text #: support/documentation/content/en/documentation/user/obs.md msgid "Don't share these links to anyone, as it would allow them to connect as yourself." msgstr "" +"Geben Sie diese Links nicht weiter, da sie es anderen Personen ermöglichen " +"würden, eine Verbindung mit Ihrem Konto herzustellen." #. type: Plain text #: support/documentation/content/en/documentation/user/obs.md msgid "If a token is compromised, or no more needed, you can revoke them." msgstr "" +"Wenn ein Token kompromittiert ist oder nicht mehr benötigt wird, können Sie " +"ihn widerrufen." #. type: Plain text #: support/documentation/content/en/documentation/user/obs.md msgid "These tokens can be used for other purposes, as connecting to your account with XMPP bots or clients. This feature is not documented yet, and not officially supported. So use with care." msgstr "" +"Diese Token können für andere Zwecke verwendet werden, z. B. für die " +"Verbindung mit XMPP-Bots oder -Clients zu Ihrem Konto. Diese Funktion ist " +"noch nicht dokumentiert und wird offiziell nicht unterstützt. Verwenden Sie " +"sie also mit Vorsicht." #. type: Title ## #: support/documentation/content/en/documentation/user/obs.md @@ -2747,6 +2783,8 @@ msgstr "Diese Schaltfläche öffnet ein Popup-Fenster, in dem Sie eine URL erhal #: support/documentation/content/en/documentation/user/streamers/basics.md msgid "The \"{{% livechat_label share_chat_embed %}}\" tab provide some links to embed the chat in websites, or in your live stream." msgstr "" +"Auf der Registerkarte \"{{% livechat_label share_chat_embed %}}\" finden Sie " +"einige Links zum Einbetten des Chats in Websites oder in Ihre Livestream." #. type: Plain text #: support/documentation/content/en/documentation/user/streamers/basics.md @@ -2770,22 +2808,26 @@ msgstr "{{% livechat_label generate_iframe %}}: Anstelle einer URL erhalten Sie #. type: Plain text #: support/documentation/content/en/documentation/user/streamers/basics.md -#, fuzzy -#| msgid "Please refer to the [OBS documentation](/peertube-plugin-livechat/documentation/user/obs)." msgid "For more information on the \"{{% livechat_label share_chat_dock %}}\" tab, check the [OBS documentation](/peertube-plugin-livechat/documentation/user/obs)." -msgstr "Bitte lesen Sie die [OBS-Dokumentation](/peertube-plugin-livechat/de/documentation/user/obs)." +msgstr "" +"Weitere Informationen über die Registerkarte \"{{% livechat_label " +"share_chat_dock %}}\" finden Sie in der [OBS-Dokumentation](/peertube-" +"plugin-livechat/de/documentation/user/obs)." #. type: Plain text #: support/documentation/content/en/documentation/user/streamers/basics.md msgid "In the \"{{% livechat_label web %}}\" tab, the provided url opens the chat in the Peertube interface. You can share this link to other users to invite them to join the chat." msgstr "" +"Auf der Registerkarte \"{{% livechat_label web %}}\" öffnet die angegebene " +"URL den Chat in der Peertube-Oberfläche. Sie können diesen Link an andere " +"Benutzer weitergeben, um sie zum Chat einzuladen." #. type: Plain text #: support/documentation/content/en/documentation/user/streamers/basics.md -#, fuzzy -#| msgid "![Share link popup](/peertube-plugin-livechat/images/share_readonly.png?classes=shadow,border&height=200px)" msgid "![Share link popup - web tab](/peertube-plugin-livechat/images/share_web.png?classes=shadow,border&height=200px)" -msgstr "![Link Teilen Popup](/peertube-plugin-livechat/images/share_readonly.png?classes=shadow,border&height=200px)" +msgstr "" +"![Link teilen Fenster - Web Reiter](/peertube-plugin-livechat/images/" +"share_web.png?classes=shadow,border&height=200px)" #. type: Plain text #: support/documentation/content/en/documentation/user/streamers/basics.md @@ -2794,10 +2836,10 @@ msgstr "Das \"{{% livechat_label share_chat_link %}}\" Popup-Fenster kann auch e #. type: Plain text #: support/documentation/content/en/documentation/user/streamers/basics.md -#, fuzzy -#| msgid "![Share XMPP](/peertube-plugin-livechat/images/share_xmpp_dialog.png?classes=shadow,border&height=200px)" msgid "![Share link popup - xmpp tab](/peertube-plugin-livechat/images/share_xmpp_dialog.png?classes=shadow,border&height=200px)" -msgstr "![XMPP Link teilen](/peertube-plugin-livechat/images/share_xmpp_dialog.png?classes=shadow,border&height=200px)" +msgstr "" +"![Link teilen Fenster - xmpp Reiter](/peertube-plugin-livechat/images/" +"share_xmpp_dialog.png?classes=shadow,border&height=200px)" #. type: Title ## #: support/documentation/content/en/documentation/user/streamers/basics.md