Changing defaults MUC affiliation (#385):
* For Peertube moderators/admins, we add a button "Promote". Clicking on it will promote them as MUC owner.
This commit is contained in:
2
client/@types/global.d.ts
vendored
2
client/@types/global.d.ts
vendored
@ -77,3 +77,5 @@ declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FOR_MORE_INFO: string
|
||||
|
||||
declare const LOC_INVALID_VALUE: string
|
||||
declare const LOC_CHATROOM_NOT_ACCESSIBLE: string
|
||||
|
||||
declare const LOC_PROMOTE: string
|
||||
|
@ -121,6 +121,7 @@ function register (clientOptions: RegisterClientOptions): void {
|
||||
titleChannelConfiguration.textContent = labels.channelConfiguration
|
||||
titleLineEl.append(titleChannelConfiguration)
|
||||
}
|
||||
titleLineEl.append(document.createElement('th'))
|
||||
table.append(titleLineEl)
|
||||
rooms.forEach(room => {
|
||||
const localpart = room.localpart
|
||||
@ -137,6 +138,35 @@ function register (clientOptions: RegisterClientOptions): void {
|
||||
const date = new Date(room.lasttimestamp * 1000)
|
||||
lastActivityEl.textContent = date.toLocaleDateString() + ' ' + date.toLocaleTimeString()
|
||||
}
|
||||
const promoteButton = document.createElement('a')
|
||||
promoteButton.classList.add('orange-button', 'peertube-button-link')
|
||||
promoteButton.style.margin = '5px'
|
||||
promoteButton.onclick = async () => {
|
||||
await fetch(
|
||||
getBaseRoute(clientOptions) + '/api/promote/' + encodeURIComponent(room.jid.replace(/@.*$/, '')),
|
||||
{
|
||||
method: 'PUT',
|
||||
headers: peertubeHelpers.getAuthHeader()
|
||||
}
|
||||
)
|
||||
}
|
||||
// FIXME: we can use promoteSVG, which is in client scope...
|
||||
promoteButton.innerHTML = `<svg
|
||||
xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 4.233 4.233"
|
||||
>
|
||||
<g style="stroke-width:1.00021;stroke-miterlimit:4;stroke-dasharray:none">
|
||||
<path
|
||||
style="opacity:.998;fill:currentColor;fill-opacity:1;stroke:currentColor;stroke-width:1.17052;
|
||||
stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M2.403.583V-.19a.94.94 0 0 1 .942-.943l1.962-.023 1.961.01a.94.94 0 0 1
|
||||
.942.942v.772S6.586 4.9 5.307 4.92C4.027 4.939 2.403.583 2.403.583Z"
|
||||
transform="matrix(.45208 0 0 .45208 -.526 1.335)"
|
||||
/>
|
||||
</g>
|
||||
</svg>`
|
||||
const promoteEl = document.createElement('td')
|
||||
promoteEl.append(promoteButton)
|
||||
|
||||
const channelConfigurationEl = document.createElement('td')
|
||||
nameEl.append(aEl)
|
||||
lineEl.append(nameEl)
|
||||
@ -146,6 +176,7 @@ function register (clientOptions: RegisterClientOptions): void {
|
||||
if (useChannelConfiguration) {
|
||||
lineEl.append(channelConfigurationEl) // else the element will just be dropped.
|
||||
}
|
||||
lineEl.append(promoteEl)
|
||||
table.append(lineEl)
|
||||
|
||||
const writeChannelConfigurationLink = (channelId: number | string): void => {
|
||||
|
@ -1,12 +1,16 @@
|
||||
import type { Video } from '@peertube/peertube-types'
|
||||
import type { RegisterClientOptions } from '@peertube/peertube-types/client'
|
||||
import type { InitConverseJSParams } from 'shared/lib/types'
|
||||
import { videoHasWebchat, videoHasRemoteWebchat } from 'shared/lib/video'
|
||||
import { localizedHelpUrl } from './utils/help'
|
||||
import { logger } from './utils/logger'
|
||||
import { closeSVG, openBlankChatSVG, openChatSVG, shareChatUrlSVG, helpButtonSVG } from './videowatch/buttons'
|
||||
import {
|
||||
closeSVG, openBlankChatSVG, openChatSVG, shareChatUrlSVG, helpButtonSVG, promoteSVG
|
||||
} from './videowatch/buttons'
|
||||
import { displayButton, displayButtonOptions } from './videowatch/button'
|
||||
import { shareChatUrl } from './videowatch/share'
|
||||
import { displayConverseJS } from './utils/conversejs'
|
||||
import { getBaseRoute } from './utils/uri'
|
||||
|
||||
interface VideoWatchLoadedHookOptions {
|
||||
videojs: any
|
||||
@ -71,7 +75,11 @@ function register (registerOptions: RegisterClientOptions): void {
|
||||
let settings: any = {} // will be loaded later
|
||||
|
||||
async function insertChatDom (
|
||||
container: HTMLElement, video: Video, showOpenBlank: boolean, showShareUrlButton: boolean
|
||||
container: HTMLElement,
|
||||
video: Video,
|
||||
showOpenBlank: boolean,
|
||||
showShareUrlButton: boolean,
|
||||
showPromote: boolean
|
||||
): Promise<void> {
|
||||
logger.log('Adding livechat in the DOM...')
|
||||
const viewersDocumentationHelpUrl = await localizedHelpUrl(registerOptions, {
|
||||
@ -84,13 +92,15 @@ function register (registerOptions: RegisterClientOptions): void {
|
||||
peertubeHelpers.translate(LOC_OPEN_CHAT_NEW_WINDOW),
|
||||
peertubeHelpers.translate(LOC_CLOSE_CHAT),
|
||||
peertubeHelpers.translate(LOC_SHARE_CHAT_LINK),
|
||||
peertubeHelpers.translate(LOC_ONLINE_HELP)
|
||||
peertubeHelpers.translate(LOC_ONLINE_HELP),
|
||||
peertubeHelpers.translate(LOC_PROMOTE)
|
||||
]).then(labels => {
|
||||
const labelOpen = labels[0]
|
||||
const labelOpenBlank = labels[1]
|
||||
const labelClose = labels[2]
|
||||
const labelShareUrl = labels[3]
|
||||
const labelHelp = labels[4]
|
||||
const labelPromote = labels[5]
|
||||
|
||||
const buttonContainer = document.createElement('div')
|
||||
buttonContainer.classList.add('peertube-plugin-livechat-buttons')
|
||||
@ -133,6 +143,45 @@ function register (registerOptions: RegisterClientOptions): void {
|
||||
additionalClasses: []
|
||||
})
|
||||
}
|
||||
if (showPromote) {
|
||||
groupButtons.push({
|
||||
buttonContainer,
|
||||
name: 'promote',
|
||||
label: labelPromote,
|
||||
callback: async () => {
|
||||
try {
|
||||
// First we must get the room JID (can be video.uuid@ or channel.id@)
|
||||
const response = await fetch(
|
||||
getBaseRoute(registerOptions) + '/api/configuration/room/' +
|
||||
encodeURIComponent(video.uuid),
|
||||
{
|
||||
method: 'GET',
|
||||
headers: peertubeHelpers.getAuthHeader()
|
||||
}
|
||||
)
|
||||
|
||||
const converseJSParams: InitConverseJSParams = await (response).json()
|
||||
if (converseJSParams.isRemoteChat) {
|
||||
throw new Error('Cant promote on remote chat.')
|
||||
}
|
||||
|
||||
const roomJIDLocalPart = converseJSParams.room.replace(/@.*$/, '')
|
||||
|
||||
await fetch(
|
||||
getBaseRoute(registerOptions) + '/api/promote/' + encodeURIComponent(roomJIDLocalPart),
|
||||
{
|
||||
method: 'PUT',
|
||||
headers: peertubeHelpers.getAuthHeader()
|
||||
}
|
||||
)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
},
|
||||
icon: promoteSVG,
|
||||
additionalClasses: []
|
||||
})
|
||||
}
|
||||
groupButtons.push({
|
||||
buttonContainer,
|
||||
name: 'help',
|
||||
@ -287,6 +336,7 @@ function register (registerOptions: RegisterClientOptions): void {
|
||||
}
|
||||
|
||||
let showShareUrlButton: boolean = false
|
||||
let showPromote: boolean = false
|
||||
if (video.isLocal) { // No need for shareButton on remote chats.
|
||||
const chatShareUrl = settings['chat-share-url'] ?? ''
|
||||
if (chatShareUrl === 'everyone') {
|
||||
@ -296,9 +346,19 @@ function register (registerOptions: RegisterClientOptions): void {
|
||||
} else if (chatShareUrl === 'owner+moderators') {
|
||||
showShareUrlButton = guessIsMine(registerOptions, video) || guessIamIModerator(registerOptions)
|
||||
}
|
||||
|
||||
if (guessIamIModerator(registerOptions)) {
|
||||
showPromote = true
|
||||
}
|
||||
}
|
||||
|
||||
await insertChatDom(container as HTMLElement, video, !!settings['chat-open-blank'], showShareUrlButton)
|
||||
await insertChatDom(
|
||||
container as HTMLElement,
|
||||
video,
|
||||
!!settings['chat-open-blank'],
|
||||
showShareUrlButton,
|
||||
showPromote
|
||||
)
|
||||
if (settings['chat-auto-display']) {
|
||||
await openChat(video)
|
||||
} else if (container) {
|
||||
|
@ -10,7 +10,7 @@ interface displayButtonOptionsBase {
|
||||
}
|
||||
|
||||
interface displayButtonOptionsCallback extends displayButtonOptionsBase {
|
||||
callback: () => void | boolean
|
||||
callback: () => void | boolean | Promise<void>
|
||||
}
|
||||
|
||||
interface displayButtonOptionsHref extends displayButtonOptionsBase {
|
||||
|
@ -111,12 +111,26 @@ const helpButtonSVG: SVGButton = () => {
|
||||
`
|
||||
}
|
||||
|
||||
const promoteSVG: SVGButton = () => {
|
||||
// This content comes from the file assets/images/moderator.svg, after svgo cleaning.
|
||||
// To get the formated content, you can do:
|
||||
// xmllint dist/client/images/moderator.svg --format
|
||||
// Then replace the color by `currentColor`
|
||||
return `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 4.233 4.233">
|
||||
<g style="stroke-width:1.00021;stroke-miterlimit:4;stroke-dasharray:none">
|
||||
<path style="opacity:.998;fill:currentColor;fill-opacity:1;stroke:currentColor;stroke-width:1.17052;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M2.403.583V-.19a.94.94 0 0 1 .942-.943l1.962-.023 1.961.01a.94.94 0 0 1 .942.942v.772S6.586 4.9 5.307 4.92C4.027 4.939 2.403.583 2.403.583Z" transform="matrix(.45208 0 0 .45208 -.526 1.335)"/>
|
||||
</g>
|
||||
</svg>
|
||||
`
|
||||
}
|
||||
|
||||
export {
|
||||
closeSVG,
|
||||
openChatSVG,
|
||||
openBlankChatSVG,
|
||||
shareChatUrlSVG,
|
||||
helpButtonSVG
|
||||
helpButtonSVG,
|
||||
promoteSVG
|
||||
}
|
||||
export type {
|
||||
SVGButton
|
||||
|
Reference in New Issue
Block a user