diff --git a/CHANGELOG.md b/CHANGELOG.md
index 13144054..6e071119 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,8 @@ TODO: https://github.com/JohnXLivingston/peertube-plugin-livechat/issues/48
* #143: User colors: implementing [XEP-0392](https://xmpp.org/extensions/xep-0392.html) to have random colors on users nicknames
* #330: Chat does no more use an iframe to display the chat besides the videos.
* #330: Fullscreen chat: now uses a custom page (in other words: when opening the chat in a new tab, you will have the Peertube menu).
+* For anonymous users: new "log in using an external account" dialog, with following options:
+ * remote Peertube account
### Minor changes and fixes
diff --git a/conversejs/build-conversejs-patch-i18n.js b/conversejs/build-conversejs-patch-i18n.js
index eb19020c..ec2325d5 100644
--- a/conversejs/build-conversejs-patch-i18n.js
+++ b/conversejs/build-conversejs-patch-i18n.js
@@ -2,6 +2,7 @@
const fs = require('node:fs')
const path = require('node:path')
const YAML = require('yaml')
+const locKeys = require('./loc.keys.js')
/**
* This script will patch ConverseJS .po files, to add custom strings.
@@ -11,9 +12,7 @@ const livechatDir = path.resolve(__dirname, '..', 'languages')
const converseDir = path.resolve(__dirname, '..', 'build', 'conversejs', 'src', 'i18n')
// Labels to import:
-const labels = loadLabels([
- 'slow_mode_info'
-])
+const labels = loadLabels(locKeys)
function loadLabels (keys) {
const labels = {}
diff --git a/conversejs/build-conversejs.sh b/conversejs/build-conversejs.sh
index a1385470..74504f5a 100644
--- a/conversejs/build-conversejs.sh
+++ b/conversejs/build-conversejs.sh
@@ -79,6 +79,7 @@ rm -rf "$converse_build_dir/custom/"
echo "Adding the custom files..."
cp -r "$src_dir/custom/" "$converse_build_dir/custom/"
mv "$converse_build_dir/custom/webpack.livechat.js" "$converse_build_dir/"
+cp "$src_dir/loc.keys.js" "$converse_build_dir/"
echo "Patching i18n files to add custom labels..."
/bin/env node conversejs/build-conversejs-patch-i18n.js
diff --git a/conversejs/custom/livechat-external-login-content.js b/conversejs/custom/livechat-external-login-content.js
new file mode 100644
index 00000000..481dca66
--- /dev/null
+++ b/conversejs/custom/livechat-external-login-content.js
@@ -0,0 +1,117 @@
+import { api } from '@converse/headless/core.js'
+import { CustomElement } from 'shared/components/element.js'
+import { tplExternalLoginModal } from 'templates/livechat-external-login-modal.js'
+import { __ } from 'i18n'
+
+export default class LivechatExternalLoginContentElement extends CustomElement {
+ static get properties () {
+ return {
+ remote_peertube_state: { type: String, attribute: false },
+ remote_peertube_alert_message: { type: String, attribute: false },
+ remote_peertube_try_anyway_url: { type: String, attribute: false }
+ }
+ }
+
+ constructor () {
+ super()
+ this.remote_peertube_state = 'init'
+ }
+
+ render () {
+ return tplExternalLoginModal(this, {
+ remote_peertube_state: this.remote_peertube_state,
+ remote_peertube_alert_message: this.remote_peertube_alert_message,
+ remote_peertube_try_anyway_url: this.remote_peertube_try_anyway_url
+ })
+ }
+
+ onKeyUp (_ev) {
+ if (this.remote_peertube_state !== 'init') {
+ this.remote_peertube_state = 'init'
+ this.remote_peertube_alert_message = ''
+ this.clearAlert()
+ }
+ }
+
+ async openRemotePeertube (ev) {
+ ev.preventDefault()
+ this.clearAlert()
+
+ const remotePeertubeUrl = ev.target.peertube_url.value.trim()
+ if (!remotePeertubeUrl) { return }
+
+ this.remote_peertube_state = 'loading'
+
+ try {
+ // Calling Peertube API to check if livechat plugin is available.
+ // In the meantime, this will also check that the URL exists, and is a Peertube instance
+ // (or something with similar API result... as the user typed the url, we assume there is no security risk here).
+ const configApiUrl = new URL('/api/v1/config', remotePeertubeUrl)
+ const config = await (await fetch(configApiUrl.toString())).json()
+ if (!config || typeof config !== 'object') {
+ throw new Error('Invalid config API result')
+ }
+ if (!('plugin' in config) || !('registered' in config.plugin) || !Array.isArray(config.plugin.registered)) {
+ throw new Error('No registered plugin in config API result')
+ }
+ if (!config.plugin.registered.find(p => p.npmName === 'peertube-plugin-livechat')) {
+ console.error('Plugin livechat not available on remote instance')
+ this.remote_peertube_state = 'error'
+ // eslint-disable-next-line no-undef
+ this.remote_peertube_alert_message = __(LOC_login_remote_peertube_no_livechat)
+ return
+ }
+ // Note: we do not check if the livechat plugin disables federation (neither on current or remote instance).
+ // We assume this is not a standard use case, and we don't want to add to much use cases.
+
+ // Now we must search the current video on the remote instance, to be sure it federates, and to get the url.
+ // Note: url search can be disabled on remote instance for non logged in users...
+ // As we are not authenticated on remote here, there are chances that the search wont return anything.
+ // As a fallback, we will launch another search with the video UUID.
+ // And if no result neither, we will just propose to open using the lazy-load page.
+ const videoUrl = api.settings.get('livechat_peertube_video_original_url')
+ const videoUUID = api.settings.get('livechat_peertube_video_uuid')
+ for (const search of [videoUrl, videoUUID]) {
+ if (!search) { continue }
+ // searching first on federation network, then on vidiverse (this could be disabled)
+ for (const searchTarget of ['local', 'search-index']) {
+ const searchAPIUrl = new URL('/api/v1/search/videos', remotePeertubeUrl)
+ searchAPIUrl.searchParams.append('start', '0')
+ searchAPIUrl.searchParams.append('count', 1)
+ searchAPIUrl.searchParams.append('search', search)
+ searchAPIUrl.searchParams.append('searchTarget', searchTarget)
+ const videos = await (await fetch(searchAPIUrl.toString())).json()
+ if (videos && Array.isArray(videos.data) && videos.data.length > 0 && videos.data[0].uuid) {
+ console.log('Video found, opening on remote instance')
+ this.remote_peertube_state = 'ok'
+ window.location.href = new URL(
+ '/videos/watch/' + encodeURIComponent(videos.data[0].uuid), remotePeertubeUrl
+ ).toString()
+ return
+ }
+ }
+ }
+
+ console.error('Video not found on remote instance')
+ this.remote_peertube_state = 'error'
+ // eslint-disable-next-line no-undef
+ this.remote_peertube_alert_message = __(LOC_login_remote_peertube_video_not_found)
+ this.remote_peertube_try_anyway_url = new URL(
+ '/search/lazy-load-video;url=' + encodeURIComponent(videoUrl),
+ remotePeertubeUrl
+ ).toString()
+ } catch (err) {
+ console.error(err)
+ this.remote_peertube_state = 'error'
+ // eslint-disable-next-line no-undef
+ this.remote_peertube_alert_message = __(LOC_login_remote_peertube_url_invalid)
+ }
+ }
+
+ clearAlert () {
+ this.remote_peertube_alert_message = ''
+ this.remote_peertube_try_anyway_url = ''
+ }
+}
+
+api.elements.define('converse-livechat-external-login-content', LivechatExternalLoginContentElement)
diff --git a/conversejs/custom/shared/modals/livechat-external-login.js b/conversejs/custom/shared/modals/livechat-external-login.js
new file mode 100644
index 00000000..a583e9b3
--- /dev/null
+++ b/conversejs/custom/shared/modals/livechat-external-login.js
@@ -0,0 +1,20 @@
+import { __ } from 'i18n'
+import BaseModal from 'plugins/modal/modal.js'
+import { api } from '@converse/headless/core'
+import { html } from 'lit'
+import 'livechat-external-login-content.js'
+
+class ExternalLoginModal extends BaseModal {
+ remotePeertubeError = ''
+
+ renderModal () {
+ return html`