Browse Source

Mise à jour automatique des nombres d'articles non lus et favoris

En JavaScript, sans requête au serveur, décrémente ou incrémente le
nombre d'articles non lus ou en favoris suite à une action de
l'utilisateur.
Utilise un nouvel attribut data-unread pour stocker le nombre d'articles
non-lus et du pur CSS pour afficher cette valeur.
Nouvel attribut data-priority (pour savoir s'il faut inclure le flux ou
pas dans les nombres d'articles non lus).
Légère simplification CSS au passage (d'autres optimisations des
performances CSS seraient souhaitables en évitant les règles contenant
trop de sélecteurs universels imbriqués genre ".categories .favorites
.btn" et en évitant les changements de style en JavaScript lors du
chargement - j'essayerai de faire une proposition dans un patch séparé).
Bug connu : une catégorie finissant par une espace suivi d'un nombre
entre parenthèses comme "Exemple (2)" cause actuellement un léger bug
d'affichage dans le <title> s'il y a 0 article non lu et que
l'utilisateur en marque un comme "non lu". Il faudra une modification
pour utiliser data-unread aussi pour le <title>
Alexandre Alapetite 12 years ago
parent
commit
e7dd482979

+ 17 - 30
app/layout/aside_flux.phtml

@@ -30,20 +30,16 @@
 		<?php } ?>
 
 		<li>
-			<div class="all">
-				<a class="btn<?php echo $this->get_c == 'all' ? ' active' : ''; ?>" href="<?php echo _url ('index', 'index'); ?>">
-					<i class="icon i_all"></i>
-					<?php echo Translate::t ('all_feeds', $this->nb_total); ?>
-					<?php if ($this->nb_not_read > 0) { ?>
-					<span class="notRead"><?php echo $this->nb_not_read > 1 ? Translate::t ('not_reads', $this->nb_not_read) : Translate::t ('not_read', $this->nb_not_read); ?></span>
-					<?php } ?>
+			<div class="category all">
+				<a data-unread="<?php echo $this->nb_not_read; ?>" class="btn<?php echo $this->get_c == 'all' ? ' active' : ''; ?>" href="<?php echo _url ('index', 'index'); ?>">
+					<i class="icon i_all"></i><?php echo Translate::t ('all_feeds', $this->nb_total); ?>
 				</a>
 			</div>
 		</li>
 
 		<li>
-			<div class="favorites">
-				<a class="btn<?php echo $this->get_c == 'favoris' ? ' active' : ''; ?>" href="<?php echo _url ('index', 'index', 'get', 'favoris'); ?>">
+			<div class="category favorites">
+				<a data-unread="0" class="btn<?php echo $this->get_c == 'favoris' ? ' active' : ''; ?>" href="<?php echo _url ('index', 'index', 'get', 'favoris'); ?>">
 					<i class="icon i_bookmark"></i>
 					<?php echo Translate::t ('favorite_feeds', $this->nb_favorites); ?>
 				</a>
@@ -51,36 +47,27 @@
 		</li>
 
 		<?php foreach ($this->cat_aside as $cat) { ?>
-		<?php $feeds = $cat->feeds (); $catNotRead = $cat->nbNotRead (); ?>
+		<?php $feeds = $cat->feeds (); ?>
 		<?php if (!empty ($feeds)) { ?>
 		<li>
 			<?php $c_active = false; if ($this->get_c == $cat->id ()) { $c_active = true; } ?>
 			<div class="category<?php echo $c_active ? ' active' : ''; ?>">
-				<a class="btn<?php echo $c_active ? ' active' : ''; ?>" href="<?php echo _url ('index', 'index', 'get', 'c_' . $cat->id ()); ?>">
-					<?php echo $cat->name (); ?>
-					<?php if ($catNotRead > 0) { ?>
-					<span class="notRead" title="<?php echo $catNotRead > 1 ? Translate::t ('not_reads', $catNotRead) : Translate::t ('not_read', $catNotRead); ?>"><?php echo $catNotRead ; ?></span>
-					<?php } ?>
-				</a>
+				<a data-unread="<?php echo $cat->nbNotRead (); ?>" class="btn<?php echo $c_active ? ' active' : ''; ?>" href="<?php echo _url ('index', 'index', 'get', 'c_' . $cat->id ()); ?>"><?php echo $cat->name (); ?></a>
 			</div>
 
 			<ul class="feeds<?php echo $c_active ? ' active' : ''; ?>">
-				<?php foreach ($feeds as $feed) { ?>
-				<?php $nbEntries = $feed->nbEntries (); ?>
-				<?php $f_active = false; if ($this->get_f == $feed->id ()) { $f_active = true; } ?>
-				<li class="item<?php echo $f_active ? ' active' : ''; ?><?php echo $feed->inError () ? ' error' : ''; ?><?php echo $nbEntries == 0 ? ' empty' : ''; ?>">
+				<?php foreach ($feeds as $feed) {
+						$feed_id = $feed->id (); $nbEntries = $feed->nbEntries ();
+						$f_active = ($this->get_f == $feed_id);
+				?>
+				<li id="f_<?php echo $feed_id; ?>" class="item<?php echo $f_active ? ' active' : ''; ?><?php echo $feed->inError () ? ' error' : ''; ?><?php echo $nbEntries == 0 ? ' empty' : ''; ?>">
 					<div class="dropdown">
 						<div class="dropdown-target"></div>
-						<a class="dropdown-toggle" data-fid="<?php echo $feed->id (); ?>" data-fweb="<?php echo $feed->website (); ?>"><i class="icon i_configure"></i></a>
+						<a class="dropdown-toggle" data-fweb="<?php echo $feed->website (); ?>"><i class="icon i_configure"></i></a>
 <?php /* feed_config_template */ ?>
 					</div>
-					<?php $not_read = $feed->nbNotRead (); ?>
-					<img class="favicon" src="<?php echo $feed->favicon (); ?>" alt="" />
-					<?php echo $not_read > 0 ? '<b>' : ''; ?>
-					<a class="feed" href="<?php echo _url ('index', 'index', 'get', 'f_' . $feed->id ()); ?>">
-						<?php echo ($not_read > 0 ? '(' . $not_read . ') ' : ''), $feed->name(); ?>
-					</a>
-					<?php echo $not_read > 0 ? '</b>' : ''; ?>
+					<img class="favicon" src="<?php echo $feed->favicon (); ?>" />
+					<a class="feed" data-unread="<?php echo $feed->nbNotRead (); ?>" data-priority="<?php echo $feed->priority (); ?>" href="<?php echo _url ('index', 'index', 'get', 'f_' . $feed_id); ?>"><?php echo $feed->name(); ?></a>
 				</li>
 				<?php } ?>
 			</ul>
@@ -94,13 +81,13 @@
 <script id="feed_config_template" type="text/html">
 	<ul class="dropdown-menu">
 		<li class="dropdown-close"><a href="#close">&nbsp;</a></li>
-		<li class="item"><a href="<?php echo _url ('index', 'index', 'get', 'f_' . '!!!!!!'); ?>"><?php echo Translate::t ('filter'); ?></a></li>
+		<li class="item"><a href="<?php echo _url ('index', 'index', 'get', 'f_!!!!!!'); ?>"><?php echo Translate::t ('filter'); ?></a></li>
 		<li class="item"><a target="_blank" href="http://example.net/"><?php echo Translate::t ('see_website'); ?></a></li>
 		<?php if (!login_is_conf ($this->conf) || is_logged ()) { ?>
 		<li class="separator"></li>
 		<li class="item"><a href="<?php echo _url ('configure', 'feed', 'id', '!!!!!!'); ?>"><?php echo Translate::t ('administration'); ?></a></li>
 		<li class="item"><a href="<?php echo _url ('feed', 'actualize', 'id', '!!!!!!'); ?>"><?php echo Translate::t ('actualize'); ?></a></li>
-		<li class="item"><a href="<?php echo _url ('entry', 'read', 'is_read', 1, 'get', 'f_' . '!!!!!!'); ?>"><?php echo Translate::t ('mark_read'); ?></a></li>
+		<li class="item"><a href="<?php echo _url ('entry', 'read', 'is_read', 1, 'get', 'f_!!!!!!'); ?>"><?php echo Translate::t ('mark_read'); ?></a></li>
 		<?php } ?>
 	</ul>
 </script>

+ 49 - 4
public/scripts/main.js

@@ -71,6 +71,11 @@ function toggleContent (new_active, old_active) {
 	}
 }
 
+function _incLabel(p, inc) {
+	var i = (parseInt(p.replace(/\D/g, '')) || 0) + inc;
+	return i > 0 ? ' (' + i + ')' : '';
+}
+
 function mark_read (active, only_not_read) {
 	if (active[0] === undefined || (
 		only_not_read === true && !active.hasClass("not_read"))) {
@@ -91,11 +96,41 @@ function mark_read (active, only_not_read) {
 
 		active.find ("a.read").attr ("href", res.url);
 
+		var inc = 0;
 		if (active.hasClass ("not_read")) {
 			active.removeClass ("not_read");
+			inc--;
 		} else if (only_not_read !== true || active.hasClass("not_read")) {
 			active.addClass ("not_read");
+			inc++;
+		}
+
+		//Update unread: feed	//Alex
+		var feed_url = active.find(".website>a").attr("href"),
+			feed_id = feed_url.substr(feed_url.lastIndexOf('f_')),
+			elem = $('#' + feed_id + ' .feed').get(0),
+			attr_unread = elem ? elem.getAttributeNode('data-unread') : null,
+			feed_priority = elem ? parseInt(elem.getAttribute('data-priority')) : 0;
+		if (attr_unread)
+			attr_unread.value = Math.max(0, parseInt(attr_unread.value) + inc);
+
+		//Update unread: category
+		elem = $('#' + feed_id).parent().prevAll('.category').children(':first').get(0);
+		attr_unread = elem ? elem.getAttributeNode('data-unread') : null;
+		if (attr_unread)
+			attr_unread.value = Math.max(0, parseInt(attr_unread.value) + inc);
+
+		if (feed_priority > 0) {	//Update unread: all
+			elem = $('#aside_flux .all').children(':first').get(0);
+			attr_unread = elem ? elem.getAttributeNode('data-unread') : null;
+			if (attr_unread)
+				attr_unread.value = Math.max(0, parseInt(attr_unread.value) + inc);
 		}
+
+		//Update unread: title
+		document.title = document.title.replace(/((?: \(\d+\))?)( - .*?)((?: \(\d+\))?)$/, function(m, p1, p2, p3) {
+			return _incLabel(p1, inc) + p2 + _incLabel(p3, feed_priority > 0 ? inc : 0);
+		});
 	});
 }
 
@@ -117,11 +152,20 @@ function mark_favorite (active) {
 		res = jQuery.parseJSON(data);
 
 		active.find ("a.bookmark").attr ("href", res.url);
+		var inc = 0;
 		if (active.hasClass ("favorite")) {
 			active.removeClass ("favorite");
+			inc--;
 		} else {
 			active.addClass ("favorite");
+			inc++;
 		}
+
+		var favourites = $('.favorites>a').contents().last().get(0);
+		if (favourites && favourites.textContent)
+			favourites.textContent = favourites.textContent.replace(/((?: \(\d+\))?\s*)$/, function(m, p1) {
+				return _incLabel(p1, inc);
+			});
 	});
 }
 
@@ -235,9 +279,10 @@ function init_column_categories () {
 		return;
 	}
 
-	$(".category").addClass ("stick");
-	$(".categories .category .btn:first-child").width ("160px");
-	$(".category").append ("<a class=\"btn dropdown-toggle\" href=\"#\"><i class=\"icon i_down\"></i></a>");
+	//TODO: toggle class in PHP and remove the CSS changes done in JavaScript
+	$(".category:not(.all):not(.favorites) .btn:first-child").width ("160px");
+	$(".category:not(.all):not(.favorites)").addClass("stick").
+		append ("<a class=\"btn dropdown-toggle\" href=\"#\"><i class=\"icon i_down\"></i></a>");
 
 	$(".category + .feeds").not(".active").hide();
 	$(".category.active a.dropdown-toggle i").toggleClass ("i_up");
@@ -391,7 +436,7 @@ function init_nav_entries() {
 function init_templates() {
 	$('#aside_flux').on('click', '.dropdown-toggle', function () {
 		if ($(this).nextAll('.dropdown-menu').length === 0) {
-			var feed_id = $(this).data('fid'),
+			var feed_id = $(this).closest('li').attr('id').substr(2),
 				feed_web = $(this).data('fweb'),
 				template = $('#feed_config_template').html().replace(/!!!!!!/g, feed_id).replace('http://example.net/', feed_web);
 			$(this).attr('href', '#dropdown-' + feed_id).prev('.dropdown-target').attr('id', 'dropdown-' + feed_id).parent().append(template);

+ 21 - 18
public/themes/default/freshrss.css

@@ -93,9 +93,7 @@
 	text-align: center;
 	list-style: none;
 }
-	.categories .all,
-	.categories .favorites,
-	.categories .category {
+	.category {
 		display: block;
 		padding: 5px 0;
 		width: 220px;
@@ -105,12 +103,23 @@
 		white-space: nowrap;
 		text-overflow: ellipsis;
 	}
-		.categories .all .btn,
-		.categories .favorites .btn,
-		.categories .category .btn:first-child {
+		.category .btn:first-child {
 			width: 195px;
 			position: relative;
 		}
+		.category .btn:first-child:not([data-unread="0"]):after {
+			content: attr(data-unread);
+			position: absolute;
+			top: 3px; right: 3px;
+			padding: 1px 5px;
+			background: #ccc;
+			color: #fff;
+			font-size: 90%;
+			border: 1px solid #bbb;
+			border-radius: 5px;
+			box-shadow: 1px 3px 3px #aaa inset;
+			text-shadow: 0 0 1px #aaa;
+		}
 	.categories .feeds {
 		width: 100%;
 		margin: 0;
@@ -146,6 +155,12 @@
 			white-space: nowrap;
 			text-overflow: ellipsis;
 		}
+		.feed:not([data-unread="0"]) {
+			font-weight:bold;
+		}
+		.feed:not([data-unread="0"]):before {
+			content: "(" attr(data-unread) ") ";
+		}
 		.categories .feeds .dropdown .dropdown-menu {
 			left: 0;
 		}
@@ -163,18 +178,6 @@
 				background-color: #fff;
 				border-radius: 3px;
 			}
-	.categories .notRead {
-		position: absolute;
-		top: 3px; right: 3px;
-		padding: 1px 5px;
-		background: #ccc;
-		color: #fff;
-		font-size: 90%;
-		border: 1px solid #bbb;
-		border-radius: 5px;
-		box-shadow: 1px 3px 3px #aaa inset;
-		text-shadow: 0 0 1px #aaa;
-	}
 
 .post {
 	padding: 10px 50px;

+ 19 - 16
public/themes/flat-design/freshrss.css

@@ -92,9 +92,7 @@ body {
 	text-align: center;
 	list-style: none;
 }
-	.categories .all,
-	.categories .favorites,
-	.categories .category {
+	.category {
 		display: block;
 		padding: 5px 0;
 		width: 220px;
@@ -104,12 +102,21 @@ body {
 		white-space: nowrap;
 		text-overflow: ellipsis;
 	}
-		.categories .all .btn,
-		.categories .favorites .btn,
-		.categories .category .btn:first-child {
+		.category .btn:first-child {
 			width: 195px;
 			position: relative;
 		}
+		.category .btn:first-child:not([data-unread="0"]):after {
+			content: attr(data-unread);
+			position: absolute;
+			top: 5px; right: 0px;
+			padding: 0 5px;
+			color: #fff;
+			font-size: 90%;
+			background: #3498DB;
+			border-left: 3px solid #2980B9;
+			border-radius: 5px 0 0 5px;
+		}
 	.categories .feeds {
 		width: 220px;
 		margin: 0 auto;
@@ -138,6 +145,12 @@ body {
 			white-space: nowrap;
 			text-overflow: ellipsis;
 		}
+		.feed:not([data-unread="0"]) {
+			font-weight:bold;
+		}
+		.feed:not([data-unread="0"]):before {
+			content: "(" attr(data-unread) ") ";
+		}
 		.categories .feeds .dropdown .dropdown-menu {
 			left: 0;
 		}
@@ -155,16 +168,6 @@ body {
 				background-color: #95a5a6;
 				border-radius: 3px;
 			}
-	.categories .notRead {
-		position: absolute;
-		top: 5px; right: 0px;
-		padding: 0 5px;
-		color: #fff;
-		font-size: 90%;
-		background: #3498DB;
-		border-left: 3px solid #2980B9;
-		border-radius: 5px 0 0 5px;
-	}
 	.categories .btn:hover .notRead,
 	.categories .btn.active .notRead {
 		background: #2980B9;