diff --git a/app/config.json b/app/config.json index b75a208416963d76a844e6a7eefd6385c690d4c2..0c14d7293ee6897072b936b1c4d5d86ea44907f0 100644 --- a/app/config.json +++ b/app/config.json @@ -35,11 +35,12 @@ }, "feed": { "jsonFeed": { - "fr-FR": "https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/feed-fr.json", - "en": "https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/feed-en.json", + "fr-FR": "https://forum.monnaie-libre.fr/c/tools/cesium/110.json", + "en": "https://forum.monnaie-libre.fr/t/actualites-fr/28088.json", "es": "https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/feed-es.json" }, - "maxContentLength": 1300 + "maxContentLength": 1300, + "maxAgeInMonths": 3 }, "fallbackNodes": [ { diff --git a/www/i18n/locale-ca.json b/www/i18n/locale-ca.json index 7db48c3f588f1766ccf0dd7a41609a85b19fb2a0..a93f3cb02ca8e783fcaaf5f70daab7a3dbdbfe37 100644 --- a/www/i18n/locale-ca.json +++ b/www/i18n/locale-ca.json @@ -117,6 +117,7 @@ }, "HOME": { "FEED_SOURCE": "Font", + "FEEDS_TITLE": "NotÃcies", "READ_MORE": "Amplia", "SHOW_ALL_FEED": "Mostra-m'ho tot", "TITLE": "Cesium", diff --git a/www/i18n/locale-de-DE.json b/www/i18n/locale-de-DE.json index 98a491564b9582de3044ce673fa928a75b23f285..dca7b56446bc030f9ac742735aa2d7676cc7fff4 100644 --- a/www/i18n/locale-de-DE.json +++ b/www/i18n/locale-de-DE.json @@ -139,7 +139,8 @@ "NETWORK_CONNECTION_ERROR": "Netzwerk ist nicht erreichbar.<br/><br/>Ãœberprüfen Sie Ihre Internetverbindung, oder wählen Sie einen Knoten manuell <a class=\"positive\" ng-click=\"doQuickFix('settings')\">in den Einstellungen</a> aus.", "SHOW_ALL_FEED": "Alles anzeigen", "READ_MORE": "Mehr lesen", - "FEED_SOURCE": "Quelle" + "FEED_SOURCE": "Quelle", + "FEEDS_TITLE": "Nachrichten" }, "SETTINGS": { "TITLE": "Einstellungen", diff --git a/www/i18n/locale-en-GB.json b/www/i18n/locale-en-GB.json index 893ff2a9de2232be30dfe36c82074f5c3a538a24..97ae039e5f7f530d3d17393dda71acba509c5a5f 100644 --- a/www/i18n/locale-en-GB.json +++ b/www/i18n/locale-en-GB.json @@ -135,7 +135,8 @@ "NETWORK_CONNECTION_ERROR": "Network is unreachable.<br/><br/>Check your Internet connection, or select a peer manually <a class=\"positive\" ng-click=\"doQuickFix('settings')\">in the settings</a>.", "SHOW_ALL_FEED": "Show all", "READ_MORE": "Read more", - "FEED_SOURCE": "Source" + "FEED_SOURCE": "Source", + "FEEDS_TITLE": "News" }, "SETTINGS": { "TITLE": "Settings", diff --git a/www/i18n/locale-en.json b/www/i18n/locale-en.json index 817b9c948cbb7fefb832eab8c4314e8d0766fce5..a23d15730163dab1e1bd53a918f2421b07531f8b 100644 --- a/www/i18n/locale-en.json +++ b/www/i18n/locale-en.json @@ -135,7 +135,8 @@ "NETWORK_CONNECTION_ERROR": "Network is unreachable.<br/><br/>Check your Internet connection, or select a peer manually <a class=\"positive\" ng-click=\"doQuickFix('settings')\">in the settings</a>.", "SHOW_ALL_FEED": "Show all", "READ_MORE": "Read more", - "FEED_SOURCE": "Source" + "FEED_SOURCE": "Source", + "FEEDS_TITLE": "News" }, "SETTINGS": { "TITLE": "Settings", diff --git a/www/i18n/locale-eo-EO.json b/www/i18n/locale-eo-EO.json index fafe943f8665f07f8dff2a4b141588b71843f1ab..b355afdff59712f41e5b807be21524ea3232470e 100644 --- a/www/i18n/locale-eo-EO.json +++ b/www/i18n/locale-eo-EO.json @@ -135,7 +135,8 @@ "NETWORK_CONNECTION_ERROR": "Reto neatingeblas.<br/><br/>Kontrolu vian interreta konekto, aÅ selektu nodon permane <a class=\"positive\" ng-click=\"doQuickFix('settings')\">en la agordoj</a>.", "SHOW_ALL_FEED": "Vidi ĉion", "READ_MORE": "Legi la sekvon", - "FEED_SOURCE": "Fonto" + "FEED_SOURCE": "Fonto", + "FEEDS_TITLE": "Novaĵoj" }, "SETTINGS": { "TITLE": "Parametroj", diff --git a/www/i18n/locale-es-ES.json b/www/i18n/locale-es-ES.json index 8021895d06856055be3a2db80e95bc37d1edf9a4..f60c11f2e2b09e3b60b3734eef6de8ee1ead4004 100644 --- a/www/i18n/locale-es-ES.json +++ b/www/i18n/locale-es-ES.json @@ -117,6 +117,7 @@ }, "HOME": { "FEED_SOURCE": "Fuente", + "FEEDS_TITLE": "Noticias", "READ_MORE": "Leer más", "SHOW_ALL_FEED": "Ver todo", "TITLE": "Cesium", diff --git a/www/i18n/locale-fr-FR.json b/www/i18n/locale-fr-FR.json index a10c5e26db4705342359762a2bd608f46901cf05..121321c68376a0ba38717ed03e27e82cd2bd2195 100644 --- a/www/i18n/locale-fr-FR.json +++ b/www/i18n/locale-fr-FR.json @@ -135,7 +135,8 @@ "NETWORK_CONNECTION_ERROR": "Réseau injoignable.<br/><br/>Vérifiez votre connexion Internet, ou sélectionnez un nÅ“ud manuellement <a class=\"positive\" ng-click=\"doQuickFix('settings')\">dans les paramètres</a>.", "SHOW_ALL_FEED": "Voir tout", "READ_MORE": "Lire la suite", - "FEED_SOURCE": "Source" + "FEED_SOURCE": "Source", + "FEEDS_TITLE": "Actualités" }, "SETTINGS": { "TITLE": "Paramètres", diff --git a/www/i18n/locale-it-IT.json b/www/i18n/locale-it-IT.json index c8739f03ec830bda85be21dc549a2bbe9248e390..b21269c1f87a5c18e511be6a7d4b35a009387a88 100644 --- a/www/i18n/locale-it-IT.json +++ b/www/i18n/locale-it-IT.json @@ -135,7 +135,8 @@ "NETWORK_CONNECTION_ERROR": "Rete non raggiungibile.<br/><br/>Controlla la tua connessione Internet, o seleziona un nodo manualmente <a class=\"positive\" ng-click=\"doQuickFix('settings')\">nelle impostazioni</a>.", "SHOW_ALL_FEED": "Mostra tutto", "READ_MORE": "Leggi di più", - "FEED_SOURCE": "Fonte" + "FEED_SOURCE": "Fonte", + "FEEDS_TITLE": "Notizie" }, "SETTINGS": { "TITLE": "Impostazioni", diff --git a/www/i18n/locale-nl-NL.json b/www/i18n/locale-nl-NL.json index 716698e5a40fd9e3b3827a9837de6e65596c57c2..f04ac3aabe4c4e4b2f7ba906afdb27cecc17e58d 100644 --- a/www/i18n/locale-nl-NL.json +++ b/www/i18n/locale-nl-NL.json @@ -126,7 +126,8 @@ "NETWORK_CONNECTION_ERROR": "Netwerk is niet bereikbaar.<br/><br/>Controleer uw internetverbinding, of selecteer een node handmatig <a class=\"positive\" ng-click=\"doQuickFix('settings')\">in de instellingen</a>.", "SHOW_ALL_FEED": "Alles weergeven", "READ_MORE": "Lees meer", - "FEED_SOURCE": "Bron" + "FEED_SOURCE": "Bron", + "FEEDS_TITLE": "Nieuws" }, "SETTINGS": { "TITLE": "Instellingen", diff --git a/www/i18n/locale-pt-PT.json b/www/i18n/locale-pt-PT.json index 40c50ecc7b8f1d8bdd7070807c690ba8a01ec97e..b7fc19e1230402dda1720a18f5d0fe751bdf50ba 100644 --- a/www/i18n/locale-pt-PT.json +++ b/www/i18n/locale-pt-PT.json @@ -117,6 +117,7 @@ }, "HOME": { "FEED_SOURCE": "Fonte", + "FEEDS_TITLE": "NotÃcias", "READ_MORE": "Ler mais", "SHOW_ALL_FEED": "Ver tudo", "TITLE": "Cesium", diff --git a/www/js/config.js b/www/js/config.js index 0364af5a152e3ba715fe22afaba46706c823a744..86f2af3991cf5a6147480c777225da7533367b0c 100644 --- a/www/js/config.js +++ b/www/js/config.js @@ -44,21 +44,18 @@ angular.module("cesium.config", []) }, "feed": { "jsonFeed": { - "fr-FR": "https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/feed-fr.json", + /*"fr-FR": "https://forum.monnaie-libre.fr/t/actualites-test/28088/2.json",*/ + "fr-FR": "https://forum.monnaie-libre.fr/c/tools/cesium/110.json", "en": "https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/feed-en.json", "es": "https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/feed-es.json" }, - "maxContentLength": 1300 + "maxContentLength": 1300, + "maxAgeInMonths": 15 }, "fallbackNodes": [ { - "host": "g1v1.p2p.legal", + "host": "g1.e-is.pro", "port": 443 - }, - { - "host": "duniter.moul.re", - "port": 443, - "path": "/bma" } ], "developers": [ diff --git a/www/js/controllers/home-controllers.js b/www/js/controllers/home-controllers.js index 77326e56446041b5bfa42e7202cb932e8b47d284..27529ebc0f26d0e5d8554f2ca5ead4975d7f4c7c 100644 --- a/www/js/controllers/home-controllers.js +++ b/www/js/controllers/home-controllers.js @@ -25,7 +25,7 @@ angular.module('cesium.home.controllers', ['cesium.platform', 'cesium.services'] ; function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $http, $q, $location, - UIUtils, BMA, Device, csConfig, csCache, csPlatform, csCurrency, csSettings) { + UIUtils, BMA, Device, csConfig, csHttp, csCache, csPlatform, csCurrency, csSettings) { 'ngInject'; $scope.loading = true; @@ -86,22 +86,47 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $ht if (!feedUrl || typeof feedUrl !== 'string') return; // Skip var maxContentLength = (csConfig.feed && csConfig.feed.maxContentLength) || 650; + var maxAgeInMonths = (csConfig.feed && csConfig.feed.maxAgeInMonths) || 3; // 3 month by default + // Min unix time, to exclude old topics + var minDate = moment().subtract(maxAgeInMonths, 'month').utc(); + var minTime = minDate.unix(); var now = Date.now(); - console.debug("[home] Loading feeds from {0}...".format(feedUrl)); + console.debug("[home] Loading recent feeds at {url: {0}, minTime: '{1}'}".format(feedUrl, minDate.toISOString())); - $http.get(feedUrl, {responseType: 'json', cache: csCache.get(null, csCache.constants.LONG)}) - .success(function(feed) { - console.debug('[home] Feeds loaded in {0}ms'.format(Date.now()-now)); - if (!feed || !feed.items || !feed.items.length) return; // skip if empty + $scope.getJson(feedUrl) + .then(function(feed) { + console.debug('[home] Feeds loaded in {0}ms'.format(Date.now() - now)); + + // Detect Discourse category, then convert + if ($scope.isDiscourseCategory(feed)) { + return $scope.parseDiscourseCategory(feedUrl, feed); + } + + // Detect Discourse topic, then convert + else if ($scope.isDiscourseTopic(feed)) { + return $scope.parseDiscourseTopic(feedUrl, feed); + } + + return feed; + }) + .then(function(feed) { + if (!feed || !feed.items) return; // skip feed.items = feed.items.reduce(function(res, item) { - if (!item || (!item.title && !item.content_text && !item.content_html)) return res; // Skip + if (!item || (!item.title && !item.content_text && !item.content_html)) return res; // Skip // Convert UTC time if (item.date_published) { - item.time = moment.utc(item.date_published).unix(); + item.creationTime = moment.utc(item.date_published).unix(); } + if (item.date_modified) { + item.time = moment.utc(item.date_modified).unix(); + } + + // Skip if too old items + if (item.creationTime && (item.creationTime < minTime)) return res; + // Convert content to HTML if (item.content_html) { item.content = item.content_html; @@ -113,7 +138,7 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $ht // Trunc content, if need if (maxContentLength !== -1 && item.content && item.content.length > maxContentLength) { var endIndex = Math.max(item.content.lastIndexOf(" ", maxContentLength), item.content.lastIndexOf("<", maxContentLength)); - item.content = item.content.substr(0, endIndex) + ' (...)'; + item.content = item.content.substring(0, endIndex) + ' (...)'; item.truncated = true; } @@ -123,14 +148,118 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $ht return res.concat(item); }, []); + if (!feed.items.length) return; // No items: skip + $scope.feed = feed; }) - .error(function(data, status) { - console.error('[home] Failed to load feeds.'); + .catch(function(err) { + console.error('[home] Failed to load feeds.', err); $scope.feed = null; }); }; + $scope.getJson = function(url) { + return $q(function(resolve, reject) { + $http.get(url, { + timeout: csConfig.timeout, + responseType: 'json', + cache: csCache.get(null, csCache.constants.LONG) + }) + .success(resolve) + .error(reject) + }); + }; + + $scope.isDiscourseCategory = function(category) { + return category && category.topic_list && Array.isArray(category.topic_list.topics); + } + + $scope.parseDiscourseCategory = function(url, category) { + // Make sure this is a valid topic + if (!$scope.isDiscourseCategory(category)) throw new Error('Not a discourse category'); + + var uri = csHttp.uri.parse(url); + var baseUrl = uri.protocol + '//' + uri.host + (uri.port != 443 && uri.port != 80 ? uri.port : ''); + var feed = { + version: "https://jsonfeed.org/version/1", // fixed value + home_page_url: category.topic_list.more_topics_url.replace(/\?page=[0-9]+/, ''), + feed_url: url, + title: 'HOME.FEEDS_TITLE' // FIXME: how get the category title ? + }; + + return $q.all( + category.topic_list.topics.reduce(function(res, topic) { + if (!topic.pinned) return res; // Skip not pinned topic + + var topicUrl = [baseUrl, 't', topic.slug, topic.id].join('/') + '.json'; + return res.concat($scope.getJson(topicUrl)) + }, []) + ).then(function(topics) { + feed.items = topics.reduce(function(res, topic) { + if (!$scope.isDiscourseTopic(topic)) return res; // Not a topic: skip + var feedTopic = $scope.parseDiscourseTopic(baseUrl, topic, feed); + + if (!feedTopic.items || !feedTopic.items.length) return res; // Topic is empty: skip + return res.concat(feedTopic.items[0]); + }, []); + return feed; + }); + } + + $scope.isDiscourseTopic = function(topic) { + return topic && topic.title && topic.post_stream && Array.isArray(topic.post_stream.posts); + } + + $scope.parseDiscourseTopic = function(url, topic, feed) { + // Make sure this is a valid topic + if (!$scope.isDiscourseTopic(topic)) throw new Error('Not a discourse topic'); + + var uri = csHttp.uri.parse(url); + var baseUrl = uri.protocol + '//' + uri.host + (uri.port != 443 && uri.port != 80 ? uri.port : ''); + + // Prepare root feed, if not yet exists + feed = feed || { + version: "https://jsonfeed.org/version/1", // fixed value + home_page_url: [baseUrl, 't', topic.slug, topic.id].join('/'), + feed_url: url, + title: topic.title + }; + + feed.items = topic.post_stream.posts.reduce(function(res, post) { + if (!post.cooked || post.cooked.trim() === '') return res; // Skip if empty + + var author = { + name: post.display_username, + url: [baseUrl, 'u', post.username].join('/'), + avatar: post.avatar_template ? (baseUrl + post.avatar_template.replace('{size}', '60')) : undefined + } + + // Try to resolve author pubkey, to replace author url by a link to wot identity + var developer = _.find(csConfig.developers || [], function(developer) { + return developer.name && ( + (post.display_username && developer.name.toLowerCase() === post.display_username.toLowerCase()) || + (post.username && developer.name.toLowerCase() === post.username.toLowerCase()) + ); + }); + if (developer && developer.pubkey) { + author.url = '@' + developer.pubkey; + } + + return res.concat({ + id: post.id, + url: [baseUrl, 't', post.topic_slug, post.topic_id, post.post_number].join('/'), + title: feed.title === topic.title ? '' : topic.title, // Only if different + date_published: post.created_at, + date_modified: post.updated_at, + content_html: post.cooked, + author: author, + tags: post.tags || topic.tags + }); + }, []); + + return feed; + }; + /** * Catch click for quick fix * @param action diff --git a/www/templates/home/home.html b/www/templates/home/home.html index 6e1473387a3c63dff5ad2f636e73d63a2363bdc0..c49100cadbf74768dea37ff721311f66f74439f6 100644 --- a/www/templates/home/home.html +++ b/www/templates/home/home.html @@ -166,7 +166,7 @@ <div class="feed padding-horizontal no-padding-xs padding-top"> <h3 class="padding-left"> <i class="icon ion-speakerphone"></i> - {{feed.title}} + {{feed.title|translate}} <small><a ng-click="openLink($event, feed.home_page_url)" class="gray"> <span translate>HOME.SHOW_ALL_FEED</span> <i class="icon ion-chevron-right"></i> @@ -191,7 +191,7 @@ title="{{item.time|formatDate}}" ng-click="openLink($event, item.url)" class="item-note "> - <small><i class="icon ion-clock"></i> {{item.time|formatFromNow}}</small> + <small><i class="icon ion-clock"></i> {{item.creationTime|formatFromNow}}</small> </a> </div> <!-- title -->