Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display address information and history #33

Merged
merged 15 commits into from
Jan 29, 2024
1 change: 1 addition & 0 deletions env.example
Original file line number Diff line number Diff line change
Expand Up @@ -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=<foo>
Expand Down
3 changes: 3 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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,
})
Expand All @@ -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,
})
Expand Down
93 changes: 90 additions & 3 deletions public/full.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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';
Expand Down Expand Up @@ -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
Expand Down
88 changes: 86 additions & 2 deletions views/index.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/pako/2.1.0/pako.min.js"></script>

<!-- To style up the demo a little -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-wEmeIV1mKuiNpC+IOBjI7aAzPcEZeedi5yW5f2yOq55WWLwNGmvvx4Um1vskeMj0" crossorigin="anonymous" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css">

</head>
Expand Down Expand Up @@ -352,6 +352,88 @@
</div>
<hr />
</div>

<div class="modal fade" id="addressModal" tabindex="-1" aria-labelledby="addressModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-body">
<div class="container-fluid">

<div class="text-center address-info mt-3">
<div class="address-avatar-container">
<img src="https://i.pravatar.cc/[email protected]" alt="Avatar" class="rounded-circle avatar address-avatar">
</div>
<div class="row mt-1">
<div class="col-auto">
<span class="badge address-badge bg-primary me-2">
type
</span>
</div>
<div class="col-auto">
<h2 class="address-display-name">
First Last
</h2>
</div>
</div>
<div class="address-channels-buttons mt-3">
<button class="btn btn-secondary btn-address-dial-audio">
<i class="bi bi-phone"></i>
Call
</button>
<button class="btn btn-secondary btn-address-dial-video">
<i class="bi bi-camera-video"></i>
Video
</button>
<button class="btn btn-secondary btn-address-dial-messaging">
<i class="bi bi-chat"></i>
Chat
</button>
</div>
</div>

<div class="mt-4">
<h3 class="text-center">Recents</h3>
<div class="address-recent-messages">
<div class="address-recent-message shadow-sm p-2 mb-1 bg-body rounded">
<p class="card-text placeholder-glow">
<span class="placeholder placeholder-lg col-6"></span>
<span class="placeholder placeholder-lg col-12"></span>
</p>
</div>
<div class="address-recent-message shadow-sm p-2 mb-1 bg-body rounded">
<p class="card-text placeholder-glow">
<span class="placeholder placeholder-lg col-6"></span>
<span class="placeholder placeholder-lg col-12"></span>
</p>
</div>
<div class="address-recent-message shadow-sm p-2 mb-1 bg-body rounded">
<p class="card-text placeholder-glow">
<span class="placeholder placeholder-lg col-6"></span>
<span class="placeholder placeholder-lg col-12"></span>
</p>
</div>
<div class="address-recent-message shadow-sm p-2 mb-1 bg-body rounded">
<p class="card-text placeholder-glow">
<span class="placeholder placeholder-lg col-6"></span>
<span class="placeholder placeholder-lg col-12"></span>
</p>
</div>
<div class="address-recent-message shadow-sm p-2 mb-1 bg-body rounded">
<p class="card-text placeholder-glow">
<span class="placeholder placeholder-lg col-6"></span>
<span class="placeholder placeholder-lg col-12"></span>
</p>
</div>
</div>
</div>
</div>

</div>
</div>
</div>
</div>
</div>

<script type="module">
// Import Firebase libs
import { initializeApp } from 'https://www.gstatic.com/firebasejs/10.4.0/firebase-app.js'
Expand All @@ -367,8 +449,10 @@
<script type="text/javascript">
const _token = "<%= token %>";
const _host = "<%= host %>";
const _fabricApiUrl = "<%= fabricApiUrl %>";
const _firebaseConfig = <%- firebaseConfig %>;
</script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
<script type="text/javascript" src="https://unpkg.com/@signalwire/js@dev"></script>
<script src="/full.js"></script>
</body>
Expand Down
1 change: 1 addition & 0 deletions views/minimal.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
<script type="text/javascript">
const _token = "<%= token %>";
const _host = "<%= host %>";
const _fabricApiUrl = "<%= fabricApiUrl %>";
const _firebaseConfig = <%- firebaseConfig %>;
</script>
<script type="text/javascript" src="https://unpkg.com/@signalwire/js@dev"></script>
Expand Down