update panels

This commit is contained in:
rxdn 2020-06-20 15:09:19 +01:00
parent ca7eee6daa
commit 66d54b4e03
12 changed files with 538 additions and 38 deletions

View File

@ -228,11 +228,11 @@ func (p *panel) verifyContent() bool {
return len(p.Content) > 0 && len(p.Content) < 1025 return len(p.Content) > 0 && len(p.Content) < 1025
} }
func (p *panel) getEmoji() (string, bool) { func (p *panel) getEmoji() (emoji string, ok bool) {
p.Emote = strings.Replace(p.Emote, ":", "", -1) p.Emote = strings.Replace(p.Emote, ":", "", -1)
emoji := utils.GetEmojiByName(p.Emote) emoji, ok = utils.GetEmoji(p.Emote)
return emoji, emoji != "" return
} }
func (p *panel) verifyChannel(channels []channel.Channel) bool { func (p *panel) verifyChannel(channels []channel.Channel) bool {

View File

@ -0,0 +1,206 @@
package api
import (
"github.com/TicketsBot/GoPanel/botcontext"
dbclient "github.com/TicketsBot/GoPanel/database"
"github.com/TicketsBot/GoPanel/rpc"
"github.com/TicketsBot/common/premium"
"github.com/TicketsBot/database"
"github.com/gin-gonic/gin"
"github.com/rxdn/gdl/rest"
"github.com/rxdn/gdl/rest/request"
"strconv"
)
func UpdatePanel(ctx *gin.Context) {
guildId := ctx.Keys["guildid"].(uint64)
botContext, err := botcontext.ContextForGuild(guildId)
if err != nil {
ctx.AbortWithStatusJSON(500, gin.H{
"success": false,
"error": err.Error(),
})
return
}
var data panel
if err := ctx.BindJSON(&data); err != nil {
ctx.AbortWithStatusJSON(400, gin.H{
"success": false,
"error": err.Error(),
})
return
}
messageId, err := strconv.ParseUint(ctx.Param("message"), 10, 64)
if err != nil {
ctx.AbortWithStatusJSON(400, gin.H{
"success": false,
"error": err.Error(),
})
return
}
data.MessageId = messageId
// get existing
existing, err := dbclient.Client.Panel.Get(data.MessageId)
if err != nil {
ctx.AbortWithStatusJSON(500, gin.H{
"success": false,
"error": err.Error(),
})
return
}
// check guild ID matches
if existing.GuildId != guildId {
ctx.AbortWithStatusJSON(400, gin.H{
"success": false,
"error": "Guild ID does not match",
})
return
}
if !data.doValidations(ctx, guildId) {
return
}
// check if we need to update the message
shouldUpdateMessage := uint32(existing.Colour) != data.Colour ||
existing.ChannelId != data.ChannelId ||
existing.Content != data.Content ||
existing.Title != data.Title ||
existing.ReactionEmote != data.Emote
emoji, _ := data.getEmoji() // already validated
newMessageId := messageId
if shouldUpdateMessage {
// delete old message
if err := rest.DeleteMessage(botContext.Token, botContext.RateLimiter, existing.ChannelId, existing.MessageId); err != nil {
ctx.AbortWithStatusJSON(500, gin.H{
"success": false,
"error": err.Error(),
})
return
}
premiumTier := rpc.PremiumClient.GetTierByGuildId(guildId, true, botContext.Token, botContext.RateLimiter)
newMessageId, err = data.sendEmbed(&botContext, premiumTier > premium.None)
if err != nil {
if err == request.ErrForbidden {
ctx.AbortWithStatusJSON(500, gin.H{
"success": false,
"error": "I do not have permission to send messages in the specified channel",
})
} else {
// TODO: Most appropriate error?
ctx.AbortWithStatusJSON(500, gin.H{
"success": false,
"error": err.Error(),
})
}
return
}
// Add reaction
if err = rest.CreateReaction(botContext.Token, botContext.RateLimiter, data.ChannelId, newMessageId, emoji); err != nil {
if err == request.ErrForbidden {
ctx.AbortWithStatusJSON(500, gin.H{
"success": false,
"error": "I do not have permission to add reactions in the specified channel",
})
} else {
// TODO: Most appropriate error?
ctx.AbortWithStatusJSON(500, gin.H{
"success": false,
"error": err.Error(),
})
}
return
}
}
// Store in DB
panel := database.Panel{
MessageId: newMessageId,
ChannelId: data.ChannelId,
GuildId: guildId,
Title: data.Title,
Content: data.Content,
Colour: int32(data.Colour),
TargetCategory: data.CategoryId,
ReactionEmote: emoji,
WelcomeMessage: data.WelcomeMessage,
}
if err = dbclient.Client.Panel.Update(messageId, panel); err != nil {
ctx.AbortWithStatusJSON(500, gin.H{
"success": false,
"error": err.Error(),
})
return
}
// insert role mention data
// delete old data
if err = dbclient.Client.PanelRoleMentions.DeleteAll(newMessageId); err != nil {
ctx.AbortWithStatusJSON(500, gin.H{
"success": false,
"error": err.Error(),
})
return
}
// TODO: Reduce to 1 query
if err = dbclient.Client.PanelUserMention.Set(newMessageId, false); err != nil {
ctx.AbortWithStatusJSON(500, gin.H{
"success": false,
"error": err.Error(),
})
return
}
// string is role ID or "user" to mention the ticket opener
for _, mention := range data.Mentions {
if mention == "user" {
if err = dbclient.Client.PanelUserMention.Set(newMessageId, true); err != nil {
ctx.AbortWithStatusJSON(500, gin.H{
"success": false,
"error": err.Error(),
})
return
}
} else {
roleId, err := strconv.ParseUint(mention, 10, 64)
if err != nil {
ctx.AbortWithStatusJSON(500, gin.H{
"success": false,
"error": err.Error(),
})
return
}
// should we check the role is a valid role in the guild?
// not too much of an issue if it isnt
if err = dbclient.Client.PanelRoleMentions.Add(newMessageId, roleId); err != nil {
ctx.AbortWithStatusJSON(500, gin.H{
"success": false,
"error": err.Error(),
})
return
}
}
}
ctx.JSON(200, gin.H{
"success": true,
"message_id": strconv.FormatUint(newMessageId, 10),
})
}

View File

@ -89,6 +89,7 @@ func StartServer() {
guildAuthApi.GET("/panels", api.ListPanels) guildAuthApi.GET("/panels", api.ListPanels)
guildAuthApi.PUT("/panels", api.CreatePanel) guildAuthApi.PUT("/panels", api.CreatePanel)
guildAuthApi.PUT("/panels/:message", api.UpdatePanel)
guildAuthApi.DELETE("/panels/:message", api.DeletePanel) guildAuthApi.DELETE("/panels/:message", api.DeletePanel)
guildAuthApi.GET("/logs/", api.GetLogs) guildAuthApi.GET("/logs/", api.GetLogs)
@ -142,36 +143,43 @@ func createRenderer() multitemplate.Renderer {
r = addManageTemplate(r, "blacklist") r = addManageTemplate(r, "blacklist")
r = addManageTemplate(r, "logs") r = addManageTemplate(r, "logs")
r = addManageTemplate(r, "modmaillogs") r = addManageTemplate(r, "modmaillogs")
r = addManageTemplate(r, "settings") r = addManageTemplate(r, "settings", "./public/templates/includes/substitutionmodal.tmpl")
r = addManageTemplate(r, "ticketlist") r = addManageTemplate(r, "ticketlist")
r = addManageTemplate(r, "ticketview") r = addManageTemplate(r, "ticketview")
r = addManageTemplate(r, "panels") r = addManageTemplate(r, "panels", "./public/templates/includes/substitutionmodal.tmpl", "./public/templates/includes/paneleditmodal.tmpl")
r = addManageTemplate(r, "tags") r = addManageTemplate(r, "tags")
return r return r
} }
func addMainTemplate(renderer multitemplate.Renderer, name string) multitemplate.Renderer { func addMainTemplate(renderer multitemplate.Renderer, name string, extra ...string) multitemplate.Renderer {
renderer.AddFromFiles(fmt.Sprintf("main/%s", name), files := []string{
"./public/templates/layouts/main.tmpl", "./public/templates/layouts/main.tmpl",
"./public/templates/includes/head.tmpl", "./public/templates/includes/head.tmpl",
"./public/templates/includes/sidebar.tmpl", "./public/templates/includes/sidebar.tmpl",
"./public/templates/includes/loadingscreen.tmpl", "./public/templates/includes/loadingscreen.tmpl",
fmt.Sprintf("./public/templates/views/%s.tmpl", name), fmt.Sprintf("./public/templates/views/%s.tmpl", name),
) }
files = append(files, extra...)
renderer.AddFromFiles(fmt.Sprintf("main/%s", name), files...)
return renderer return renderer
} }
func addManageTemplate(renderer multitemplate.Renderer, name string) multitemplate.Renderer { func addManageTemplate(renderer multitemplate.Renderer, name string, extra ...string) multitemplate.Renderer {
renderer.AddFromFiles(fmt.Sprintf("manage/%s", name), files := []string{
"./public/templates/layouts/manage.tmpl", "./public/templates/layouts/manage.tmpl",
"./public/templates/includes/head.tmpl", "./public/templates/includes/head.tmpl",
"./public/templates/includes/sidebar.tmpl", "./public/templates/includes/sidebar.tmpl",
"./public/templates/includes/navbar.tmpl", "./public/templates/includes/navbar.tmpl",
"./public/templates/includes/substitutionmodal.tmpl",
"./public/templates/includes/loadingscreen.tmpl", "./public/templates/includes/loadingscreen.tmpl",
fmt.Sprintf("./public/templates/views/%s.tmpl", name), fmt.Sprintf("./public/templates/views/%s.tmpl", name),
) }
files = append(files, extra...)
renderer.AddFromFiles(fmt.Sprintf("manage/%s", name), files...)
return renderer return renderer
} }

2
go.mod
View File

@ -6,7 +6,7 @@ require (
github.com/BurntSushi/toml v0.3.1 github.com/BurntSushi/toml v0.3.1
github.com/TicketsBot/archiverclient v0.0.0-20200425115930-0ca198cc8306 github.com/TicketsBot/archiverclient v0.0.0-20200425115930-0ca198cc8306
github.com/TicketsBot/common v0.0.0-20200529141045-7426ad13f1a4 github.com/TicketsBot/common v0.0.0-20200529141045-7426ad13f1a4
github.com/TicketsBot/database v0.0.0-20200619194554-a6db672a94cf github.com/TicketsBot/database v0.0.0-20200620140717-f747a0bb4238
github.com/apex/log v1.1.2 github.com/apex/log v1.1.2
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/dgrijalva/jwt-go v3.2.0+incompatible

View File

@ -195,3 +195,7 @@ html > ::-webkit-scrollbar {
.bootstrap-select .bs-ok-default:after { .bootstrap-select .bs-ok-default:after {
color: #2ECC71 !important; color: #2ECC71 !important;
} }
.wrapper {
z-index: 1000 !important;
}

View File

@ -0,0 +1,23 @@
function clear(...elements) {
for (const elementId of elements) {
document.getElementById(elementId).value = '';
}
}
function hideBackdrop() {
for (const backdrop of document.getElementsByClassName('modal-backdrop fade show')) {
backdrop.remove();
}
}
function registerHideListener(elementId) {
$(`#${elementId}`).on('hidden.bs.modal', hideBackdrop);
}
function showBackdrop() {
hideBackdrop();
const backdrop = document.createElement('div');
backdrop.classList.add('modal-backdrop', 'fade', 'show');
document.getElementsByClassName('main-panel')[0].appendChild(backdrop);
}

View File

@ -0,0 +1,222 @@
{{define "paneleditmodal"}}
<div class="modal fade" id="editmodal" tabindex="-1" role="dialog" aria-labelledby="editmodal" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><b>Edit Panel</b></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="container-fluid">
<div class="row">
<div class="col-md-4 pr-1">
<div class="form-group">
<label class="black">Panel Title</label>
<input type="text" class="form-control" placeholder="Open a ticket!" id="edit-title">
</div>
</div>
<div class="col-md-8 pr-1">
<div class="form-group">
<label class="black">Panel Content</label>
<textarea type="text" class="form-control"
placeholder="By reacting to this ticket, a ticket will be opened for you."
id="edit-content"></textarea>
</div>
</div>
</div>
<div class="row">
<div class="col-md-2 pr-1">
<label class="black">Panel Colour</label>
<div class="input-group mb-3">
<input type="color" class="form-control input-fill" id="edit-colour">
</div>
</div>
<div class="col-md-3 pr-1">
<label class="black">Panel Channel</label>
<div class="input-group mb-3">
<div class="input-group-prepend">
<div class="input-group-text">#</div>
</div>
<select class="form-control" id="edit-channel-container">
</select>
</div>
</div>
<div class="col-md-4 pr-1">
<label class="black">Ticket Channel Category</label>
<div class="input-group mb-3">
<select class="form-control" id="edit-category-container">
</select>
</div>
</div>
<div class="col-md-3 pr-1">
<div class="form-group">
<label class="black">Reaction Emote</label>
<input type="text" class="form-control" placeholder="envelope_with_arrow" id="edit-reaction">
</div>
</div>
</div>
<div class="row">
<div class="col-md-4 pr-1 offset-md-4">
<div class="text-center">
<button class="btn btn-primary btn-fill" type="button" data-toggle="collapse" data-target="#edit-advanced" aria-expanded="false" aria-controls="edit-advanced">
Expand advanced settings
</button>
</div>
</div>
</div>
<div class="row">
<div class="collapse" id="edit-advanced" style="width: 100%">
<div class="container-fluid">
<div class="row">
<div class="col-md-12 pr-1">
<div class="form-group">
<label class="black">Welcome Message</label>
<textarea type="text" class="form-control" placeholder="If not provided, your server's default welcome message will be used" id="edit-welcome-message"></textarea>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12 pr-1">
<div class="form-group">
<label class="black">Mention On Open</label>
<select class="selectpicker form-control" id="edit-mentions" multiple data-live-search="true" data-dropup-auto="false" data-size="5" data-display="static">
</select>
</div>
</div>
</div>
<input type="hidden" id="edit-message-id">
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary btn-fill" onclick="updatePanel()">Save</button>
</div>
</div>
</div>
</div>
<script>
function resetEditModal() {
clear('edit-title', 'edit-content', 'edit-message-id', 'edit-reaction', 'edit-welcome-message');
$('#edit-mentions').selectpicker('deselectAll');
}
registerHideListener('editmodal');
$('#editmodal').on('hidden.bs.modal', resetEditModal);
async function openEditModal(messageId) {
resetEditModal();
const res = await axios.get('/api/{{.guildId}}/panels');
if (res.status !== 200) {
showToast("Error", res.data);
return;
}
const panel = res.data.find(panel => panel.message_id === messageId);
if (panel === undefined) {
showToast('Error', 'Panel not found');
return;
}
await fillEditData(panel);
$('#editmodal').modal('show');
showBackdrop();
}
async function fillEditData(panel) {
document.getElementById('edit-message-id').value = panel.message_id;
document.getElementById('edit-title').value = panel.title;
document.getElementById('edit-content').value = panel.content;
document.getElementById('edit-colour').value = `#${panel.colour.toString(16)}`;
document.getElementById('edit-reaction').value = panel.emote;
if (panel.welcome_message !== null) {
document.getElementById('edit-welcome-message').value = panel.welcome_message;
}
const channels = await getChannels();
await fillChannels('edit-channel-container', channels);
await fillCategories('edit-category-container', channels);
await fillMentions('edit-mentions');
setActiveChannel(panel);
setActiveCategory(panel);
setActiveMentions(panel);
}
function setActiveChannel(panel) {
const select = document.getElementById('edit-channel-container');
for (let i = 0; i < select.children.length; i++) {
const child = select.children[i];
if (child.value === panel.channel_id) {
select.selectedIndex = i;
}
}
}
function setActiveCategory(panel) {
const select = document.getElementById('edit-category-container');
for (let i = 0; i < select.children.length; i++) {
const child = select.children[i];
if (child.value === panel.category_id) {
select.selectedIndex = i;
}
}
}
function setActiveMentions(panel) {
if (panel.mentions !== null) {
$('#edit-mentions').selectpicker('val', panel.mentions);
}
}
async function updatePanel() {
const messageId = document.getElementById('edit-message-id').value;
const title = document.getElementById('edit-title').value;
const content = document.getElementById('edit-content').value;
const emote = document.getElementById('edit-reaction').value.replace(':', '');
const welcomeMessage = document.getElementById('edit-welcome-message').value;
const data = {
message_id: messageId,
title: title === '' ? 'Open a ticket!' : title,
content: content === '' ? 'By reacting to this ticket, a message will be opened for you.' : content,
emote: emote === '' ? 'envelope_with_arrow' : emote,
colour: parseInt(`0x${document.getElementById('colour').value.slice(1)}`),
channel_id: document.getElementById('edit-channel-container').options[document.getElementById('edit-channel-container').selectedIndex].value,
category_id: document.getElementById('edit-category-container').options[document.getElementById('edit-category-container').selectedIndex].value,
welcome_message: welcomeMessage === '' ? null : welcomeMessage,
mentions: $('#edit-mentions').val()
};
const res = await axios.put('/api/{{.guildId}}/panels/' + messageId, data);
if (res.status === 200 && res.data.success) {
showToast('Success', 'Panel updated successfully')
$('#editmodal').modal('hide');
resetEditModal();
// remove old data
// TODO: Don't remove just update, looks cleaner
const el = document.getElementById(messageId);
el.parentNode.removeChild(el);
data.message_id = res.data.message_id;
appendPanel(data, await getChannels());
} else {
showToast('Error', res.data.error);
}
}
</script>
{{end}}

View File

@ -31,4 +31,13 @@
</div> </div>
</div> </div>
</div> </div>
<script>
registerHideListener('modal-substitutions');
function showSubstitutionModal() {
$('#modal-substitutions').modal('show');
showBackdrop();
}
</script>
{{end}} {{end}}

View File

@ -5,7 +5,6 @@
</head> </head>
<body> <body>
<div class="wrapper"> <div class="wrapper">
{{template "substitutions" .}} <!-- Has to be here for some reason -->
<div class="main-panel" style="width: 100% !important;"> <div class="main-panel" style="width: 100% !important;">
{{template "navbar" .}} {{template "navbar" .}}
{{template "loadingscreen" .}} {{template "loadingscreen" .}}

View File

@ -1,4 +1,8 @@
{{define "content"}} {{define "content"}}
<script src="/assets/js/modalbackdrop.js"></script>
{{template "substitutions" .}}
{{template "paneleditmodal" .}}
<div class="content"> <div class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
@ -18,6 +22,7 @@
<th>Panel Title</th> <th>Panel Title</th>
<th>Panel Content</th> <th>Panel Content</th>
<th>Ticket Channel Category</th> <th>Ticket Channel Category</th>
<th>Edit</th>
<th>Delete</th> <th>Delete</th>
</tr> </tr>
</thead> </thead>
@ -69,9 +74,6 @@
<div class="col-md-3 pr-1"> <div class="col-md-3 pr-1">
<label class="black">Ticket Channel Category</label> <label class="black">Ticket Channel Category</label>
<div class="input-group mb-3"> <div class="input-group mb-3">
<div class="input-group-prepend">
<div class="input-group-text">#</div>
</div>
<select class="form-control" name="categories" id="category-container"> <select class="form-control" name="categories" id="category-container">
</select> </select>
</div> </div>
@ -102,7 +104,7 @@
<div class="col-md-6 pr-1"> <div class="col-md-6 pr-1">
<div class="form-group"> <div class="form-group">
<label class="black">Welcome Message <label class="black">Welcome Message
<i class="fas fa-question pointer" data-toggle="modal" data-target="#modal-substitutions"></i> <i class="fas fa-question pointer" onclick="showSubstitutionModal()"></i>
</label> </label>
<textarea type="text" class="form-control" placeholder="If not provided, your server's default welcome message will be used" id="welcome_message"></textarea> <textarea type="text" class="form-control" placeholder="If not provided, your server's default welcome message will be used" id="welcome_message"></textarea>
</div> </div>
@ -181,7 +183,7 @@
channel_id: document.getElementById('channel-container').options[document.getElementById('channel-container').selectedIndex].value, channel_id: document.getElementById('channel-container').options[document.getElementById('channel-container').selectedIndex].value,
category_id: document.getElementById('category-container').options[document.getElementById('category-container').selectedIndex].value, category_id: document.getElementById('category-container').options[document.getElementById('category-container').selectedIndex].value,
welcome_message: welcomeMessage === '' ? null : welcomeMessage, welcome_message: welcomeMessage === '' ? null : welcomeMessage,
mentions: $('.selectpicker').val() mentions: $('#mentions').val()
}; };
const res = await axios.put('/api/{{.guildId}}/panels', data); const res = await axios.put('/api/{{.guildId}}/panels', data);
@ -194,8 +196,8 @@
} }
} }
async function fillChannels(channels) { async function fillChannels(elementId, channels) {
const container = document.getElementById('channel-container'); const container = document.getElementById(elementId);
channels.filter(ch => ch.type === 0).forEach(ch => { channels.filter(ch => ch.type === 0).forEach(ch => {
const el = document.createElement('option'); const el = document.createElement('option');
@ -205,8 +207,8 @@
}); });
} }
async function fillCategories(channels) { async function fillCategories(elementId, channels) {
const container = document.getElementById('category-container'); const container = document.getElementById(elementId);
channels.filter(ch => ch.type === 4).forEach(ch => { channels.filter(ch => ch.type === 4).forEach(ch => {
const el = document.createElement('option'); const el = document.createElement('option');
@ -242,6 +244,16 @@
appendTd(tr, panel.content); appendTd(tr, panel.content);
appendTd(tr, getChannelName(channels, panel.category_id)); appendTd(tr, getChannelName(channels, panel.category_id));
// build edit button
const editTd = document.createElement('td');
const editButton = document.createElement('button');
editButton.type = 'button';
editButton.classList.add('btn', 'btn-primary', 'btn-fill', 'mx-auto');
editButton.appendChild(document.createTextNode('Edit'));
editButton.onclick = () => { openEditModal(panel.message_id) };
editTd.appendChild(editButton);
tr.appendChild(editTd);
// build remove button // build remove button
const deleteTd = document.createElement('td'); const deleteTd = document.createElement('td');
const deleteButton = document.createElement('button'); const deleteButton = document.createElement('button');
@ -269,8 +281,8 @@
return res.data.length; return res.data.length;
} }
async function fillMentions() { async function fillMentions(elementId) {
const select = document.getElementById('mentions'); const select = document.getElementById(elementId);
// ticket opener // ticket opener
const ticketOpener = document.createElement('option'); const ticketOpener = document.createElement('option');
@ -294,7 +306,7 @@
} }
} }
$('.selectpicker').selectpicker('refresh'); $('#mentions').selectpicker('refresh');
} }
async function loadData() { async function loadData() {
@ -302,13 +314,12 @@
const panelCount = await fillPanels(channels); const panelCount = await fillPanels(channels);
await fillPanelQuota(panelCount); await fillPanelQuota(panelCount);
await fillChannels(channels); await fillChannels('channel-container', channels);
await fillCategories(channels); await fillCategories('category-container', channels);
await fillMentions(); await fillMentions('mentions');
} }
withLoadingScreen(loadData); withLoadingScreen(loadData);
</script> </script>
</div> </div>
{{end}} {{end}}

View File

@ -1,4 +1,7 @@
{{define "content"}} {{define "content"}}
<script src="/assets/js/modalbackdrop.js"></script>
{{template "substitutions" .}}
<div class="content"> <div class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
@ -79,7 +82,7 @@
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<label>Welcome Message (Max len. 1000) <label>Welcome Message (Max len. 1000)
<i class="fas fa-question pointer" data-toggle="modal" data-target="#modal-substitutions"></i> <i class="fas fa-question pointer" onclick="showSubstitutionModal()"></i>
</label> </label>
<textarea name="welcomeMessage" class="form-control" rows="3" id="welcome_message"></textarea> <textarea name="welcomeMessage" class="form-control" rows="3" id="welcome_message"></textarea>
</div> </div>

View File

@ -6,7 +6,8 @@ import (
"io/ioutil" "io/ioutil"
) )
var emojis map[string]interface{} var emojisByName map[string]string
var emojis []string
func LoadEmoji() { func LoadEmoji() {
bytes, err := ioutil.ReadFile("emojis.json"); if err != nil { bytes, err := ioutil.ReadFile("emojis.json"); if err != nil {
@ -14,17 +15,31 @@ func LoadEmoji() {
return return
} }
if err := json.Unmarshal(bytes, &emojis); err != nil { if err := json.Unmarshal(bytes, &emojisByName); err != nil {
log.Error("Couldn't load emoji: " + err.Error()) log.Error("Couldn't load emoji: " + err.Error())
return return
} }
emojis = make([]string, len(emojisByName))
i := 0
for _, emoji := range emojisByName {
emojis[i] = emoji
i++
}
} }
func GetEmojiByName(name string) string { func GetEmoji(input string) (emoji string, ok bool) {
emoji, ok := emojis[name]; if !ok { // try by name first
return "" emoji, ok = emojisByName[input]
if !ok { // else try by the actual unicode char
for _, unicode := range emojis {
if unicode == input {
emoji = unicode
ok = true
break
}
}
} }
str, _ := emoji.(string) return
return str
} }