-
-
Notifications
You must be signed in to change notification settings - Fork 776
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add an occupants filter to the MUC sidebar
Adds a new component `converse-contacts-filter` which is used to filter both roster contacts as well as MUC occupants.
- Loading branch information
Showing
17 changed files
with
380 additions
and
163 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,6 +28,7 @@ const api = { | |
...promise_api, | ||
|
||
disco: null, | ||
elements: null, | ||
}; | ||
|
||
export default api; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,51 @@ | ||
import 'shared/components/contacts-filter.js'; | ||
import tplOccupant from "./occupant.js"; | ||
import tplOccupantsFilter from './occupants-filter.js'; | ||
import { __ } from 'i18n'; | ||
import { html } from "lit"; | ||
import { repeat } from 'lit/directives/repeat.js'; | ||
|
||
function isOccupantFiltered (el, occ) { | ||
const type = el.filter.get('filter_type'); | ||
const q = (type === 'state') ? | ||
el.filter.get('chat_state').toLowerCase() : | ||
el.filter.get('filter_text').toLowerCase(); | ||
|
||
export default (o) => { | ||
if (!q) return false; | ||
|
||
if (type === 'state') { | ||
const show = occ.get('show'); | ||
return q === 'online' ? ["offline", "unavailable"].includes(show) : !show.includes(q); | ||
} else if (type === 'contacts') { | ||
return !occ.getDisplayName().toLowerCase().includes(q); | ||
} | ||
} | ||
|
||
function shouldShowOccupant (el, occ, o) { | ||
return isOccupantFiltered(el, occ) ? '' : tplOccupant(occ, o); | ||
} | ||
|
||
export default (el, o) => { | ||
const i18n_participants = o.occupants.length === 1 ? __('Participant') : __('Participants'); | ||
return html` | ||
<div class="occupants-header"> | ||
<div class="occupants-header--title"> | ||
<span class="occupants-heading">${o.occupants.length} ${i18n_participants}</span> | ||
<i class="hide-occupants" @click=${o.closeSidebar}> | ||
<i class="hide-occupants" @click=${ev => el.closeSidebar(ev)}> | ||
<converse-icon class="fa fa-times" size="1em"></converse-icon> | ||
</i> | ||
</div> | ||
</div> | ||
<div class="dragresize dragresize-occupants-left"></div> | ||
<ul class="occupant-list">${ repeat(o.occupants, (occ) => occ.get('jid'), (occ) => tplOccupant(occ, o)) }</ul> | ||
<ul class="occupant-list"> | ||
<converse-contacts-filter | ||
@update=${() => el.requestUpdate()} | ||
.promise=${el.model.initialized} | ||
.contacts=${el.model.occupants} | ||
.template=${tplOccupantsFilter} | ||
.filter=${el.filter}></converse-contacts-filter> | ||
${ repeat(o.occupants, (occ) => occ.get('jid'), (occ) => shouldShowOccupant(el, occ, o)) } | ||
</ul> | ||
`; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { html } from "lit"; | ||
import { __ } from 'i18n'; | ||
|
||
/** | ||
* @param {import('shared/components/contacts-filter').ContactsFilter} el | ||
*/ | ||
export default (el) => { | ||
const i18n_placeholder = __('Filter'); | ||
const title_contact_filter = __('Filter by name'); | ||
const title_status_filter = __('Filter by status'); | ||
const label_any = __('Any'); | ||
const label_online = __('Online'); | ||
const label_chatty = __('Chatty'); | ||
const label_busy = __('Busy'); | ||
const label_away = __('Away'); | ||
const label_xa = __('Extended Away'); | ||
const label_offline = __('Offline'); | ||
|
||
const chat_state = el.filter.get('chat_state'); | ||
const filter_text = el.filter.get('filter_text'); | ||
const filter_type = el.filter.get('filter_type'); | ||
|
||
return html` | ||
<form class="contacts-filter-form input-button-group ${ (!el.shouldBeVisible()) ? 'hidden' : 'fade-in' }" | ||
@submit=${ev => el.submitFilter(ev)}> | ||
<div class="form-inline flex-nowrap"> | ||
<div class="filter-by d-flex flex-nowrap"> | ||
<converse-icon | ||
size="1em" | ||
@click=${ev => el.changeTypeFilter(ev)} | ||
class="fa fa-user clickable ${ (filter_type === 'contacts') ? 'selected' : '' }" | ||
data-type="contacts" | ||
title="${title_contact_filter}"></converse-icon> | ||
<converse-icon | ||
size="1em" | ||
@click=${ev => el.changeTypeFilter(ev)} | ||
class="fa fa-circle clickable ${ (filter_type === 'state') ? 'selected' : '' }" | ||
data-type="state" | ||
title="${title_status_filter}"></converse-icon> | ||
</div> | ||
<div class="btn-group"> | ||
<input .value="${filter_text || ''}" | ||
@keydown=${ev => el.liveFilter(ev)} | ||
class="contacts-filter form-control ${ (filter_type === 'state') ? 'hidden' : '' }" | ||
placeholder="${i18n_placeholder}"/> | ||
<converse-icon size="1em" | ||
class="fa fa-times clear-input ${ (!filter_text || filter_type === 'state') ? 'hidden' : '' }" | ||
@click=${ev => el.clearFilter(ev)}> | ||
</converse-icon> | ||
</div> | ||
<select class="form-control state-type ${ (filter_type !== 'state') ? 'hidden' : '' }" | ||
@change=${ev => el.changeChatStateFilter(ev)}> | ||
<option value="">${label_any}</option> | ||
<option ?selected=${chat_state === 'online'} value="online">${label_online}</option> | ||
<option ?selected=${chat_state === 'chat'} value="chat">${label_chatty}</option> | ||
<option ?selected=${chat_state === 'dnd'} value="dnd">${label_busy}</option> | ||
<option ?selected=${chat_state === 'away'} value="away">${label_away}</option> | ||
<option ?selected=${chat_state === 'xa'} value="xa">${label_xa}</option> | ||
<option ?selected=${chat_state === 'offline'} value="offline">${label_offline}</option> | ||
</select> | ||
</div> | ||
</form>` | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/* global mock, converse */ | ||
|
||
const { $pres, u } = converse.env; | ||
|
||
describe("The MUC occupants filter", function () { | ||
|
||
fit("can be used to filter which occupants are shown", | ||
mock.initConverse( | ||
[], {}, | ||
async function (_converse) { | ||
|
||
const muc_jid = 'lounge@montague.lit' | ||
const members = [{ | ||
'nick': 'juliet', | ||
'jid': 'juliet@capulet.lit', | ||
'affiliation': 'member' | ||
}, { | ||
'nick': 'tybalt', | ||
'jid': 'tybalt@capulet.lit', | ||
'affiliation': 'member' | ||
}]; | ||
await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo', [], members); | ||
const view = _converse.chatboxviews.get(muc_jid); | ||
await u.waitUntil(() => view.model.occupants.length === 3); | ||
|
||
let filter_el = view.querySelector('converse-contacts-filter'); | ||
expect(u.isVisible(filter_el.firstElementChild)).toBe(false); | ||
|
||
for (let i=0; i<mock.chatroom_names.length; i++) { | ||
const name = mock.chatroom_names[i]; | ||
const role = mock.chatroom_roles[name].role; | ||
// See example 21 https://xmpp.org/extensions/xep-0045.html#enter-pres | ||
const presence = $pres({ | ||
to:'romeo@montague.lit/pda', | ||
from:'lounge@montague.lit/'+name | ||
}).c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'}) | ||
.c('item').attrs({ | ||
affiliation: mock.chatroom_roles[name].affiliation, | ||
jid: name.replace(/ /g,'.').toLowerCase() + '@montague.lit', | ||
role: role | ||
}); | ||
_converse.api.connection.get()._dataRecv(mock.createRequest(presence)); | ||
} | ||
|
||
const occupants = view.querySelector('.occupant-list'); | ||
await u.waitUntil(() => occupants.querySelectorAll('li').length > 3); | ||
expect(occupants.querySelectorAll('li').length).toBe(3+mock.chatroom_names.length); | ||
expect(view.model.occupants.length).toBe(3+mock.chatroom_names.length); | ||
|
||
mock.chatroom_names.forEach(name => { | ||
const model = view.model.occupants.findWhere({'nick': name}); | ||
const index = view.model.occupants.indexOf(model); | ||
expect(occupants.querySelectorAll('li .occupant-nick')[index].textContent.trim()).toBe(name); | ||
}); | ||
|
||
filter_el = view.querySelector('converse-contacts-filter'); | ||
expect(u.isVisible(filter_el.firstElementChild)).toBe(true); | ||
|
||
const filter = view.querySelector('.contacts-filter'); | ||
filter.value = "j"; | ||
u.triggerEvent(filter, "keydown", "KeyboardEvent"); | ||
await u.waitUntil(() => [...view.querySelectorAll('li')].filter(u.isVisible).length === 1); | ||
|
||
filter_el.querySelector('.fa-times').click(); | ||
await u.waitUntil(() => [...view.querySelectorAll('li')].filter(u.isVisible).length === 3+mock.chatroom_names.length); | ||
|
||
filter_el.querySelector('.fa-circle').click(); | ||
const state_select = view.querySelector('.state-type'); | ||
state_select.value = "dnd"; | ||
u.triggerEvent(state_select, 'change'); | ||
expect(state_select.value).toBe('dnd'); | ||
expect(state_select.options[state_select.selectedIndex].textContent).toBe('Busy'); | ||
await u.waitUntil(() => [...view.querySelectorAll('li')].filter(u.isVisible).length === 0); | ||
})); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.