Autocolors
WIP on a new feature: trying to guess current Peertube theme's colors, and apply them to ConverseJS.
This commit is contained in:
parent
fbb2e8345c
commit
8999133dcc
@ -5,10 +5,12 @@
|
|||||||
### Features
|
### Features
|
||||||
|
|
||||||
* UI/UX improvements. Now using a custom ConverseJS build.
|
* UI/UX improvements. Now using a custom ConverseJS build.
|
||||||
|
* Auto color detection: when using ConverseJS, the plugin tries to guess colors to apply to its theme.
|
||||||
|
|
||||||
### Breaking changes
|
### Breaking changes
|
||||||
|
|
||||||
* If you have some CSS customization for the plugin, it may be broken.
|
* If you have some CSS customization for the plugin, it may be broken.
|
||||||
|
* Auto color detection can have bad result for some Peertube theme. If so, you can disable it in the settings.
|
||||||
|
|
||||||
## v4.0.3
|
## v4.0.3
|
||||||
|
|
||||||
|
@ -227,6 +227,14 @@ function register ({ registerHook, registerSettingsScript, peertubeHelpers }: Re
|
|||||||
options.formValues['chat-type'] === ('builtin-converse' as ChatType) ||
|
options.formValues['chat-type'] === ('builtin-converse' as ChatType) ||
|
||||||
options.formValues['chat-type'] === ('builtin-prosody' as ChatType)
|
options.formValues['chat-type'] === ('builtin-prosody' as ChatType)
|
||||||
)
|
)
|
||||||
|
case 'converse-autocolors':
|
||||||
|
return !(
|
||||||
|
(
|
||||||
|
options.formValues['chat-type'] === ('builtin-converse' as ChatType) ||
|
||||||
|
options.formValues['chat-type'] === ('builtin-prosody' as ChatType)
|
||||||
|
) &&
|
||||||
|
options.formValues['converse-theme'] === 'peertube'
|
||||||
|
)
|
||||||
case 'chat-uri':
|
case 'chat-uri':
|
||||||
case 'chat-type-help-external-uri':
|
case 'chat-type-help-external-uri':
|
||||||
return options.formValues['chat-type'] !== ('external-uri' as ChatType)
|
return options.formValues['chat-type'] !== ('external-uri' as ChatType)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { videoHasWebchat } from 'shared/lib/video'
|
|
||||||
import type { ChatType } from 'shared/lib/types'
|
import type { ChatType } from 'shared/lib/types'
|
||||||
|
import { videoHasWebchat } from 'shared/lib/video'
|
||||||
|
import { AutoColors, isAutoColorsAvailable, areAutoColorsValid } from 'shared/lib/autocolors'
|
||||||
|
|
||||||
interface VideoWatchLoadedHookOptions {
|
interface VideoWatchLoadedHookOptions {
|
||||||
videojs: any
|
videojs: any
|
||||||
@ -60,9 +61,78 @@ function register ({ registerHook, peertubeHelpers }: RegisterOptions): void {
|
|||||||
logger.error('No iframe uri')
|
logger.error('No iframe uri')
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isAutoColorsAvailable(settings['chat-type'] as ChatType, settings['converse-theme'])) {
|
||||||
|
logger.info('We have to try to compute autocolors.')
|
||||||
|
try {
|
||||||
|
const autocolors = computeAutoColors()
|
||||||
|
if (autocolors) {
|
||||||
|
const url = new URL(iframeUri, window.location.origin)
|
||||||
|
for (const p in autocolors) {
|
||||||
|
url.searchParams.set('_ac_' + p, autocolors[p as keyof AutoColors])
|
||||||
|
}
|
||||||
|
iframeUri = url.href
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(`Failed computing autocolors: '${err as string}'`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return iframeUri
|
return iframeUri
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function computeAutoColors (): AutoColors | null {
|
||||||
|
if (!window.getComputedStyle) {
|
||||||
|
logger.warn('[AutoColors] getComputedStyle is not available, aborting.')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
// Searching for one of these button:
|
||||||
|
const button = document.querySelector('.publish-button, .peertube-button-link')
|
||||||
|
if (!button) {
|
||||||
|
logger.warn('[AutoColors] Cant find a button, aborting.')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const buttonStyles = window.getComputedStyle(button)
|
||||||
|
logger.info('[AutoColors] found button styles')
|
||||||
|
|
||||||
|
const main = document.querySelector('#content')
|
||||||
|
if (!main) {
|
||||||
|
logger.warn('[AutoColors] Cant find main, aborting.')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const mainStyles = window.getComputedStyle(main)
|
||||||
|
logger.info('[AutoColors] found main styles')
|
||||||
|
|
||||||
|
const menu = document.querySelector('.menu-link')
|
||||||
|
if (!menu) {
|
||||||
|
logger.warn('[AutoColors] Cant find menu, aborting.')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const menuStyles = window.getComputedStyle(menu)
|
||||||
|
logger.info('[AutoColors] found menu styles')
|
||||||
|
|
||||||
|
const autocolors: AutoColors = {
|
||||||
|
mainForeground: mainStyles.color,
|
||||||
|
mainBackground: mainStyles.backgroundColor,
|
||||||
|
greyForeground: '#000',
|
||||||
|
greyBackground: '#000',
|
||||||
|
menuForeground: menuStyles.color,
|
||||||
|
menuBackground: menuStyles.backgroundColor,
|
||||||
|
inputForeground: '#000',
|
||||||
|
inputBackground: '#000',
|
||||||
|
buttonForeground: buttonStyles.color,
|
||||||
|
buttonBackground: buttonStyles.backgroundColor,
|
||||||
|
link: '#000',
|
||||||
|
linkHover: '#000'
|
||||||
|
}
|
||||||
|
const autoColorsTest = areAutoColorsValid(autocolors)
|
||||||
|
if (autoColorsTest !== true) {
|
||||||
|
logger.warn('[AutoColors] Computed colors are not valid, dropping. Invalid values: ' + autoColorsTest.join(', '))
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return autocolors
|
||||||
|
}
|
||||||
|
|
||||||
function displayButton (
|
function displayButton (
|
||||||
buttonContainer: HTMLElement,
|
buttonContainer: HTMLElement,
|
||||||
name: string,
|
name: string,
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
<link type="text/css" rel="stylesheet" media="screen" href="{{BASE_STATIC_URL}}conversejs/converse.min.css" />
|
<link type="text/css" rel="stylesheet" media="screen" href="{{BASE_STATIC_URL}}conversejs/converse.min.css" />
|
||||||
<script type="text/javaScript" src="{{BASE_STATIC_URL}}conversejs/converse.min.js"></script>
|
<script type="text/javaScript" src="{{BASE_STATIC_URL}}conversejs/converse.min.js"></script>
|
||||||
<script type="text/javascript" src="{{BASE_STATIC_URL}}static/builtin.js"></script>
|
<script type="text/javascript" src="{{BASE_STATIC_URL}}static/builtin.js"></script>
|
||||||
|
{{CONVERSEJS_AUTOCOLORS}}
|
||||||
</head>
|
</head>
|
||||||
<body class="converse-fullscreen theme-peertube">
|
<body class="converse-fullscreen theme-peertube">
|
||||||
<noscript>You need to enable JavaScript to run the Converse.js chat app.</noscript>
|
<noscript>You need to enable JavaScript to run the Converse.js chat app.</noscript>
|
||||||
|
@ -8,6 +8,7 @@ import { asyncMiddleware } from '../middlewares/async'
|
|||||||
import { getProsodyDomain } from '../prosody/config/domain'
|
import { getProsodyDomain } from '../prosody/config/domain'
|
||||||
import { getAPIKey } from '../apikey'
|
import { getAPIKey } from '../apikey'
|
||||||
import { getChannelInfosById, getChannelNameById } from '../database/channel'
|
import { getChannelInfosById, getChannelNameById } from '../database/channel'
|
||||||
|
import { isAutoColorsAvailable, areAutoColorsValid, AutoColors } from '../../../shared/lib/autocolors'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
const bodyParser = require('body-parser')
|
const bodyParser = require('body-parser')
|
||||||
const got = require('got')
|
const got = require('got')
|
||||||
@ -42,7 +43,7 @@ async function initWebchatRouter (options: RegisterServerOptions): Promise<Route
|
|||||||
'chat-type', 'chat-room', 'chat-server',
|
'chat-type', 'chat-room', 'chat-server',
|
||||||
'chat-bosh-uri', 'chat-ws-uri',
|
'chat-bosh-uri', 'chat-ws-uri',
|
||||||
'prosody-room-type',
|
'prosody-room-type',
|
||||||
'converse-theme'
|
'converse-theme', 'converse-autocolors'
|
||||||
])
|
])
|
||||||
const chatType: ChatType = (settings['chat-type'] ?? 'disabled') as ChatType
|
const chatType: ChatType = (settings['chat-type'] ?? 'disabled') as ChatType
|
||||||
|
|
||||||
@ -149,6 +150,53 @@ async function initWebchatRouter (options: RegisterServerOptions): Promise<Route
|
|||||||
}
|
}
|
||||||
room = room.replace(/{{CHANNEL_NAME}}/g, channelName)
|
room = room.replace(/{{CHANNEL_NAME}}/g, channelName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let autocolorsStyles = ''
|
||||||
|
if (
|
||||||
|
settings['converse-autocolors'] &&
|
||||||
|
isAutoColorsAvailable(settings['chat-type'] as ChatType, settings['converse-theme'] as string)
|
||||||
|
) {
|
||||||
|
peertubeHelpers.logger.debug('Trying to load AutoColors...')
|
||||||
|
const autocolors: AutoColors = {
|
||||||
|
mainForeground: req.query._ac_mainForeground?.toString() ?? '',
|
||||||
|
mainBackground: req.query._ac_mainBackground?.toString() ?? '',
|
||||||
|
greyForeground: req.query._ac_greyForeground?.toString() ?? '',
|
||||||
|
greyBackground: req.query._ac_greyBackground?.toString() ?? '',
|
||||||
|
menuForeground: req.query._ac_menuForeground?.toString() ?? '',
|
||||||
|
menuBackground: req.query._ac_menuBackground?.toString() ?? '',
|
||||||
|
inputForeground: req.query._ac_inputForeground?.toString() ?? '',
|
||||||
|
inputBackground: req.query._ac_inputBackground?.toString() ?? '',
|
||||||
|
buttonForeground: req.query._ac_buttonForeground?.toString() ?? '',
|
||||||
|
buttonBackground: req.query._ac_buttonBackground?.toString() ?? '',
|
||||||
|
link: req.query._ac_link?.toString() ?? '',
|
||||||
|
linkHover: req.query._ac_linkHover?.toString() ?? ''
|
||||||
|
}
|
||||||
|
const autoColorsTest = areAutoColorsValid(autocolors)
|
||||||
|
if (autoColorsTest === true) {
|
||||||
|
autocolorsStyles = `
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--peertube-main-foreground: ${autocolors.mainForeground};
|
||||||
|
--peertube-main-background: ${autocolors.mainBackground};
|
||||||
|
--peertube-grey-foreground: ${autocolors.greyForeground};
|
||||||
|
--peertube-grey-background: ${autocolors.greyBackground};
|
||||||
|
--peertube-menu-foreground: ${autocolors.menuForeground};
|
||||||
|
--peertube-menu-background: ${autocolors.menuBackground};
|
||||||
|
--peertube-input-foreground: ${autocolors.inputForeground};
|
||||||
|
--peertube-input-background: ${autocolors.inputBackground};
|
||||||
|
--peertube-button-foreground: ${autocolors.buttonForeground};
|
||||||
|
--peertube-button-background: ${autocolors.buttonBackground};
|
||||||
|
--peertube-link: ${autocolors.link};
|
||||||
|
--peertube-link-hover: ${autocolors.linkHover};
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
`
|
||||||
|
} else {
|
||||||
|
peertubeHelpers.logger.error('Provided AutoColors are invalid.', autoColorsTest)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
peertubeHelpers.logger.debug('No AutoColors.')
|
||||||
|
}
|
||||||
// ... then inject it in the page.
|
// ... then inject it in the page.
|
||||||
page = page.replace(/{{ROOM}}/g, room)
|
page = page.replace(/{{ROOM}}/g, room)
|
||||||
page = page.replace(/{{BOSH_SERVICE_URL}}/g, boshUri)
|
page = page.replace(/{{BOSH_SERVICE_URL}}/g, boshUri)
|
||||||
@ -156,6 +204,7 @@ async function initWebchatRouter (options: RegisterServerOptions): Promise<Route
|
|||||||
page = page.replace(/{{AUTHENTICATION_URL}}/g, authenticationUrl)
|
page = page.replace(/{{AUTHENTICATION_URL}}/g, authenticationUrl)
|
||||||
page = page.replace(/{{ADVANCEDCONTROLS}}/g, advancedControls ? 'true' : 'false')
|
page = page.replace(/{{ADVANCEDCONTROLS}}/g, advancedControls ? 'true' : 'false')
|
||||||
page = page.replace(/{{CONVERSEJS_THEME}}/g, converseJSTheme)
|
page = page.replace(/{{CONVERSEJS_THEME}}/g, converseJSTheme)
|
||||||
|
page = page.replace(/{{CONVERSEJS_AUTOCOLORS}}/g, autocolorsStyles)
|
||||||
|
|
||||||
res.status(200)
|
res.status(200)
|
||||||
res.type('html')
|
res.type('html')
|
||||||
|
@ -282,7 +282,7 @@ Example: height:400px;`,
|
|||||||
label: 'ConverseJS theme',
|
label: 'ConverseJS theme',
|
||||||
type: 'select',
|
type: 'select',
|
||||||
default: 'peertube' as ConverseJSTheme,
|
default: 'peertube' as ConverseJSTheme,
|
||||||
private: true,
|
private: false,
|
||||||
options: [
|
options: [
|
||||||
{ value: 'peertube', label: 'Peertube theme' },
|
{ value: 'peertube', label: 'Peertube theme' },
|
||||||
{ value: 'default', label: 'Default ConverseJS theme' },
|
{ value: 'default', label: 'Default ConverseJS theme' },
|
||||||
@ -291,6 +291,22 @@ Example: height:400px;`,
|
|||||||
descriptionHTML: 'Please choose the converseJS theme you want to use.'
|
descriptionHTML: 'Please choose the converseJS theme you want to use.'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
registerSetting({
|
||||||
|
name: 'converse-autocolors',
|
||||||
|
label: 'Automatic color detection',
|
||||||
|
type: 'input-checkbox',
|
||||||
|
default: true,
|
||||||
|
private: false,
|
||||||
|
descriptionHTML:
|
||||||
|
`Try to auto detect colors from user's current theme.<br>
|
||||||
|
When this settings is enabled, the plugin tries to auto-detect colors to apply to the chat theme.<br>
|
||||||
|
If this is not correctly working for some of your Peertube theme, you can disable this option.
|
||||||
|
You can report the bug on the official
|
||||||
|
<a href="https://github.com/JohnXLivingston/peertube-plugin-livechat/issues" target="_blank">
|
||||||
|
issue tracker
|
||||||
|
</a>. Don't forget to specify which theme is not working.`
|
||||||
|
})
|
||||||
|
|
||||||
// ********** Built-in Prosody advanced settings
|
// ********** Built-in Prosody advanced settings
|
||||||
registerSetting({
|
registerSetting({
|
||||||
name: 'prosody-advanced',
|
name: 'prosody-advanced',
|
||||||
|
59
shared/lib/autocolors.ts
Normal file
59
shared/lib/autocolors.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import type { ChatType } from './types'
|
||||||
|
|
||||||
|
type AutoColorValue = string
|
||||||
|
|
||||||
|
interface AutoColors {
|
||||||
|
mainForeground: AutoColorValue
|
||||||
|
mainBackground: AutoColorValue
|
||||||
|
greyForeground: AutoColorValue
|
||||||
|
greyBackground: AutoColorValue
|
||||||
|
menuForeground: AutoColorValue
|
||||||
|
menuBackground: AutoColorValue
|
||||||
|
inputForeground: AutoColorValue
|
||||||
|
inputBackground: AutoColorValue
|
||||||
|
buttonForeground: AutoColorValue
|
||||||
|
buttonBackground: AutoColorValue
|
||||||
|
link: AutoColorValue
|
||||||
|
linkHover: AutoColorValue
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param chatType value of the settings 'chat-type'
|
||||||
|
* @param theme value of the settings 'converse-theme'
|
||||||
|
* @returns true if the theme can use autocolors
|
||||||
|
*/
|
||||||
|
function isAutoColorsAvailable (chatType: ChatType, theme: string): boolean {
|
||||||
|
if (chatType !== 'builtin-prosody' && chatType !== 'builtin-converse') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return theme === 'peertube' // currently the only theme that can handle autocolors.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param autocolors
|
||||||
|
* @returns true if ok. Else a string array with invalid values.
|
||||||
|
*/
|
||||||
|
function areAutoColorsValid (autocolors: AutoColors): true | string[] {
|
||||||
|
const errors: string[] = []
|
||||||
|
for (const k in autocolors) {
|
||||||
|
const color = autocolors[k as keyof AutoColors]
|
||||||
|
// FIXME: there are missing cases. For now there are only basic values formats.
|
||||||
|
if (
|
||||||
|
!/^rgb\(\d{1,3},\s*\d{1,3},\s*\d{1,3}\)$/.test(color) &&
|
||||||
|
!/^rgba\(\d{1,3},\s*\d{1,3},\s*\d{1,3},\s*(1|0|0?.\d+)\)$/.test(color) &&
|
||||||
|
!/^#[0-9a-fA-F]{3,6}$/.test(color)
|
||||||
|
) {
|
||||||
|
errors.push(color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (errors.length) {
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
AutoColors,
|
||||||
|
isAutoColorsAvailable,
|
||||||
|
areAutoColorsValid
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user