|
|
|
|
@@ -1,162 +1,268 @@
|
|
|
|
|
/* Vodia Outbound Identity */
|
|
|
|
|
document.body.insertAdjacentHTML('beforeend', `
|
|
|
|
|
<template id="vodia-identity-template">
|
|
|
|
|
<li id="vodia-identity-container" class="dropdown notification-list ml-2 my-auto" style="display: none;">
|
|
|
|
|
<a href="#" class="nav-link nav-user dropdown-toggle d-flex align-items-center" aria-haspopup="true" aria-expanded="false" data-ref="toggleButton">
|
|
|
|
|
<i class="phone-icon fas fa-phone" data-ref="phoneIcon"></i>
|
|
|
|
|
<div class="pro-user-name ml-2">
|
|
|
|
|
<div style="line-height: 1.2;">
|
|
|
|
|
<span>Ausgehende Identität: </span>
|
|
|
|
|
<span class="font-weight-bold" data-ref="currentName"></span>
|
|
|
|
|
<i class="far fa-chevron-down ml-2"></i>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="small opacity-75" data-ref="currentNumber"></div>
|
|
|
|
|
</div>
|
|
|
|
|
</a>
|
|
|
|
|
<div class="dropdown-menu dropdown-menu-right shadow-lg" style="min-width: 320px;" data-ref="dropdownMenu">
|
|
|
|
|
<div class="dropdown-item noti-title"><h6 class="m-0" data-ref="dropdownTitle"></h6></div>
|
|
|
|
|
<ul class="list-group list-group-flush" data-ref="identityList"></ul>
|
|
|
|
|
</div>
|
|
|
|
|
</li>
|
|
|
|
|
</template>
|
|
|
|
|
<template id="vodia-list-item-template">
|
|
|
|
|
<li class="list-group-item list-group-item-action d-flex align-items-center">
|
|
|
|
|
<i class="vodia-list-item-icon fas fa-circle mr-3" data-ref="colorBlock"></i>
|
|
|
|
|
<div>
|
|
|
|
|
<span class="name font-weight-bold d-block" data-ref="name"></span>
|
|
|
|
|
<span class="number small text-muted" data-ref="numberDisplay"></span>
|
|
|
|
|
</div>
|
|
|
|
|
</li>
|
|
|
|
|
</template>
|
|
|
|
|
`);
|
|
|
|
|
|
|
|
|
|
var vodia_identity_switcher_timer;
|
|
|
|
|
var vodia_identity = null;
|
|
|
|
|
class VodiaIdentitySwitcher {
|
|
|
|
|
API_BASE_URL = window.baseurl || '/';
|
|
|
|
|
POLLING_INTERVAL_MS = 30000;
|
|
|
|
|
CACHE_DURATION_MS = 30000;
|
|
|
|
|
CACHE_KEY = 'vodiaIdentityCache'; // Key for localStorage
|
|
|
|
|
|
|
|
|
|
getVodiaIdentity();
|
|
|
|
|
TEXT = {
|
|
|
|
|
checking: "Prüfe...",
|
|
|
|
|
setting: "Ändere...",
|
|
|
|
|
dropdownTitle: "Ausgehende Identität wählen:",
|
|
|
|
|
ownExtension: "Eigene Nummer",
|
|
|
|
|
customIdentity: "Andere Nummer"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
async function getVodiaIdentity() {
|
|
|
|
|
clearTimeout(vodia_identity_switcher_timer);
|
|
|
|
|
|
|
|
|
|
if(!$("#vodia-identity-switcher .phone-icon").hasClass("fa-spin")) {
|
|
|
|
|
$("#vodia-identity-switcher .phone-icon").addClass("fa-spin text-warning");
|
|
|
|
|
pollingTimer = null;
|
|
|
|
|
elements = {};
|
|
|
|
|
templates = {};
|
|
|
|
|
|
|
|
|
|
constructor(parentElement) {
|
|
|
|
|
if (!parentElement) return;
|
|
|
|
|
|
|
|
|
|
this.templates.main = document.getElementById('vodia-identity-template');
|
|
|
|
|
this.templates.listItem = document.getElementById('vodia-list-item-template');
|
|
|
|
|
|
|
|
|
|
if (!this.templates.main || !this.templates.listItem) {
|
|
|
|
|
return console.error("Vodia Switcher Error: Required HTML <template> tags not found.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this._createSwitcherUI(parentElement);
|
|
|
|
|
this._addEventListeners();
|
|
|
|
|
this.getVodiaIdentity();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var response = await fetch(baseurl + 'User/Api/do=getVodiaIdentity');
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
$("#vodia-identity-switcher a.phone-icon i").removeClass("fa-spin");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
var resp_json = await response.json();
|
|
|
|
|
if(resp_json.status != "OK") {
|
|
|
|
|
console.error("Error getting Vodia Identity: " + resp_json.message);
|
|
|
|
|
$("#vodia-identity-switcher a.phone-icon i").removeClass("fa-spin");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
vodia_identity = resp_json.result;
|
|
|
|
|
|
|
|
|
|
if(!("enabled" in vodia_identity) || !vodia_identity.enabled) {
|
|
|
|
|
$("#vodia-identity-switcher a.phone-icon i").removeClass("fa-spin");
|
|
|
|
|
return true;
|
|
|
|
|
_getCache() {
|
|
|
|
|
const cachedString = localStorage.getItem(this.CACHE_KEY);
|
|
|
|
|
if (!cachedString) return null;
|
|
|
|
|
try {
|
|
|
|
|
return JSON.parse(cachedString);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error("Vodia Cache Error: Could not parse cached data.", e);
|
|
|
|
|
// Clean up corrupted data
|
|
|
|
|
localStorage.removeItem(this.CACHE_KEY);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var default_ident = vodia_identity.default;
|
|
|
|
|
var default_ident_number = vodia_identity.default_number.replaceAll(' ', "");
|
|
|
|
|
_setCache(vodiaState) {
|
|
|
|
|
const itemToCache = { data: vodiaState, timestamp: Date.now() };
|
|
|
|
|
try {
|
|
|
|
|
localStorage.setItem(this.CACHE_KEY, JSON.stringify(itemToCache));
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error("Vodia Cache Error: Could not write to localStorage.", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var current_ident = vodia_identity.current;
|
|
|
|
|
var current_ident_number = current_ident.replaceAll(' ', "");
|
|
|
|
|
_createSwitcherUI(parentElement) {
|
|
|
|
|
const fragment = this.templates.main.content.cloneNode(true);
|
|
|
|
|
const container = fragment.querySelector('#vodia-identity-container');
|
|
|
|
|
|
|
|
|
|
var identities = vodia_identity.identities;
|
|
|
|
|
this.elements = {
|
|
|
|
|
container,
|
|
|
|
|
toggleButton: container.querySelector('[data-ref="toggleButton"]'),
|
|
|
|
|
phoneIcon: container.querySelector('[data-ref="phoneIcon"]'),
|
|
|
|
|
currentName: container.querySelector('[data-ref="currentName"]'),
|
|
|
|
|
currentNumber: container.querySelector('[data-ref="currentNumber"]'),
|
|
|
|
|
dropdownMenu: container.querySelector('[data-ref="dropdownMenu"]'),
|
|
|
|
|
identityList: container.querySelector('[data-ref="identityList"]'),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$("#vodia-identity-container").show();
|
|
|
|
|
this.elements.dropdownMenu.querySelector('[data-ref="dropdownTitle"]').textContent = this.TEXT.dropdownTitle;
|
|
|
|
|
parentElement.prepend(fragment);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(default_ident_number == current_ident_number) {
|
|
|
|
|
$("#vodia-identity-current-text").text("Xinon Eigene Durchwahl");
|
|
|
|
|
$("#vodia-identity-current-number").text("(" + default_ident + ")");
|
|
|
|
|
$("#vodia-identity-switcher").attr("title", "Xinon Eigene Durchwahl (" + default_ident + ")");
|
|
|
|
|
} else {
|
|
|
|
|
ident_found = false;
|
|
|
|
|
for(const [ident_name, ident] of Object.entries(identities)) {
|
|
|
|
|
if(ident.number == current_ident_number) {
|
|
|
|
|
ident_found = true;
|
|
|
|
|
$("#vodia-identity-current-text").text(ident_name);
|
|
|
|
|
$("#vodia-identity-current-number").text("(" + ident.display + ")");
|
|
|
|
|
$("#vodia-identity-switcher").attr("title", ident_name + " (" + ident.display + ")");
|
|
|
|
|
_addEventListeners() {
|
|
|
|
|
this.elements.toggleButton.addEventListener('click', e => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
const isShown = this.elements.container.classList.toggle('show');
|
|
|
|
|
this.elements.toggleButton.setAttribute('aria-expanded', isShown);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.addEventListener('click', e => {
|
|
|
|
|
if (!this.elements.container.contains(e.target)) {
|
|
|
|
|
this.elements.container.classList.remove('show');
|
|
|
|
|
this.elements.toggleButton.setAttribute('aria-expanded', 'false');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(!ident_found) {
|
|
|
|
|
$("#vodia-identity-current-number").text(vodia_identity.current);
|
|
|
|
|
$("#vodia-identity-switcher").attr("title", vodia_identity.current);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$("#vodia-identities-list").empty();
|
|
|
|
|
|
|
|
|
|
let item = addVodiaIdentityListItem(
|
|
|
|
|
default_ident_number,
|
|
|
|
|
default_ident_number,
|
|
|
|
|
"Xinon Eigene Durchwahl",
|
|
|
|
|
"blue"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
console.log(default_ident_number, current_ident_number);
|
|
|
|
|
if(default_ident_number == current_ident_number) {
|
|
|
|
|
$(item).addClass("bg-info text-white");
|
|
|
|
|
$(item).find(".text-block").addClass("text-white");
|
|
|
|
|
$(item).removeClass("pointer");
|
|
|
|
|
} else {
|
|
|
|
|
$(item).click(function () {
|
|
|
|
|
setVodiaOutboundIdentity(default_ident_number)
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_setLoadingState(isLoading, message = '') {
|
|
|
|
|
const { phoneIcon, currentName, currentNumber } = this.elements;
|
|
|
|
|
phoneIcon.className = 'phone-icon fas fa-phone'; // Reset
|
|
|
|
|
|
|
|
|
|
for(const [ident_name, ident] of Object.entries(identities)) {
|
|
|
|
|
|
|
|
|
|
let item = addVodiaIdentityListItem(
|
|
|
|
|
ident.number,
|
|
|
|
|
ident.display,
|
|
|
|
|
ident_name,
|
|
|
|
|
ident.color
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if(ident.number == current_ident_number) {
|
|
|
|
|
$(item).addClass("bg-info text-white");
|
|
|
|
|
$(item).find(".text-block").addClass("text-white");
|
|
|
|
|
$(item).removeClass("pointer");
|
|
|
|
|
if (isLoading) {
|
|
|
|
|
phoneIcon.classList.add('fa-spin', 'text-warning');
|
|
|
|
|
currentName.textContent = message;
|
|
|
|
|
currentNumber.textContent = '';
|
|
|
|
|
} else {
|
|
|
|
|
$(item).click(function () {
|
|
|
|
|
setVodiaOutboundIdentity(ident.number)
|
|
|
|
|
});
|
|
|
|
|
phoneIcon.classList.add('text-success');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$("#vodia-identity-switcher .phone-icon").removeClass("text-warning fa-spin");
|
|
|
|
|
$("#vodia-identity-switcher .phone-icon").addClass("fa-bounce");
|
|
|
|
|
setTimeout(() => { $("#vodia-identity-switcher .phone-icon").removeClass("fa-bounce"); }, 800);
|
|
|
|
|
async getVodiaIdentity() {
|
|
|
|
|
clearTimeout(this.pollingTimer);
|
|
|
|
|
|
|
|
|
|
vodia_identity_switcher_timer = setTimeout(() => {getVodiaIdentity()}, 30000); //
|
|
|
|
|
try {
|
|
|
|
|
const cachedItem = this._getCache();
|
|
|
|
|
let vodiaState;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
if (cachedItem && (Date.now() - cachedItem.timestamp < this.CACHE_DURATION_MS)) {
|
|
|
|
|
vodiaState = cachedItem.data;
|
|
|
|
|
} else {
|
|
|
|
|
this._setLoadingState(true, this.TEXT.checking);
|
|
|
|
|
const response = await fetch(`${this.API_BASE_URL}User/Api/do=getVodiaIdentity`);
|
|
|
|
|
if (!response.ok) throw new Error('Network response was not ok');
|
|
|
|
|
|
|
|
|
|
function addVodiaIdentityListItem(number, number_display, name, color) {
|
|
|
|
|
//let tpl = $("#vodia-identity-list-item-template")[0].innerHTML;
|
|
|
|
|
//item = $($.parseXML(tpl)).contents();
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
if (data.status !== "OK") throw new Error(data.message || 'API returned an error');
|
|
|
|
|
|
|
|
|
|
vodiaState = data.result;
|
|
|
|
|
this._setCache(vodiaState);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const tpl = document.querySelector("#vodia-identity-list-item-template").content.cloneNode(true);
|
|
|
|
|
const item = tpl.querySelector('li');
|
|
|
|
|
|
|
|
|
|
//console.log(item);
|
|
|
|
|
|
|
|
|
|
$(item).data("number", number);
|
|
|
|
|
|
|
|
|
|
$(item).find("td.color-block").addClass("color-block-" + color);
|
|
|
|
|
$(item).find("span.name").text(name);
|
|
|
|
|
$(item).find("span.number").text("(" + number_display + ")");
|
|
|
|
|
|
|
|
|
|
$("#vodia-identities-list").append(item);
|
|
|
|
|
|
|
|
|
|
return item;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function setVodiaOutboundIdentity(number) {
|
|
|
|
|
$("#vodia-identity-switcher .phone-icon").addClass("fa-spin text-warning");
|
|
|
|
|
|
|
|
|
|
$("#vodia-identities-list").empty();
|
|
|
|
|
$("#vodia-identities-list").append("<img src='" + baseurl + "img/ajax-loader.gif' />");
|
|
|
|
|
|
|
|
|
|
$("#vodia-identity-current-text").text("Identität wird gesetzt...");
|
|
|
|
|
$("#vodia-identity-current-number").text("");
|
|
|
|
|
$("#vodia-identity-switcher").attr("title", "Identität wird gesetzt...");
|
|
|
|
|
|
|
|
|
|
var response = await fetch(baseurl + 'User/Api/do=setVodiaIdentity',{
|
|
|
|
|
method: 'POST',
|
|
|
|
|
body: new URLSearchParams({
|
|
|
|
|
'number': number
|
|
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
var resp_json = await response.json();
|
|
|
|
|
if(resp_json.status != "OK") {
|
|
|
|
|
console.error("Error setting Vodia Identity: " + resp_json.message);
|
|
|
|
|
notify.error("Fehler beim Setzen der Identität!");
|
|
|
|
|
if (vodiaState?.enabled) {
|
|
|
|
|
this.elements.container.style.display = 'list-item';
|
|
|
|
|
this._updateUI(vodiaState);
|
|
|
|
|
this._setLoadingState(false);
|
|
|
|
|
} else {
|
|
|
|
|
this.elements.container.style.display = 'none';
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Vodia Fetch Error:", error.message);
|
|
|
|
|
this.elements.phoneIcon.className = 'phone-icon fas fa-phone text-danger';
|
|
|
|
|
this.elements.currentName.textContent = 'Fehler';
|
|
|
|
|
} finally {
|
|
|
|
|
this.pollingTimer = setTimeout(() => this.getVodiaIdentity(), this.POLLING_INTERVAL_MS);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getVodiaIdentity();
|
|
|
|
|
async setVodiaOutboundIdentity(number) {
|
|
|
|
|
this._setLoadingState(true, this.TEXT.setting);
|
|
|
|
|
this.elements.container.classList.remove('show');
|
|
|
|
|
this.elements.toggleButton.setAttribute('aria-expanded', 'false');
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch(`${this.API_BASE_URL}User/Api/do=setVodiaIdentity`, {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
|
|
|
body: new URLSearchParams({ 'number': number })
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!response.ok) throw new Error("Network response was not ok.");
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
if (data.status !== "OK") throw new Error(data.message || 'API returned an error.');
|
|
|
|
|
|
|
|
|
|
localStorage.removeItem(this.CACHE_KEY)
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Vodia Set Error:", error.message);
|
|
|
|
|
if (window.notify) window.notify.error("Fehler beim Ändern der ID!");
|
|
|
|
|
} finally {
|
|
|
|
|
await this.getVodiaIdentity();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_updateUI(vodiaState) {
|
|
|
|
|
const { 'default': defaultDisplay, default_number, current, identities } = vodiaState;
|
|
|
|
|
const currentId = current.replaceAll(' ', "");
|
|
|
|
|
|
|
|
|
|
let activeName = this.TEXT.customIdentity;
|
|
|
|
|
let activeNumberDisplay = `(${current})`;
|
|
|
|
|
|
|
|
|
|
if (currentId === default_number.replaceAll(' ', "")) {
|
|
|
|
|
activeName = this.TEXT.ownExtension;
|
|
|
|
|
activeNumberDisplay = `(${defaultDisplay})`;
|
|
|
|
|
} else if (identities) {
|
|
|
|
|
const found = Object.entries(identities).find(([, ident]) => ident.number === currentId);
|
|
|
|
|
if (found) {
|
|
|
|
|
[activeName, activeNumberDisplay] = [found[0], `(${found[1].display})`];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.elements.currentName.textContent = activeName;
|
|
|
|
|
this.elements.currentNumber.textContent = activeNumberDisplay;
|
|
|
|
|
this.elements.toggleButton.title = `Aktive ID: ${activeName} ${activeNumberDisplay}`;
|
|
|
|
|
|
|
|
|
|
this._renderIdentityList(vodiaState);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_renderIdentityList(vodiaState) {
|
|
|
|
|
this.elements.identityList.innerHTML = '';
|
|
|
|
|
const { 'default': defaultDisplay, default_number, current, identities } = vodiaState;
|
|
|
|
|
const currentId = current.replaceAll(' ', "");
|
|
|
|
|
|
|
|
|
|
// Add "Own Extension"
|
|
|
|
|
this.elements.identityList.appendChild(this._createListItem({
|
|
|
|
|
name: this.TEXT.ownExtension,
|
|
|
|
|
number: default_number.replaceAll(' ', ""),
|
|
|
|
|
display: defaultDisplay,
|
|
|
|
|
color: 'blue',
|
|
|
|
|
isActive: currentId === default_number.replaceAll(' ', "")
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// Add other identities
|
|
|
|
|
if (!identities) return;
|
|
|
|
|
for (const name in identities) {
|
|
|
|
|
const ident = identities[name];
|
|
|
|
|
this.elements.identityList.appendChild(this._createListItem({
|
|
|
|
|
name,
|
|
|
|
|
number: ident.number,
|
|
|
|
|
display: ident.display,
|
|
|
|
|
color: ident.color,
|
|
|
|
|
isActive: currentId === ident.number
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_createListItem({ name, number, display, color, isActive }) {
|
|
|
|
|
const fragment = this.templates.listItem.content.cloneNode(true);
|
|
|
|
|
const item = fragment.querySelector('li');
|
|
|
|
|
|
|
|
|
|
item.querySelector('[data-ref="colorBlock"]').classList.add(`vodia-identity-color-${color || 'grey'}`);
|
|
|
|
|
item.querySelector('[data-ref="name"]').textContent = name;
|
|
|
|
|
item.querySelector('[data-ref="numberDisplay"]').textContent = display;
|
|
|
|
|
|
|
|
|
|
if (isActive) {
|
|
|
|
|
item.classList.add('active');
|
|
|
|
|
item.querySelector('[data-ref="numberDisplay"]').classList.remove('text-muted');
|
|
|
|
|
} else {
|
|
|
|
|
item.classList.add('pointer');
|
|
|
|
|
item.addEventListener('click', () => this.setVodiaOutboundIdentity(number));
|
|
|
|
|
}
|
|
|
|
|
return item;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
|
|
|
const topbar = document.querySelector("#topbar");
|
|
|
|
|
if (topbar) new VodiaIdentitySwitcher(topbar);
|
|
|
|
|
});
|