introduce a filter function at the contact page.

This is done with the help of textcomplete and jsmart.js.
There is annoying bug inside. If the screensize is to small, the browser freezes. It is traced back to the "media-body" class (and its css attributes) which we use in the template.
This commit is contained in:
rabuzarus 2016-04-28 21:35:33 +02:00
parent 381551569a
commit 17958da201
9 changed files with 3712 additions and 12 deletions

View file

@ -1596,6 +1596,7 @@ ul.viewcontact_wrapper > li {
.contact-wrapper.media { .contact-wrapper.media {
overflow: visible; overflow: visible;
word-wrap: break-word; word-wrap: break-word;
margin-top: 0;
} }
/* bootstrap hack for .media */ /* bootstrap hack for .media */
.contact-wrapper.media .media-body { .contact-wrapper.media .media-body {
@ -1710,3 +1711,35 @@ main .nav-tabs>li.active>a:hover {
.theme-frio .back-bar .pointer-label { .theme-frio .back-bar .pointer-label {
color: #999; color: #999;
} }
/* textcomplete for contact filtering*/
#contact-list ul.dropdown-menu.textcomplete-dropdown.media-list {
position: relative !important;
top: inherit !important;
bottom: inherit !important;
left: inherit !important;
padding: 0;
margin-left: -15px;
margin-right: -15px;
background-color: transparent;
box-shadow: none;
border: none;
}
#contact-list ul.dropdown-menu.textcomplete-dropdown.media-list > li {
padding-left: 15px;
border-bottom: 1px solid rgba(238, 238, 238, $contentbg_transp);
}
#contact-list ul.dropdown-menu.textcomplete-dropdown.media-list > li:first-child {
display: none;
}
#contact-list ul.dropdown-menu.textcomplete-dropdown.media-list
.textcomplete-item > a {
padding: 0 !important;
border-left: none;
background-color: transparent !important;
}
/* this is a little hack for texcomplete contact filter
There are for some reasons empty <a> tags. I don't know why */
.textcomplete-item .contact-wrapper a {
padding: 0;
}

3433
frameworks/jsmart/jsmart.js Normal file

File diff suppressed because it is too large Load diff

10
frameworks/jsmart/jsmart.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -170,8 +170,6 @@ $(document).ready(function(){
}; };
}); });
//function commentOpenUI(obj, id) { //function commentOpenUI(obj, id) {
// $(document).unbind( "click.commentOpen", handler ); // $(document).unbind( "click.commentOpen", handler );
@ -451,4 +449,62 @@ function cmtBbOpen(id) {
} }
function cmtBbClose(id) { function cmtBbClose(id) {
$("#comment-edit-bb-" + id).hide(); $("#comment-edit-bb-" + id).hide();
} }
function contact_filter(item) {
// get the html content from the js template of the contact-wrapper
contact_tpl = unescape($(".javascript-template[rel=contact-template]").html());
var variables = {
id: item.id,
name: item.name,
username: item.username,
thumb: item.thumb,
img_hover: item.img_hover,
edit_hover: item.edit_hover,
account_type: item.account_type,
photo_menu: item.photo_menu,
alt_text: item.alt_text,
dir_icon: item.dir_icon,
sparkle: item.sparkle,
itemurl: item.itemurl,
url: item.url,
network: item.network,
tags: item.tags,
details: item.details,
};
// open a new jSmart instance with the template
var tpl = new jSmart (contact_tpl);
// replace the variable with the values
var html = tpl.fetch(variables);
return html;
}
function filter_replace(item) {
return item.name;
}
(function( $ ) {
$.fn.contact_filter = function(backend_url, typ, autosubmit, onselect) {
if(typeof typ === 'undefined') typ = '';
if(typeof autosubmit === 'undefined') autosubmit = false;
// Autocomplete contacts
contacts = {
match: /(^)([^\n]+)$/,
index: 2,
search: function(term, callback) { contact_search(term, callback, backend_url, typ); },
replace: filter_replace,
template: contact_filter,
};
this.attr('autocomplete','off');
var a = this.textcomplete([contacts], {className:'accontacts', zIndex:10000, appendTo: '#contact-list'});
a.on('textComplete:select', function(e, value, strategy) { $(".dropdown-menu.textcomplete-dropdown.media-list").show(); });
};
})( jQuery );

View file

@ -158,6 +158,7 @@ $("nav").bind('nav-update', function(e,data)
<script src="<?=$frio?>/frameworks/justifiedGallery/jquery.justifiedGallery.min.js"></script> <script src="<?=$frio?>/frameworks/justifiedGallery/jquery.justifiedGallery.min.js"></script>
<script src="<?=$frio?>/frameworks/bootstrap-colorpicker/js/bootstrap-colorpicker.min.js"></script> <script src="<?=$frio?>/frameworks/bootstrap-colorpicker/js/bootstrap-colorpicker.min.js"></script>
<script src="<?=$frio?>/frameworks/flexMenu/flexmenu.custom.js"></script> <script src="<?=$frio?>/frameworks/flexMenu/flexmenu.custom.js"></script>
<script src="<?=$frio?>/frameworks/jsmart/jsmart.js"></script>
<script src="<?=$frio?>/js/theme.js"></script> <script src="<?=$frio?>/js/theme.js"></script>
<script src="<?=$frio?>/js/acl.js"></script> <script src="<?=$frio?>/js/acl.js"></script>

View file

@ -16,7 +16,7 @@
</div> </div>
{{* use a smaller picture on very small displays (e.g. mobiles) *}} {{* use a smaller picture on very small displays (e.g. mobiles) *}}
<div class="contact-photo-image-wrapper hidden-lg hidden-md"> <div class="contact-photo-image-wrapper hidden-lg hidden-md hidden-sm">
<img class="contact-photo-xs media-object" src="{{$contact.thumb}}" {{$contact.sparkle}} alt="{{$contact.name}}" /> <img class="contact-photo-xs media-object" src="{{$contact.thumb}}" {{$contact.sparkle}} alt="{{$contact.name}}" />
{{* Overlay background on hover the avatar picture *}} {{* Overlay background on hover the avatar picture *}}
@ -73,3 +73,83 @@
</div> </div>
</div> </div>
{{* the following part is a nearly a copy of the part above but it is modyfied for working with js.
We use this part to filter the contacts with jquery.textcomplete *}}
<div class="javascript-template" rel="contact-template" style="display: none">
<div class="contact-wrapper media" id="contact-entry-wrapper-{$id}" >
{{* This is a wrapper for the contact picture and the dropdown menu with contact relating actions *}}
<div class="contact-photo-wrapper dropdown pull-left" >
<div class="contact-entry-photo mframe" id="contact-entry-photo-{$id}" >
<a class="dropdown-toggle" id="contact-photo-menu-{$id}" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" >
<div class="contact-photo-image-wrapper hidden-xs">
<img class="contact-photo media-object xl" src="{$thumb}" {11} alt="{$name}" />
{{* Overlay background on hover the avatar picture *}}
<div class="contact-photo-overlay">
<span class="contact-photo-overlay-content xl"><i class="fa fa-angle-down"></i></span>
</div>
</div>
{{* use a smaller picture on very small displays (e.g. mobiles) *}}
<div class="contact-photo-image-wrapper hidden-lg hidden-md hidden-sm">
<img class="contact-photo-xs media-object" src="{$thumb}" {11} alt="{$name}" />
{{* Overlay background on hover the avatar picture *}}
<div class="contact-photo-overlay">
<span class="contact-photo-overlay-content overlay-xs"><i class="fa fa-angle-down"></i></span>
</div>
</div>
</a>
{if $photo_menu}
<ul class="contact-photo-menu menu-popup dropdown-menu " id="contact-photo-menu-{$id}" role="menu" aria-labelledby="contact-photo-menu-{$id}">
{foreach $photo_menu as $c}
{if $c.2}
<li role="menuitem"><a target="redir" href="{$c.1}">{$c.0}</a></li>
{elseif $c.3}
<li role="menuitem"><a onclick="addToModal('{$c.1}')">{$c.0}</a></li>
{else}
<li role="menuitem"><a href="{$c.1}">{$c.0}</a></li>
{/if}
{/foreach}
</ul>
{/if}
</div>
</div>
<div class="media-body"> {{* @todo There is a bug with this class - the browser freezes if the screensize is to small - but only fith textcomplete*}}
{{* The contact description (e.g. Name, Network, kind of connection and so on *}}
<div class="contact-entry-desc">
<div class="contact-entry-name" id="contact-entry-name-{$id}" >
<h4 class="media-heading">{$name}
{if $account_type} <small class="contact-entry-details" id="contact-entry-accounttype-{$id}">({$account_type})</small>{/if}
{if $account_type == 'Forum'}<i class="fa fa-comments-o" aria-hidden="true"></i>{/if}
{{* @todo this needs some changing in core because $contact.account_type contains a translated string which may notbe the same in every language *}}
</h4>
</div>
{if $alt_text}<div class="contact-entry-details" id="contact-entry-rel-{$id}" >{$alt_text}</div>{/if}
{if $itemurl}<div class="contact-entry-details" id="contact-entry-url-{$id}" >{$itemurl}</div>{/if}
{if $tags}<div class="contact-entry-details" id="contact-entry-tags-{$id}" >{$tags}</div>{/if}
{if $details}<div class="contact-entry-details" id="contact-entry-details-{$id}" >{$details}</div>{/if}
{if $network}<div class="contact-entry-details" id="contact-entry-network-{$id}" >{$network}</div>{/if}
</div>
{{* The checkbox to perform batch actions to these contacts (for batch actions have a look at contacts-template.tpl) *}}
{{* if !$no_contacts_checkbox *}}
{{if $multiselect}}
<div class="checkbox contact-entry-checkbox pull-right">
<input id="checkbox-{$id}" type="checkbox" class="contact-select pull-right" name="contact_batch[]" value="{$id}">
<label for="checkbox-{$id}"></label>
</div>
{{/if}}
</div>
</div>
</div>

View file

@ -0,0 +1,23 @@
<script>
$(document).ready(function() {
// Add contact_filter autocompletion to the search field
$("#contacts-search").contact_filter(baseurl + '/acl', 'r', true);
// Hide the viewcontact_wrapper if there is an input in the search field
// We are doing this to let the the contact_filter replace the original
// shown contacts
$("#contacts-search").keyup(function(){
var elText = $(this).val();
if(elText.length !== 0) {
$("#viewcontact_wrapper").hide();
$("ul.textcomplete-dropdown").addClass("show media-list");
} else {
$("#viewcontact_wrapper").show();
$("ul.textcomplete-dropdown").removeClass("show");
}
});
});
</script>

View file

@ -49,14 +49,15 @@
</ul> </ul>
</li> </li>
</ul> </ul>
<div class="clear"> <div class="clear"></div>
<div id="contact-list">
{{* format each contact with the contact_template.tpl *}} {{* format each contact with the contact_template.tpl *}}
<ul id="viewcontact_wrapper" class="viewcontact_wrapper media-list"> <ul id="viewcontact_wrapper" class="viewcontact_wrapper media-list">
{{foreach $contacts as $contact}} {{foreach $contacts as $contact}}
<li>{{include file="contact_template.tpl"}}</li> <li>{{include file="contact_template.tpl"}}</li>
{{/foreach}} {{/foreach}}
</ul> </ul>
</div>
<div id="contact-edit-end"></div> <div id="contact-edit-end"></div>
</form> </form>

View file

@ -34,6 +34,7 @@ function frio_install() {
register_hook('item_photo_menu', 'view/theme/frio/theme.php', 'frio_item_photo_menu'); register_hook('item_photo_menu', 'view/theme/frio/theme.php', 'frio_item_photo_menu');
register_hook('contact_photo_menu', 'view/theme/frio/theme.php', 'frio_contact_photo_menu'); register_hook('contact_photo_menu', 'view/theme/frio/theme.php', 'frio_contact_photo_menu');
register_hook('nav_info', 'view/theme/frio/theme.php', 'frio_remote_nav'); register_hook('nav_info', 'view/theme/frio/theme.php', 'frio_remote_nav');
register_hook('acl_lookup_end', 'view/theme/frio/theme.php', 'frio_acl_lookup');
logger("installed theme frio"); logger("installed theme frio");
} }
@ -41,7 +42,9 @@ function frio_install() {
function frio_uninstall() { function frio_uninstall() {
unregister_hook('prepare_body_final', 'view/theme/frio/theme.php', 'frio_item_photo_links'); unregister_hook('prepare_body_final', 'view/theme/frio/theme.php', 'frio_item_photo_links');
unregister_hook('item_photo_menu', 'view/theme/frio/theme.php', 'frio_item_photo_menu'); unregister_hook('item_photo_menu', 'view/theme/frio/theme.php', 'frio_item_photo_menu');
unregister_hook('contact_photo_menu', 'view/theme/frio/theme.php', 'frio_contact_photo_menu');
unregister_hook('nav_info', 'view/theme/frio/theme.php', 'frio_remote_nav'); unregister_hook('nav_info', 'view/theme/frio/theme.php', 'frio_remote_nav');
unregister_hook('acl_lookup_end', 'view/theme/frio/theme.php', 'frio_acl_lookup');
logger("uninstalled theme frio"); logger("uninstalled theme frio");
} }
@ -244,3 +247,63 @@ function frio_remote_nav($a,&$nav) {
$nav['sitename'] = $a->config['sitename']; $nav['sitename'] = $a->config['sitename'];
} }
} }
/**
* @brief: Search for contacts
*
* This function search for a users contacts. The code is copied from contact search
* in /mod/contacts.php. With this function the contacts will permitted to acl_lookup()
* and can grabbed as json. For this we use the type="r". This is usful to to let js
* grab the contact data.
* We use this to give the data to textcomplete and have a filter function at the
* contact page.
*
* @param App $a The app data
* @param array $results The array with the originals from acl_lookup()
*/
function frio_acl_lookup($a, &$results) {
require_once("mod/contacts.php");
$nets = ((x($_GET,"nets")) ? notags(trim($_GET["nets"])) : "");
// we introduce a new search type, r should do the same query like it's
// done in /mod/contacts for connections
if($results["type"] == "r") {
$searching = false;
if($search) {
$search_hdr = $search;
$search_txt = dbesc(protect_sprintf(preg_quote($search)));
$searching = true;
}
$sql_extra .= (($searching) ? " AND (`attag` LIKE '%%".dbesc($search_txt)."%%' OR `name` LIKE '%%".dbesc($search_txt)."%%' OR `nick` LIKE '%%".dbesc($search_txt)."%%') " : "");
if($nets)
$sql_extra .= sprintf(" AND network = '%s' ", dbesc($nets));
$sql_extra2 = ((($sort_type > 0) && ($sort_type <= CONTACT_IS_FRIEND)) ? sprintf(" AND `rel` = %d ",intval($sort_type)) : '');
$r = q("SELECT COUNT(*) AS `total` FROM `contact`
WHERE `uid` = %d AND `self` = 0 AND `pending` = 0 $sql_extra $sql_extra2 ",
intval($_SESSION['uid']));
if(count($r)) {
$total = $r[0]["total"];
}
$sql_extra3 = unavailable_networks();
$r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `pending` = 0 $sql_extra $sql_extra2 $sql_extra3 ORDER BY `name` ASC LIMIT 100 ",
intval($_SESSION['uid'])
);
$contacts = array();
if(count($r)) {
foreach($r as $rr) {
$contacts[] = _contact_detail_for_template($rr);
}
}
$results["items"] = $contacts;
$results["tot"] = $total;
}
}