chat: theming support, hide chat when not needed, other minor changes

This commit is contained in:
moscovium
2018-09-26 17:24:02 +02:00
parent 9914752788
commit 03362d2c6d
7 changed files with 385 additions and 35 deletions

View File

@@ -1,5 +1,7 @@
local chatInputActive = false
local chatInputActivating = false
local chatHidden = true
local chatLoaded = false
RegisterNetEvent('chatMessage')
RegisterNetEvent('chat:addTemplate')
@@ -132,16 +134,54 @@ local function refreshCommands()
end
end
local function refreshThemes()
local themes = {}
for resIdx = 0, GetNumResources() - 1 do
local resource = GetResourceByFindIndex(resIdx)
if GetResourceState(resource) == 'started' then
local numThemes = GetNumResourceMetadata(resource, 'chat_theme')
if numThemes > 0 then
local themeName = GetResourceMetadata(resource, 'chat_theme')
local themeData = json.decode(GetResourceMetadata(resource, 'chat_theme_extra') or 'null')
if themeName and themeData then
themeData.baseUrl = 'nui://' .. resource .. '/'
themes[themeName] = themeData
end
end
end
end
SendNUIMessage({
type = 'ON_UPDATE_THEMES',
themes = themes
})
end
AddEventHandler('onClientResourceStart', function(resName)
Wait(500)
refreshCommands()
refreshThemes()
end)
AddEventHandler('onClientResourceStop', function(resName)
Wait(500)
refreshCommands()
refreshThemes()
end)
RegisterNUICallback('loaded', function(data, cb)
TriggerServerEvent('chat:init');
refreshCommands()
refreshThemes()
chatLoaded = true
cb('ok')
end)
@@ -171,5 +211,22 @@ Citizen.CreateThread(function()
chatInputActivating = false
end
end
if chatLoaded then
local shouldBeHidden = false
if IsScreenFadedOut() or IsPauseMenuActive() then
shouldBeHidden = true
end
if (shouldBeHidden and not chatHidden) or (not shouldBeHidden and chatHidden) then
chatHidden = shouldBeHidden
SendNUIMessage({
type = 'ON_SCREEN_STATE_CHANGE',
shouldHide = shouldBeHidden
})
end
end
end
end)

View File

@@ -6,6 +6,7 @@ window.APP = {
style: CONFIG.style,
showInput: false,
showWindow: false,
shouldHide: true,
backingSuggestions: [],
removedSuggestions: [],
templates: CONFIG.templates,
@@ -13,6 +14,8 @@ window.APP = {
messages: [],
oldMessages: [],
oldMessagesIndex: -1,
tplBackups: [],
msgTplBackups: []
};
},
destroyed() {
@@ -48,6 +51,9 @@ window.APP = {
},
},
methods: {
ON_SCREEN_STATE_CHANGE({ shouldHide }) {
this.shouldHide = shouldHide;
},
ON_OPEN() {
this.showInput = true;
this.showWindow = true;
@@ -91,6 +97,85 @@ window.APP = {
this.templates[template.id] = template.html;
}
},
ON_UPDATE_THEMES({ themes }) {
this.removeThemes();
this.setThemes(themes);
},
removeThemes() {
for (let i = 0; i < document.styleSheets.length; i++) {
const styleSheet = document.styleSheets[i];
const node = styleSheet.ownerNode;
if (node.getAttribute('data-theme')) {
node.parentNode.removeChild(node);
}
}
this.tplBackups.reverse();
for (const [ elem, oldData ] of this.tplBackups) {
elem.innerText = oldData;
}
this.tplBackups = [];
this.msgTplBackups.reverse();
for (const [ id, oldData ] of this.msgTplBackups) {
this.templates[id] = oldData;
}
this.msgTplBackups = [];
},
setThemes(themes) {
for (const [ id, data ] of Object.entries(themes)) {
if (data.style) {
const style = document.createElement('style');
style.type = 'text/css';
style.setAttribute('data-theme', id);
style.appendChild(document.createTextNode(data.style));
document.head.appendChild(style);
}
if (data.styleSheet) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = data.baseUrl + data.styleSheet;
link.setAttribute('data-theme', id);
document.head.appendChild(link);
}
if (data.templates) {
for (const [ tplId, tpl ] of Object.entries(data.templates)) {
const elem = document.getElementById(tplId);
if (elem) {
this.tplBackups.push([ elem, elem.innerText ]);
elem.innerText = tpl;
}
}
}
if (data.script) {
const script = document.createElement('script');
script.type = 'text/javascript';
script.src = data.baseUrl + data.script;
document.head.appendChild(script);
}
if (data.msgTemplates) {
for (const [ tplId, tpl ] of Object.entries(data.msgTemplates)) {
this.msgTplBackups.push([ tplId, this.templates[tplId] ]);
this.templates[tplId] = tpl;
}
}
}
},
warn(msg) {
this.messages.push({
args: [msg],

View File

@@ -103,7 +103,7 @@ textarea:focus, input:focus {
list-style-type: none;
padding: 0.5%;
padding-left: 1.4%;
font-size: 1.1rem;
font-size: 1.65vh;
box-sizing: border-box;
color: white;
background-color: rgba(44, 62, 80, 1.0);
@@ -121,3 +121,7 @@ textarea:focus, input:focus {
.suggestion {
margin-bottom: 0.5%;
}
.hidden {
display: none;
}

View File

@@ -18,7 +18,7 @@
<!-- App Template -->
<script type="text/x-template" id="app_template">
<div id="app">
<div class="chat-window" :style="this.style" :class="{ 'fadeOut animated': !showWindow }">
<div class="chat-window" :style="this.style" :class="{ 'fadeOut animated': !showWindow, 'hidden': shouldHide }">
<div class="chat-messages" ref="messages">
<message v-for="msg in messages"
:templates="templates"
@@ -32,17 +32,19 @@
</div>
</div>
<div class="chat-input" v-show="showInput">
<span class="prefix"></span>
<textarea v-model="message"
ref="input"
type="text"
autofocus
spellcheck="false"
@keyup.esc="hideInput"
@keyup="keyUp"
@keydown="keyDown"
@keypress.enter.prevent="send">
</textarea>
<div>
<span class="prefix"></span>
<textarea v-model="message"
ref="input"
type="text"
autofocus
spellcheck="false"
@keyup.esc="hideInput"
@keyup="keyUp"
@keydown="keyDown"
@keypress.enter.prevent="send">
</textarea>
</div>
<suggestions :message="message" :suggestions="suggestions">
</suggestions>
</div>
@@ -58,28 +60,30 @@
<!-- Suggestions Template -->
<script type="text/x-template" id="suggestions_template">
<ul class="suggestions" v-show="currentSuggestions.length > 0">
<li class="suggestion" v-for="s in currentSuggestions">
<p>
<span :class="{ 'disabled': s.disabled }">
{{s.name}}
</span>
<span class="param"
v-for="(p, index) in s.params"
:class="{ 'disabled': p.disabled }">
[{{p.name}}]
</span>
</p>
<small class="help">
<template v-if="!s.disabled">
{{s.help}}
</template>
<template v-for="p in s.params" v-if="!p.disabled">
{{p.help}}
</template>
</small>
</li>
</ul>
<div class="suggestions-wrap" v-show="currentSuggestions.length > 0">
<ul class="suggestions">
<li class="suggestion" v-for="s in currentSuggestions">
<p>
<span :class="{ 'disabled': s.disabled }">
{{s.name}}
</span>
<span class="param"
v-for="(p, index) in s.params"
:class="{ 'disabled': p.disabled }">
[{{p.name}}]
</span>
</p>
<small class="help">
<template v-if="!s.disabled">
{{s.help}}
</template>
<template v-for="p in s.params" v-if="!p.disabled">
{{p.help}}
</template>
</small>
</li>
</ul>
</div>
</script>
<!-- Scripts -->