Log in with external Peertube account (#348):
* For anonymous users: new "log in using an external account" dialog, with the "remote Peertube account" options * ConverseJS: using global vars for custom localized string (injected using Webpack)
This commit is contained in:
117
conversejs/custom/livechat-external-login-content.js
Normal file
117
conversejs/custom/livechat-external-login-content.js
Normal file
@ -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)
|
20
conversejs/custom/shared/modals/livechat-external-login.js
Normal file
20
conversejs/custom/shared/modals/livechat-external-login.js
Normal file
@ -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`<converse-livechat-external-login-content></converse-livechat-external-login-content>`
|
||||
}
|
||||
|
||||
getModalTitle () {
|
||||
// eslint-disable-next-line no-undef
|
||||
return __(LOC_login_using_external_account)
|
||||
}
|
||||
}
|
||||
|
||||
api.elements.define('converse-livechat-external-login', ExternalLoginModal)
|
@ -43,30 +43,39 @@ body.livechat-readonly.livechat-noscroll {
|
||||
}
|
||||
|
||||
// Viewer mode
|
||||
.livechat-viewer-mode-nick {
|
||||
.livechat-viewer-mode-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body[livechat-viewer-mode="on"] {
|
||||
.livechat-viewer-mode-nick {
|
||||
display: initial;
|
||||
form {
|
||||
display: flex !important;
|
||||
flex-flow: row wrap !important;
|
||||
padding-bottom: 0.5em !important;
|
||||
border-top: 1px solid var(--chatroom-head-bg-color) !important;
|
||||
gap: 10px;
|
||||
align-items: baseline;
|
||||
|
||||
form {
|
||||
display: flex !important;
|
||||
flex-flow: row wrap !important;
|
||||
padding-bottom: 0.5em !important;
|
||||
border-top: var(--chatroom-separator-border-bottom) !important;
|
||||
gap: 10px;
|
||||
align-items: baseline;
|
||||
|
||||
label {
|
||||
color: var(--text-color); // fix converseJs css that breaks this label color.
|
||||
}
|
||||
label {
|
||||
color: var(--text-color); // fix converseJs css that breaks this label color.
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 0;
|
||||
background-color: var(--chatroom-head-bg-color);
|
||||
}
|
||||
|
||||
.livechat-viewer-mode-external-login {
|
||||
padding: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
body[livechat-viewer-mode="on"] {
|
||||
.livechat-viewer-mode-content {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
converse-muc-bottom-panel {
|
||||
>:not(.livechat-viewer-mode-nick) {
|
||||
>:not(.livechat-viewer-mode-content) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
56
conversejs/custom/templates/livechat-external-login-modal.js
Normal file
56
conversejs/custom/templates/livechat-external-login-modal.js
Normal file
@ -0,0 +1,56 @@
|
||||
import { __ } from 'i18n'
|
||||
import { html } from 'lit'
|
||||
|
||||
export const tplExternalLoginModal = (el, o) => {
|
||||
// eslint-disable-next-line no-undef
|
||||
const i18nRemotePeertube = __(LOC_login_remote_peertube)
|
||||
// eslint-disable-next-line no-undef
|
||||
const i18nRemotePeertubeUrl = __(LOC_login_remote_peertube_url)
|
||||
const i18nRemotePeertubeOpen = __('OK')
|
||||
return html`<div class="modal-body livechat-external-login-modal">
|
||||
<form class="converse-form chatroom-form" @submit=${(ev) => el.openRemotePeertube(ev)}>
|
||||
<label>
|
||||
${i18nRemotePeertube}
|
||||
<input
|
||||
type="url"
|
||||
placeholder="${i18nRemotePeertubeUrl}"
|
||||
class="form-control ${o.remote_peertube_alert_message ? 'is-invalid' : ''}"
|
||||
name="peertube_url"
|
||||
?disabled=${o.remote_peertube_state === 'loading'}
|
||||
/>
|
||||
</label>
|
||||
<input
|
||||
type="submit"
|
||||
class="btn btn-primary"
|
||||
value="${i18nRemotePeertubeOpen}"
|
||||
@keyup=${el.onKeyUp}
|
||||
?disabled=${o.remote_peertube_state === 'loading'}
|
||||
/>
|
||||
${
|
||||
o.remote_peertube_state !== 'loading'
|
||||
? ''
|
||||
: html`<small class="form-text text-muted">${
|
||||
// eslint-disable-next-line no-undef
|
||||
__(LOC_login_remote_peertube_searching)
|
||||
}</small>`
|
||||
}
|
||||
${!o.remote_peertube_alert_message
|
||||
? ''
|
||||
: html`<div class="invalid-feedback d-block">${o.remote_peertube_alert_message}</div>`
|
||||
}
|
||||
${!o.remote_peertube_try_anyway_url
|
||||
? ''
|
||||
: html`<div class="form-text">
|
||||
${
|
||||
// eslint-disable-next-line no-undef
|
||||
__(LOC_login_remote_peertube_video_not_found_try_anyway)
|
||||
}
|
||||
<button class="btn btn-primary" onclick="window.location.href='${o.remote_peertube_try_anyway_url}'">${
|
||||
// eslint-disable-next-line no-undef
|
||||
__(LOC_login_remote_peertube_video_not_found_try_anyway_button)
|
||||
}</button>
|
||||
</div>`
|
||||
}
|
||||
</fieldset>
|
||||
</form></div>`
|
||||
}
|
@ -3,6 +3,7 @@ import { _converse, api } from '@converse/headless/core'
|
||||
import { html } from 'lit'
|
||||
import tplMucBottomPanel from '../../src/plugins/muc-views/templates/muc-bottom-panel.js'
|
||||
import { CustomElement } from 'shared/components/element.js'
|
||||
import 'shared/modals/livechat-external-login.js'
|
||||
|
||||
async function setNickname (ev, model) {
|
||||
ev.preventDefault()
|
||||
@ -54,7 +55,8 @@ class SlowMode extends CustomElement {
|
||||
return html`<div class="livechat-slow-mode-info-box">
|
||||
<converse-icon class="fa fa-info-circle" size="1.2em"></converse-icon>
|
||||
${__(
|
||||
'Slow mode is enabled, users can send a message every %1$s seconds.',
|
||||
// eslint-disable-next-line no-undef
|
||||
LOC_slow_mode_info,
|
||||
this.model.config.get('slow_mode_duration')
|
||||
)}
|
||||
<i class="livechat-hide-slow-mode-info-box" @click=${this.closeSlowModeInfoBox}>
|
||||
@ -82,14 +84,15 @@ export default (o) => {
|
||||
const i18nNickname = __('Nickname')
|
||||
const i18nJoin = __('Enter groupchat')
|
||||
const i18nHeading = __('Choose a nickname to enter')
|
||||
// eslint-disable-next-line no-undef
|
||||
const i18nExternalLogin = __(LOC_login_using_external_account)
|
||||
return html`
|
||||
<div class="livechat-viewer-mode-nick chatroom-form-container"
|
||||
@submit=${ev => setNickname(ev, model)}>
|
||||
<form class="converse-form chatroom-form">
|
||||
<div class="livechat-viewer-mode-content chatroom-form-container">
|
||||
<form class="converse-form chatroom-form" @submit=${ev => setNickname(ev, model)}>
|
||||
<label>${i18nHeading}</label>
|
||||
<fieldset class="form-group">
|
||||
<input type="text"
|
||||
required="required"
|
||||
required
|
||||
name="nick"
|
||||
value=""
|
||||
class="form-control"
|
||||
@ -99,6 +102,21 @@ export default (o) => {
|
||||
<input type="submit" class="btn btn-primary" name="join" value="${i18nJoin}"/>
|
||||
</fieldset>
|
||||
</form>
|
||||
${
|
||||
// If we open a room with forcetype, there is no current video... So just disabling external login
|
||||
// (in such case, we should be logged in as admin/moderator...)
|
||||
!api.settings.get('livechat_peertube_video_original_url')
|
||||
? ''
|
||||
: html`
|
||||
<hr>
|
||||
<div class="livechat-viewer-mode-external-login">
|
||||
<button class="btn btn-primary" @click=${ev => {
|
||||
ev.preventDefault()
|
||||
api.modal.show('converse-livechat-external-login')
|
||||
}}>${i18nExternalLogin}</button>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
</div>
|
||||
${tplSlowMode(o)}
|
||||
${tplMucBottomPanel(o)}`
|
||||
|
@ -1,18 +1,54 @@
|
||||
const prod = require('./webpack/webpack.build.js')
|
||||
const { merge } = require('webpack-merge')
|
||||
const webpack = require('webpack')
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const locKeys = require('./loc.keys.js')
|
||||
|
||||
function loadLocs () {
|
||||
// Loading english strings, so we can inject them as constants.
|
||||
const refFile = path.resolve(__dirname, '..', '..', 'dist', 'languages', 'en.reference.json')
|
||||
if (!fs.existsSync(refFile)) {
|
||||
throw new Error('Missing english reference file, please run "npm run build:languages" before building ConverseJS')
|
||||
}
|
||||
const english = require(refFile)
|
||||
|
||||
const r = {}
|
||||
for (const key of locKeys) {
|
||||
if (!(key in english) || (typeof english[key] !== 'string')) {
|
||||
throw new Error('Missing english string key=' + key)
|
||||
}
|
||||
r['LOC_' + key] = JSON.stringify(english[key])
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
module.exports = merge(prod, {
|
||||
entry: path.resolve(__dirname, 'custom/entry.js'),
|
||||
output: {
|
||||
filename: 'converse.min.js'
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin(loadLocs())
|
||||
],
|
||||
resolve: {
|
||||
extensions: ['.js'],
|
||||
alias: {
|
||||
'./templates/muc-bottom-panel.js': path.resolve('custom/templates/muc-bottom-panel.js'),
|
||||
'../../templates/background_logo.js$': path.resolve(__dirname, 'custom/templates/background_logo.js'),
|
||||
'shared/styles/index.scss$': path.resolve(__dirname, 'custom/shared/styles/livechat.scss')
|
||||
'shared/styles/index.scss$': path.resolve(__dirname, 'custom/shared/styles/livechat.scss'),
|
||||
'shared/modals/livechat-external-login.js': path.resolve(
|
||||
__dirname,
|
||||
'custom/shared/modals/livechat-external-login.js'
|
||||
),
|
||||
'templates/livechat-external-login-modal.js': path.resolve(
|
||||
__dirname,
|
||||
'custom/templates/livechat-external-login-modal.js'
|
||||
),
|
||||
'livechat-external-login-content.js': path.resolve(
|
||||
__dirname,
|
||||
'custom/livechat-external-login-content.js'
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
Reference in New Issue
Block a user