diff --git a/env.example b/env.example index b30366f..59a70cb 100644 --- a/env.example +++ b/env.example @@ -2,6 +2,7 @@ SIGNALWIRE_PROJECT_KEY=YOURPROJECTKEY SIGNALWIRE_TOKEN=YOURTOKEN SIGNALWIRE_SPACE=YOURSPACE.signalwire.com DEFAULT_DESTINATION=SOMETARGET +SIGNALWIRE_FABRIC_API_URL=https://fabric.swire.io/api #Firebase Initilization Params FIREBASE_API_KEY= diff --git a/index.js b/index.js index 3d05f4f..4c1ebf4 100644 --- a/index.js +++ b/index.js @@ -29,6 +29,7 @@ const FIREBASE_CONFIG = JSON.stringify({ }) const host = process.env.RELAY_HOST +const fabricApiUrl = process.env.SIGNALWIRE_FABRIC_API_URL async function apiRequest(uri, options) { const response = await fetch(uri, options) @@ -97,6 +98,7 @@ app.get('/', async (req, res) => { res.render('index', { host, token: token, + fabricApiUrl: fabricApiUrl, destination: process.env.DEFAULT_DESTINATION, firebaseConfig: FIREBASE_CONFIG, }) @@ -111,6 +113,7 @@ app.get('/minimal', async (req, res) => { res.render('minimal', { host, token: token, + fabricApiUrl: fabricApiUrl, destination: process.env.DEFAULT_DESTINATION, firebaseConfig: FIREBASE_CONFIG, }) diff --git a/public/full.js b/public/full.js index 950032f..e4f9ebb 100644 --- a/public/full.js +++ b/public/full.js @@ -876,6 +876,66 @@ const escapeHTML = (str) => { return div.innerHTML } +function isBlank(str) { + return str === null || str === undefined || str === '' || str === 'null'; +} + +function setupAddressModal() { + const addressModal = document.getElementById('addressModal') + if (!addressModal) return + + addressModal.addEventListener('show.bs.modal', event => { + const button = event.relatedTarget + const addressName = button.getAttribute('data-bs-name') + const address = __addressData.addresses.find(address => address.name === addressName) + updateAddressModal(address) + + // TODO: load recent conversations associated with address + // messages = await fetchConversationHistory(__subscriberId, address.id) + // renderConversationHistory(messages) + }) + + addressModal.addEventListener('hidden.bs.modal', event => { + updateAddressModal({name:'',display_name:'',resouce_id:null,cover_url:null,preview_url:null,type:null,channels: []}) + }) +} + +function updateAddressModal(address) { + const addressModal = document.getElementById('addressModal') + if (!addressModal) return + + const addressDisplayName = addressModal.querySelector('.modal-body .address-display-name') + const addressAvatar = addressModal.querySelector('.modal-body .address-avatar') + const addressBadge = addressModal.querySelector('.modal-body .address-badge') + const channelButtons = { + audio: addressModal.querySelector('.modal-body .btn-address-dial-audio'), + video: addressModal.querySelector('.modal-body .btn-address-dial-video'), + messaging: addressModal.querySelector('.modal-body .btn-address-dial-messaging') + }; + + addressDisplayName.textContent = address.display_name + addressBadge.textContent = address.type + addressAvatar.src = address.cover_url || address.preview_url || `https://i.pravatar.cc/125?u=${address.resource_id}` + + // disable all channel buttons + for (let channelButton in channelButtons) { + channelButtons[channelButton].disabled = true + } + + // re-enable appropriate channel buttons + Object.entries(address.channels).forEach(([channelName, channelValue]) => { + let button = channelButtons[channelName] + let clone = button.cloneNode(true) + clone.disabled = false + button.parentNode.replaceChild(clone, button) + clone.addEventListener('click', () => { + dialAddress(channelValue) + const addressModalInstance = bootstrap.Modal.getInstance(addressModal) + addressModalInstance.hide() + }) + }) +} + function updateAddressUI() { addressesCard.classList.remove('d-none') const addressesDiv = document.getElementById('addresses') @@ -911,9 +971,13 @@ function updateAddressUI() { badge.textContent = type; col1.appendChild(badge); - const b = document.createElement('b'); - b.textContent = displayName; - col1.appendChild(b); + const addressNameLink = document.createElement('a'); + addressNameLink.textContent = displayName; + addressNameLink.href = '#'; + addressNameLink.dataset.bsToggle = 'modal'; + addressNameLink.dataset.bsTarget = '#addressModal'; + addressNameLink.dataset.bsName = address.name; + col1.appendChild(addressNameLink); const col2 = document.createElement('div'); col2.className = 'col'; @@ -965,11 +1029,34 @@ async function fetchAddresses() { }) window.__addressData = addressData updateAddressUI() + setupAddressModal() } catch (error) { console.error('Unable to fetch addresses', error) } } +// Just a placeholder until ready. We can prepare `client` methods as well +async function fetchConversationHistory(subscriberId, addressId) { + const queryParams = new URLSearchParams({ + subscriber_id: subscriberId, + address_id: addressId, + limit: 10, + }) + + const response = await fetch(`${_fabricApiUrl}/conversations?${queryParams}`, { + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${_token}` + } + }) + + if (!response.ok) { + throw new Error('Unable to fetch conversation history') + } + + return await response.json() +} + window.dialAddress = async (address) => { const destinationInput = document.getElementById('destination') destinationInput.value = address diff --git a/views/index.ejs b/views/index.ejs index d3d781d..fd7bb04 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -11,8 +11,8 @@ - + @@ -352,6 +352,88 @@
+ + + + + diff --git a/views/minimal.ejs b/views/minimal.ejs index db6dd6d..e7f3f88 100644 --- a/views/minimal.ejs +++ b/views/minimal.ejs @@ -66,6 +66,7 @@