Custom channel emoticons WIP (#130)
This commit is contained in:
parent
893708d93a
commit
a777c7ac8d
@ -431,7 +431,7 @@ livechat-image-file-input {
|
||||
|
||||
// width and height are values coming from ConverseJS custom emojis.
|
||||
// If we want to upload something else, we should add options on the field to customize.
|
||||
height: 1.5em;
|
||||
width: 1.5em;
|
||||
max-height: 1.5em;
|
||||
max-width: 1.5em;
|
||||
}
|
||||
}
|
||||
|
1
client/@types/global.d.ts
vendored
1
client/@types/global.d.ts
vendored
@ -88,6 +88,7 @@ declare const LOC_INVALID_VALUE_WRONG_TYPE: string
|
||||
declare const LOC_INVALID_VALUE_WRONG_FORMAT: string
|
||||
declare const LOC_INVALID_VALUE_NOT_IN_RANGE: string
|
||||
declare const LOC_INVALID_VALUE_FILE_TOO_BIG: string
|
||||
declare const LOC_INVALID_VALUE_DUPLICATE: string
|
||||
|
||||
declare const LOC_CHATROOM_NOT_ACCESSIBLE: string
|
||||
|
||||
|
@ -182,12 +182,17 @@ export class ChannelDetailsService {
|
||||
public async validateEmojisConfiguration (channelEmojis: ChannelEmojis): Promise<boolean> {
|
||||
const propertiesError: ValidationError['properties'] = {}
|
||||
|
||||
const seen = new Map<string, true>()
|
||||
for (const [i, e] of channelEmojis.customEmojis.entries()) {
|
||||
propertiesError[`emojis.${i}.sn`] = []
|
||||
if (e.sn === '') {
|
||||
propertiesError[`emojis.${i}.sn`].push(ValidationErrorType.Missing)
|
||||
} else if (!/^:[\w-]+:$/.test(e.sn)) {
|
||||
propertiesError[`emojis.${i}.sn`].push(ValidationErrorType.WrongFormat)
|
||||
} else if (seen.has(e.sn)) {
|
||||
propertiesError[`emojis.${i}.sn`].push(ValidationErrorType.Duplicate)
|
||||
} else {
|
||||
seen.set(e.sn, true)
|
||||
}
|
||||
|
||||
propertiesError[`emojis.${i}.url`] = []
|
||||
|
@ -617,6 +617,9 @@ export class DynamicTableFormElement extends LivechatElement {
|
||||
if (validationErrorTypes.includes(ValidationErrorType.NotInRange)) {
|
||||
errorMessages.push(html`${ptTr(LOC_INVALID_VALUE_NOT_IN_RANGE)}`)
|
||||
}
|
||||
if (validationErrorTypes.includes(ValidationErrorType.Duplicate)) {
|
||||
errorMessages.push(html`${ptTr(LOC_INVALID_VALUE_DUPLICATE)}`)
|
||||
}
|
||||
|
||||
return html`<div id="${inputId}-feedback" class="invalid-feedback">${errorMessages}</div>`
|
||||
} else {
|
||||
|
@ -8,7 +8,6 @@ import { registerClientOptionsContext } from '../contexts/peertube'
|
||||
import { html } from 'lit'
|
||||
import { customElement, property } from 'lit/decorators.js'
|
||||
import { consume } from '@lit/context'
|
||||
import { ifDefined } from 'lit/directives/if-defined.js'
|
||||
|
||||
/**
|
||||
* Special element to upload image files.
|
||||
@ -40,7 +39,6 @@ export class ImageFileInputElement extends LivechatElement {
|
||||
public accept: string[] = ['image/jpg', 'image/png', 'image/gif']
|
||||
|
||||
protected override render = (): unknown => {
|
||||
// FIXME: limit file size in the upload field.
|
||||
return html`
|
||||
${this.value
|
||||
? html`<img src=${this.value} @click=${(ev: Event) => {
|
||||
@ -57,11 +55,6 @@ export class ImageFileInputElement extends LivechatElement {
|
||||
style=${this.value ? 'visibility: hidden;' : ''}
|
||||
@change=${async (ev: Event) => this._upload(ev)}
|
||||
/>
|
||||
<input
|
||||
type="hidden"
|
||||
name=${ifDefined(this.name)}
|
||||
value=${this.value ?? ''}
|
||||
/>
|
||||
`
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ export enum ValidationErrorType {
|
||||
WrongType,
|
||||
WrongFormat,
|
||||
NotInRange,
|
||||
Duplicate
|
||||
}
|
||||
|
||||
export class ValidationError extends Error {
|
||||
|
@ -119,7 +119,7 @@ function defaultConverseParams (
|
||||
params.emoji_categories = Object.assign(
|
||||
{},
|
||||
customEmojisUrl
|
||||
? { custom: ':xmpp:' } // TODO: put here the default custom emoji
|
||||
? { custom: ':converse:' }
|
||||
: {},
|
||||
{
|
||||
smileys: ':grinning:',
|
||||
|
@ -33,8 +33,10 @@ export const livechatEmojisPlugin = {
|
||||
return json
|
||||
}
|
||||
|
||||
// We will put default emojis at the end
|
||||
// We will put default emojis at the end, so keeping a copy
|
||||
const defaultCustom = json.custom ?? {}
|
||||
// Now we must clone json, to avoid side effects when navigating between several videos.
|
||||
json = JSON.parse(JSON.stringify(json))
|
||||
json.custom = {}
|
||||
|
||||
let defaultDef: CustomEmojiDefinition | undefined
|
||||
@ -48,6 +50,15 @@ export const livechatEmojisPlugin = {
|
||||
if (def.isCategoryEmoji) {
|
||||
defaultDef ??= def
|
||||
}
|
||||
|
||||
// We must also remove any existing emojis in category other than custom
|
||||
for (const type of Object.keys(json)) {
|
||||
const v: {[key: string]: any} = json[type]
|
||||
if (type !== 'custom' && type !== 'modifiers' && (def.sn in v)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete v[def.sn]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const key in defaultCustom) {
|
||||
|
@ -441,6 +441,7 @@ invalid_value_wrong_type: "Value is of the wrong type."
|
||||
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"
|
||||
|
||||
slow_mode_info: "Slow mode is enabled, users can send a message every %1$s seconds."
|
||||
|
||||
|
@ -285,11 +285,8 @@ async function _connectionInfos (
|
||||
params.forcetype ?? false
|
||||
)
|
||||
|
||||
if (video?.channelId && await Emojis.singletonSafe()?.channelHasCustomEmojis(video.channelId)) {
|
||||
customEmojisUrl = getBaseRouterRoute(options) +
|
||||
'emojis/channel/' +
|
||||
encodeURIComponent(video.channelId) +
|
||||
'/definition'
|
||||
if (video?.channelId) {
|
||||
customEmojisUrl = await Emojis.singletonSafe()?.channelCustomEmojisUrl(video.channelId)
|
||||
}
|
||||
} catch (err) {
|
||||
options.peertubeHelpers.logger.error(err)
|
||||
|
@ -63,6 +63,26 @@ export class Emojis {
|
||||
return fs.promises.access(filepath, fs.constants.F_OK).then(() => true, () => false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the public url for the channel emojis definition, if there are custom emojis.
|
||||
* @param channelId channel Id
|
||||
*/
|
||||
public async channelCustomEmojisUrl (channelId: number): Promise<string | undefined> {
|
||||
if (!await this.channelHasCustomEmojis(channelId)) {
|
||||
return undefined
|
||||
}
|
||||
return canonicalizePluginUri(
|
||||
this.options,
|
||||
getBaseRouterRoute(this.options) +
|
||||
'emojis/channel/' +
|
||||
encodeURIComponent(channelId) +
|
||||
'/definition',
|
||||
{
|
||||
removePluginVersion: true
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file path for the channel definition JSON file (does not test if the file exists).
|
||||
* @param channelId channel Id
|
||||
@ -286,6 +306,13 @@ export class Emojis {
|
||||
customEmojis.push(sanitized)
|
||||
}
|
||||
|
||||
// For now, the frontend does not implement isCategoryEmoji.
|
||||
// if there is no isCategoryEmoji, we will take the first value.
|
||||
// TODO: remove this when the frontend will be able to set this.
|
||||
if (!categoryEmojiFound && customEmojis.length) {
|
||||
customEmojis[0].isCategoryEmoji = true
|
||||
}
|
||||
|
||||
const result: ChannelEmojis = {
|
||||
customEmojis: customEmojis
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
export const maxSize: number = 20 * 1024
|
||||
export const maxSize: number = 30 * 1024
|
||||
export const allowedExtensions = ['png', 'jpg', 'jpeg', 'gif']
|
||||
export const inputFileAccept = ['image/jpg', 'image/png', 'image/gif']
|
||||
export const allowedMimeTypes = ['image/jpg', 'image/png', 'image/gif']
|
||||
|
Loading…
Reference in New Issue
Block a user