Skip to content

Commit

Permalink
CF SDK: Allow filtering with Address API (#948)
Browse files Browse the repository at this point in the history
  • Loading branch information
iAmmar7 authored Jan 19, 2024
1 parent cdb4b20 commit 6cb639b
Show file tree
Hide file tree
Showing 6 changed files with 227 additions and 42 deletions.
14 changes: 14 additions & 0 deletions .changeset/dry-queens-tap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
'@signalwire/core': patch
'@signalwire/js': patch
---

Allow user to pass filters to `getAddress` function


```js
const addressData = await client.getAddresses({
type: 'room',
displayName: 'domain app',
})
```
40 changes: 40 additions & 0 deletions internal/playground-js/src/fabric-http/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,47 @@ <h5>Client</h5>

<div class="col-12 col-md-8 mt-4 mt-md-1">
<div class="card">
<!-- Filters -->
<div class="input-group mt-1 px-2">
<input
type="text"
class="form-control"
placeholder="Search..."
aria-label="Search"
aria-describedby="searchButton"
id="searchInput"
/>
<select class="form-select" id="searchType">
<option value="all" selected>All</option>
<option value="app">App</option>
<option value="room">Room</option>
<option value="subscriber">Subscriber</option>
</select>
</div>
<!-- Filters end -->

<div class="card-body" id="addresses"></div>

<!-- Pagination -->
<div class="d-flex justify-content-center pb-2 gap-2">
<button
name="fetch-next-address"
type="button"
class="btn btn-light btn-sm"
onclick="fetchPrevAddresses()"
>
Prev
</button>
<button
name="fetch-prev-address"
type="button"
class="btn btn-light btn-sm"
onclick="fetchNextAddresses()"
>
Next
</button>
</div>
<!-- Pagination end -->
</div>
</div>
</div>
Expand Down
145 changes: 110 additions & 35 deletions internal/playground-js/src/fabric-http/index.js
Original file line number Diff line number Diff line change
@@ -1,44 +1,29 @@
import { SWClient } from '@signalwire/js'
import { SignalWire } from '@signalwire/js'

const searchInput = document.getElementById('searchInput')
const searchType = document.getElementById('searchType')

let client = null

async function getClient() {
if (!client) {
client = await SignalWire({
host: document.getElementById('host').value,
token: document.getElementById('token').value,
})
}

return client
}

let __client = null
/**
* Connect with Relay creating a client and attaching all the event handler.
*/
window.connect = async () => {
__client = new SWClient({
httpHost: document.getElementById('host').value,
accessToken: document.getElementById('token').value,
})

const addressesDiv = document.getElementById('addresses')
addressesDiv.innerHTML = ''
try {
const { addresses, nextPage, prevPage } = await __client.getAddresses()
const list = addresses.map((address) => {
return `<li class="list-group-item">
<b>${address.display_name}</b> / <span>${
address.name
} </span> <span class="badge bg-primary float-end">${address.type}</span>
<ul class="list-group list-group-flush">
${Object.keys(address.channels)
.map((c) => {
return `<li class="list-group-item">${address.channels[c]}</li>`
})
.join('')}
</ul>
</li>`
})
console.log('addresses', addresses, list)
const client = await getClient()
window.__client = client

addressesDiv.insertAdjacentHTML('beforeend', list.join(''))
} catch (error) {
console.error('Client Error', error)
alert('Error - Double check host and token')
}

// Navigate throught pages
// const next = await nextPage()
// const prev = await prevPage()
await fetchAddresses()
}

window.saveInLocalStorage = (e) => {
Expand Down Expand Up @@ -83,3 +68,93 @@ window.ready(async function () {
document.getElementById('token').value =
localStorage.getItem('fabric.http.token') || ''
})

const escapeHTML = (str) => {
const div = document.createElement('div')
div.textContent = str
return div.innerHTML
}

function updateAddressUI() {
const addressesDiv = document.getElementById('addresses')
addressesDiv.innerHTML = ''
const { addresses } = window.__addressData

const createListItem = (address) => {
const displayName = escapeHTML(address.display_name)
const name = escapeHTML(address.name)
const type = escapeHTML(address.type)

const dialList = document.createElement('ul')
dialList.className = 'list-group list-group-flush'

const listItem = document.createElement('li')
listItem.className = 'list-group-item'
listItem.innerHTML = `<b>${displayName}</b> / <span>${name}</span>
<span class="badge bg-primary float-end">${type}</span>`

listItem.appendChild(dialList)

Object.entries(address.channels).forEach(([channelName, channelValue]) => {
const sanitizedValue = escapeHTML(channelValue)
const li = document.createElement('li')
li.className = 'list-group-item d-flex align-items-center gap-2'
li.innerHTML = `<span>${sanitizedValue}</span>`
dialList.appendChild(li)
})

return listItem
}

addresses
.map(createListItem)
.forEach((item) => addressesDiv.appendChild(item))
}

async function fetchAddresses() {
if (!client) return
try {
const searchText = document.getElementById('searchInput').value
const selectedType = document.getElementById('searchType').value

const addressData = await client.getAddresses({
type: selectedType === 'all' ? undefined : selectedType,
displayName: !searchText.length ? undefined : searchText,
})
window.__addressData = addressData
updateAddressUI()
} catch (error) {
console.error('Unable to fetch addresses', error)
}
}

window.fetchNextAddresses = async () => {
const { nextPage } = window.__addressData
try {
const nextAddresses = await nextPage()
window.__addressData = nextAddresses
updateAddressUI()
} catch (error) {
console.error('Unable to fetch next addresses', error)
}
}

window.fetchPrevAddresses = async () => {
const { prevPage } = window.__addressData
try {
const prevAddresses = await prevPage()
window.__addressData = prevAddresses
updateAddressUI()
} catch (error) {
console.error('Unable to fetch prev addresses', error)
}
}

let debounceTimeout
searchInput.addEventListener('input', () => {
clearTimeout(debounceTimeout)
// Search after 1 seconds when user stops typing
debounceTimeout = setTimeout(fetchAddresses, 1000)
})

searchType.addEventListener('change', fetchAddresses)
28 changes: 28 additions & 0 deletions packages/core/src/types/callfabric.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export interface FetchAddressResponse {
data:
| Array<{
display_name: string
name: string
preview_url?: string
cover_url?: string
resource_id: string
type: string
channels: {
audio?: string
messaging?: string
video?: string
}
}>
| []
links: {
first: string
self: string
next?: string
prev?: string
}
}

export interface GetAddressesOptions {
type?: string
displayName?: string
}
1 change: 1 addition & 0 deletions packages/core/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,4 @@ export * from './pubSub'
export * from './task'
export * from './messaging'
export * from './voice'
export * from './callfabric'
41 changes: 34 additions & 7 deletions packages/js/src/fabric/HTTPClient.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { type UserOptions } from '@signalwire/core'
import {
FetchAddressResponse,
GetAddressesOptions,
type UserOptions,
} from '@signalwire/core'
import { createHttpClient } from './createHttpClient'

interface RegisterDeviceParams {
Expand Down Expand Up @@ -27,16 +31,33 @@ export class HTTPClient {
return `fabric.${host.split('.').splice(1).join('.')}`
}

public async getAddresses() {
const path = '/addresses' as const
const { body } = await this.httpClient<any>(path)
public async getAddresses(options?: GetAddressesOptions) {
const { type, displayName } = options || {}

let path = '/addresses' as const

if (type || displayName) {
const queryParams = new URLSearchParams()

if (type) {
queryParams.append('type', type)
}

if (displayName) {
queryParams.append('display_name', displayName)
}

path += `?${queryParams.toString()}`
}

const { body } = await this.httpClient<FetchAddressResponse>(path)

const anotherPage = async (url: string) => {
const { search } = new URL(url)
const { body } = await this.httpClient<any>(`${path}${search}`)
const { body } = await this.httpClient<FetchAddressResponse>(url)
return buildResult(body)
}

const buildResult = (body: any) => {
const buildResult = (body: FetchAddressResponse) => {
return {
addresses: body.data,
nextPage: async () => {
Expand All @@ -47,6 +68,12 @@ export class HTTPClient {
const { prev } = body.links
return prev ? anotherPage(prev) : undefined
},
firstPage: async () => {
const { first } = body.links
return first ? anotherPage(first) : undefined
},
hasNext: Boolean(body.links.next),
hasPrev: Boolean(body.links.prev),
}
}

Expand Down

0 comments on commit 6cb639b

Please sign in to comment.