diff --git a/src/main/java/com/commafeed/frontend/model/Category.java b/src/main/java/com/commafeed/frontend/model/Category.java index f782a963..62afab9e 100644 --- a/src/main/java/com/commafeed/frontend/model/Category.java +++ b/src/main/java/com/commafeed/frontend/model/Category.java @@ -15,6 +15,7 @@ import com.google.common.collect.Lists; public class Category implements Serializable { private String id; + private String parentId; private String name; private List children = Lists.newArrayList(); private List feeds = Lists.newArrayList(); @@ -28,6 +29,14 @@ public class Category implements Serializable { this.id = id; } + public String getParentId() { + return parentId; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + public String getName() { return name; } diff --git a/src/main/java/com/commafeed/frontend/model/Entry.java b/src/main/java/com/commafeed/frontend/model/Entry.java index 75808597..519d3c86 100644 --- a/src/main/java/com/commafeed/frontend/model/Entry.java +++ b/src/main/java/com/commafeed/frontend/model/Entry.java @@ -41,7 +41,8 @@ public class Entry implements Serializable { entry.setFeedName(status.getSubscription().getTitle()); entry.setFeedId(String.valueOf(status.getSubscription().getId())); - entry.setFeedUrl(status.getSubscription().getFeed().getLink()); + entry.setFeedUrl(status.getSubscription().getFeed().getUrl()); + entry.setFeedLink(status.getSubscription().getFeed().getLink()); return entry; } @@ -87,9 +88,12 @@ public class Entry implements Serializable { @ApiProperty("feed name") private String feedName; - @ApiProperty("feed url") + @ApiProperty("this entry's feed url") private String feedUrl; + @ApiProperty("this entry's website url") + private String feedLink; + @ApiProperty("entry url") private String url; @@ -203,4 +207,12 @@ public class Entry implements Serializable { this.guid = guid; } + public String getFeedLink() { + return feedLink; + } + + public void setFeedLink(String feedLink) { + this.feedLink = feedLink; + } + } diff --git a/src/main/java/com/commafeed/frontend/model/Subscription.java b/src/main/java/com/commafeed/frontend/model/Subscription.java index 0eaaa190..6406f712 100644 --- a/src/main/java/com/commafeed/frontend/model/Subscription.java +++ b/src/main/java/com/commafeed/frontend/model/Subscription.java @@ -1,11 +1,14 @@ package com.commafeed.frontend.model; import java.io.Serializable; +import java.util.Date; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; +import com.commafeed.backend.model.FeedCategory; +import com.commafeed.backend.model.FeedSubscription; import com.wordnik.swagger.annotations.ApiClass; import com.wordnik.swagger.annotations.ApiProperty; @@ -15,6 +18,23 @@ import com.wordnik.swagger.annotations.ApiProperty; @ApiClass("User information") public class Subscription implements Serializable { + public static Subscription build(FeedSubscription subscription, + long unreadCount) { + FeedCategory category = subscription.getCategory(); + Subscription sub = new Subscription(); + sub.setId(subscription.getId()); + sub.setName(subscription.getTitle()); + sub.setMessage(subscription.getFeed().getMessage()); + sub.setErrorCount(subscription.getFeed().getErrorCount()); + sub.setFeedUrl(subscription.getFeed().getUrl()); + sub.setFeedLink(subscription.getFeed().getLink()); + sub.setLastRefresh(subscription.getFeed().getLastUpdated()); + sub.setUnread(unreadCount); + sub.setCategoryId(category == null ? null : String.valueOf(category + .getId())); + return sub; + } + @ApiProperty(value = "subscription id", required = true) private Long id; @@ -27,12 +47,21 @@ public class Subscription implements Serializable { @ApiProperty(value = "error count", required = true) private int errorCount; + @ApiProperty(value = "last time the feed was refreshed", required = true) + private Date lastRefresh; + @ApiProperty(value = "this subscription's feed url", required = true) private String feedUrl; + @ApiProperty(value = "this subscription's website url", required = true) + private String feedLink; + @ApiProperty(value = "unread count", required = true) private long unread; + @ApiProperty(value = "category id") + private String categoryId; + public Long getId() { return id; } @@ -81,4 +110,28 @@ public class Subscription implements Serializable { this.errorCount = errorCount; } + public String getFeedLink() { + return feedLink; + } + + public void setFeedLink(String feedLink) { + this.feedLink = feedLink; + } + + public Date getLastRefresh() { + return lastRefresh; + } + + public void setLastRefresh(Date lastRefresh) { + this.lastRefresh = lastRefresh; + } + + public String getCategoryId() { + return categoryId; + } + + public void setCategoryId(String categoryId) { + this.categoryId = categoryId; + } + } \ No newline at end of file diff --git a/src/main/java/com/commafeed/frontend/model/request/RenameRequest.java b/src/main/java/com/commafeed/frontend/model/request/CategoryModificationRequest.java similarity index 63% rename from src/main/java/com/commafeed/frontend/model/request/RenameRequest.java rename to src/main/java/com/commafeed/frontend/model/request/CategoryModificationRequest.java index 3320a285..d8e1c15b 100644 --- a/src/main/java/com/commafeed/frontend/model/request/RenameRequest.java +++ b/src/main/java/com/commafeed/frontend/model/request/CategoryModificationRequest.java @@ -12,15 +12,18 @@ import com.wordnik.swagger.annotations.ApiProperty; @SuppressWarnings("serial") @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) -@ApiClass("Rename Request") -public class RenameRequest implements Serializable { +@ApiClass("Category modification request") +public class CategoryModificationRequest implements Serializable { @ApiProperty(value = "id", required = true) private Long id; - @ApiProperty(value = "mark as read or unread") + @ApiProperty(value = "new name") private String name; + @ApiProperty(value = "new parent category id") + private String parentId; + public Long getId() { return id; } @@ -37,4 +40,12 @@ public class RenameRequest implements Serializable { this.name = name; } + public String getParentId() { + return parentId; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + } diff --git a/src/main/java/com/commafeed/frontend/model/request/FeedModificationRequest.java b/src/main/java/com/commafeed/frontend/model/request/FeedModificationRequest.java new file mode 100644 index 00000000..5a3c7825 --- /dev/null +++ b/src/main/java/com/commafeed/frontend/model/request/FeedModificationRequest.java @@ -0,0 +1,51 @@ +package com.commafeed.frontend.model.request; + +import java.io.Serializable; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; + +import com.wordnik.swagger.annotations.ApiClass; +import com.wordnik.swagger.annotations.ApiProperty; + +@SuppressWarnings("serial") +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +@ApiClass("Feed modification request") +public class FeedModificationRequest implements Serializable { + + @ApiProperty(value = "id", required = true) + private Long id; + + @ApiProperty(value = "new name") + private String name; + + @ApiProperty(value = "new parent category id") + private String categoryId; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCategoryId() { + return categoryId; + } + + public void setCategoryId(String categoryId) { + this.categoryId = categoryId; + } + +} diff --git a/src/main/java/com/commafeed/frontend/rest/resources/CategoryREST.java b/src/main/java/com/commafeed/frontend/rest/resources/CategoryREST.java index b5629378..89de8191 100644 --- a/src/main/java/com/commafeed/frontend/rest/resources/CategoryREST.java +++ b/src/main/java/com/commafeed/frontend/rest/resources/CategoryREST.java @@ -32,10 +32,10 @@ import com.commafeed.frontend.model.Entries; import com.commafeed.frontend.model.Entry; import com.commafeed.frontend.model.Subscription; import com.commafeed.frontend.model.request.AddCategoryRequest; +import com.commafeed.frontend.model.request.CategoryModificationRequest; import com.commafeed.frontend.model.request.CollapseRequest; import com.commafeed.frontend.model.request.IDRequest; import com.commafeed.frontend.model.request.MarkRequest; -import com.commafeed.frontend.model.request.RenameRequest; import com.commafeed.frontend.rest.Enums.ReadType; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; @@ -218,9 +218,10 @@ public class CategoryREST extends AbstractResourceREST { } @POST - @Path("/rename") + @Path("/modify") @ApiOperation(value = "Rename a category", notes = "Rename an existing feed category") - public Response renameCategory(@ApiParam(required = true) RenameRequest req) { + public Response modifyCategory( + @ApiParam(required = true) CategoryModificationRequest req) { Preconditions.checkNotNull(req); Preconditions.checkNotNull(req.getId()); Preconditions.checkArgument(StringUtils.isNotBlank(req.getName())); @@ -228,6 +229,16 @@ public class CategoryREST extends AbstractResourceREST { FeedCategory category = feedCategoryDAO .findById(getUser(), req.getId()); category.setName(req.getName()); + + FeedCategory parent = null; + if (req.getParentId() != null + && !CategoryREST.ALL.equals(req.getParentId()) + && !StringUtils.equals(req.getParentId(), + String.valueOf(req.getId()))) { + parent = feedCategoryDAO.findById(getUser(), + Long.valueOf(req.getParentId())); + } + category.setParent(parent); feedCategoryDAO.update(category); return Response.ok(Status.OK).build(); @@ -281,6 +292,9 @@ public class CategoryREST extends AbstractResourceREST { subscriptions, unreadCount); child.setId(String.valueOf(c.getId())); child.setName(c.getName()); + if (c.getParent() != null && c.getParent().getId() != null) { + child.setParentId(String.valueOf(c.getParent().getId())); + } child.setExpanded(!c.isCollapsed()); category.getChildren().add(child); } @@ -296,14 +310,9 @@ public class CategoryREST extends AbstractResourceREST { if ((id == null && subscription.getCategory() == null) || (subscription.getCategory() != null && ObjectUtils .equals(subscription.getCategory().getId(), id))) { - Subscription sub = new Subscription(); - sub.setId(subscription.getId()); - sub.setName(subscription.getTitle()); - sub.setMessage(subscription.getFeed().getMessage()); - sub.setErrorCount(subscription.getFeed().getErrorCount()); - sub.setFeedUrl(subscription.getFeed().getLink()); Long size = unreadCount.get(subscription.getId()); - sub.setUnread(size == null ? 0 : size); + long unread = size == null ? 0 : size; + Subscription sub = Subscription.build(subscription, unread); category.getFeeds().add(sub); } } diff --git a/src/main/java/com/commafeed/frontend/rest/resources/FeedREST.java b/src/main/java/com/commafeed/frontend/rest/resources/FeedREST.java index a1a00ecc..a7c46b01 100644 --- a/src/main/java/com/commafeed/frontend/rest/resources/FeedREST.java +++ b/src/main/java/com/commafeed/frontend/rest/resources/FeedREST.java @@ -10,6 +10,7 @@ import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; +import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.WebApplicationException; @@ -34,9 +35,10 @@ import com.commafeed.backend.model.UserSettings.ReadingOrder; import com.commafeed.frontend.model.Entries; import com.commafeed.frontend.model.Entry; import com.commafeed.frontend.model.FeedInfo; +import com.commafeed.frontend.model.Subscription; import com.commafeed.frontend.model.request.IDRequest; import com.commafeed.frontend.model.request.MarkRequest; -import com.commafeed.frontend.model.request.RenameRequest; +import com.commafeed.frontend.model.request.FeedModificationRequest; import com.commafeed.frontend.model.request.SubscribeRequest; import com.commafeed.frontend.rest.Enums.ReadType; import com.google.common.base.Preconditions; @@ -191,6 +193,17 @@ public class FeedREST extends AbstractResourceREST { return Response.ok(Status.OK).build(); } + @GET + @Path("/get/{id}") + @ApiOperation(value = "", notes = "") + public Subscription get( + @ApiParam(value = "user id", required = true) @PathParam("id") Long id) { + + Preconditions.checkNotNull(id); + FeedSubscription sub = feedSubscriptionDAO.findById(getUser(), id); + return Subscription.build(sub, 0); + } + @POST @Path("/subscribe") @ApiOperation(value = "Subscribe to a feed", notes = "Subscribe to a feed") @@ -237,10 +250,10 @@ public class FeedREST extends AbstractResourceREST { } @POST - @Path("/rename") - @ApiOperation(value = "Rename a subscription", notes = "Rename a feed subscription") - public Response rename( - @ApiParam(value = "subscription id", required = true) RenameRequest req) { + @Path("/modify") + @ApiOperation(value = "Modify a subscription", notes = "Modify a feed subscription") + public Response modify( + @ApiParam(value = "subscription id", required = true) FeedModificationRequest req) { Preconditions.checkNotNull(req); Preconditions.checkNotNull(req.getId()); Preconditions.checkNotNull(req.getName()); @@ -248,6 +261,14 @@ public class FeedREST extends AbstractResourceREST { FeedSubscription subscription = feedSubscriptionDAO.findById(getUser(), req.getId()); subscription.setTitle(req.getName()); + + FeedCategory parent = null; + if (req.getCategoryId() != null + && !CategoryREST.ALL.equals(req.getCategoryId())) { + parent = feedCategoryDAO.findById(getUser(), + Long.valueOf(req.getCategoryId())); + } + subscription.setCategory(parent); feedSubscriptionDAO.update(subscription); return Response.ok(Status.OK).build(); diff --git a/src/main/webapp/css/app.css b/src/main/webapp/css/app.css index 5de147e8..3cceceb0 100644 --- a/src/main/webapp/css/app.css +++ b/src/main/webapp/css/app.css @@ -6,6 +6,11 @@ margin-bottom: 0px; } +.horizontal-align { + padding-top: 5px; + line-height: 20px +} + .welcome .header { margin: 20px 0 40px 0; } diff --git a/src/main/webapp/directives/category.html b/src/main/webapp/directives/category.html index 3e1fe6a9..092decd5 100644 --- a/src/main/webapp/directives/category.html +++ b/src/main/webapp/directives/category.html @@ -1,17 +1,9 @@
  • @@ -32,22 +24,12 @@
  • -
  • diff --git a/src/main/webapp/js/controllers.js b/src/main/webapp/js/controllers.js index 4bb48ffa..3e7250aa 100644 --- a/src/main/webapp/js/controllers.js +++ b/src/main/webapp/js/controllers.js @@ -169,6 +169,136 @@ function($scope, $timeout, $stateParams, $window, $location, $state, $route, Cat }); }]); +module.controller('FeedDetailsCtrl', ['$scope', '$state', '$stateParams', 'FeedService', 'CategoryService', '$dialog', + function($scope, $state, $stateParams, FeedService, CategoryService, $dialog) { + + $scope.CategoryService = CategoryService; + + $scope.sub = FeedService.get({ + id : $stateParams._id + }); + + $scope.back = function() { + $state.transitionTo('feeds.view', { + _id: $stateParams._id, + _type: 'feed' + }); + }; + + $scope.unsubscribe = function() { + var sub = $scope.sub; + var title = 'Unsubscribe'; + var msg = 'Unsubscribe from ' + sub.name + ' ?'; + var btns = [ { + result : 'cancel', + label : 'Cancel' + }, { + result : 'ok', + label : 'OK', + cssClass : 'btn-primary' + } ]; + + $dialog.messageBox(title, msg, btns).open().then( + function(result) { + if (result == 'ok') { + var data = { + id : sub.id + }; + FeedService.unsubscribe(data, + function() { + CategoryService.init(); + }); + $state.transitionTo('feeds.view', { + _id: 'all', + _type: 'category' + }); + } + }); + }; + + $scope.save = function() { + var sub = $scope.sub; + FeedService.modify({ + id : sub.id, + name : sub.name, + categoryId : sub.categoryId + }, function() { + CategoryService.init(); + $state.transitionTo('feeds.view', { + _id: 'all', + _type: 'category' + }); + }); + }; +}]); + +module.controller('CategoryDetailsCtrl', ['$scope', '$state', '$stateParams', 'FeedService', 'CategoryService', '$dialog', + function($scope, $state, $stateParams, FeedService, CategoryService, $dialog) { + $scope.CategoryService = CategoryService; + + CategoryService.get(function() { + for(var i = 0; i < CategoryService.flatCategories.length; i++){ + var cat = CategoryService.flatCategories[i]; + if (cat.id == $stateParams._id) { + $scope.category = angular.copy(cat); + $scope.category.name = $scope.category.origName; + break; + } + } + }); + + $scope.back = function() { + $state.transitionTo('feeds.view', { + _id: $stateParams._id, + _type: 'category' + }); + }; + + $scope.deleteCategory = function() { + var category = $scope.category; + var title = 'Delete category'; + var msg = 'Delete category ' + category.name + ' ?'; + var btns = [ { + result : 'cancel', + label : 'Cancel' + }, { + result : 'ok', + label : 'OK', + cssClass : 'btn-primary' + } ]; + + $dialog.messageBox(title, msg, btns).open().then( + function(result) { + if (result == 'ok') { + CategoryService.remove({ + id : category.id + }, function() { + CategoryService.init(); + }); + $state.transitionTo('feeds.view', { + _id: 'all', + _type: 'category' + }); + } + }); + }; + + $scope.save = function() { + var cat = $scope.category; + CategoryService.modify({ + id : cat.id, + name : cat.name, + parentId : cat.parentId + }, function() { + CategoryService.init(); + $state.transitionTo('feeds.view', { + _id: 'all', + _type: 'category' + }); + }); + }; +}]); + module.controller('ToolbarCtrl', ['$scope', '$http', '$state', '$stateParams', '$route', '$location', 'SettingsService', 'EntryService', 'ProfileService', 'AnalyticsService', 'ServerService', 'FeedService', function($scope, $http, $state, $stateParams, $route, $location, diff --git a/src/main/webapp/js/directives.js b/src/main/webapp/js/directives.js index ad3e8f86..8a6b8f9a 100644 --- a/src/main/webapp/js/directives.js +++ b/src/main/webapp/js/directives.js @@ -112,32 +112,6 @@ module.directive('category', [ function() { function($scope, $state, $dialog, FeedService, CategoryService, SettingsService) { $scope.settingsService = SettingsService; - $scope.unsubscribe = function(subscription) { - var title = 'Unsubscribe'; - var msg = 'Unsubscribe from ' + subscription.name - + ' ?'; - var btns = [ { - result : 'cancel', - label : 'Cancel' - }, { - result : 'ok', - label : 'OK', - cssClass : 'btn-primary' - } ]; - - $dialog.messageBox(title, msg, btns).open().then( - function(result) { - if (result == 'ok') { - var data = { - id : subscription.id - }; - FeedService.unsubscribe(data, - function() { - CategoryService.init(); - }); - } - }); - }; $scope.formatCategoryName = function(category) { var count = $scope.unreadCount({ @@ -181,52 +155,17 @@ module.directive('category', [ function() { }); } }; - - $scope.renameFeed = function(feed) { - var name = window.prompt('Rename feed : ', feed.name); - if (name && name != feed.name) { - feed.name = name; - FeedService.rename({ - id : feed.id, - name : name - }); - } + + $scope.showFeedDetails = function(feed) { + $state.transitionTo('feeds.feed_details', { + _id: feed.id + }); }; - - $scope.renameCategory = function(category) { - var name = window.prompt('Rename category: ', - category.name); - if (name && name != category.name) { - category.name = name; - CategoryService.rename({ - id : category.id, - name : name - }); - } - }; - - $scope.deleteCategory = function(category) { - var title = 'Delete category'; - var msg = 'Delete category ' + category.name + ' ?'; - var btns = [ { - result : 'cancel', - label : 'Cancel' - }, { - result : 'ok', - label : 'OK', - cssClass : 'btn-primary' - } ]; - - $dialog.messageBox(title, msg, btns).open().then( - function(result) { - if (result == 'ok') { - CategoryService.remove({ - id : category.id - }, function() { - CategoryService.init(); - }); - } - }); + + $scope.showCategoryDetails = function(category) { + $state.transitionTo('feeds.category_details', { + _id: category.id + }); }; $scope.toggleCategory = function(category) { diff --git a/src/main/webapp/js/main.js b/src/main/webapp/js/main.js index 3295387f..db1e8d13 100644 --- a/src/main/webapp/js/main.js +++ b/src/main/webapp/js/main.js @@ -23,6 +23,16 @@ function($routeProvider, $stateProvider, $urlRouterProvider) { templateUrl : 'templates/feeds.view.html', controller : 'FeedListCtrl' }); + $stateProvider.state('feeds.feed_details', { + url : '/details/feed/:_id', + templateUrl : 'templates/feeds.feed_details.html', + controller : 'FeedDetailsCtrl' + }); + $stateProvider.state('feeds.category_details', { + url : '/details/category/:_id', + templateUrl : 'templates/feeds.category_details.html', + controller : 'CategoryDetailsCtrl' + }); $stateProvider.state('feeds.help', { url : '/help', templateUrl : 'templates/feeds.help.html', diff --git a/src/main/webapp/js/services.js b/src/main/webapp/js/services.js index 1ec25b5b..2ff9afeb 100644 --- a/src/main/webapp/js/services.js +++ b/src/main/webapp/js/services.js @@ -80,14 +80,15 @@ function($resource, $http) { _method : 'unsubscribe' } }, - rename : { + modify : { method : 'POST', params : { - _method : 'rename' + _method : 'modify' } } }; var res = $resource('rest/feed/:_method', {}, actions); + res.get = $resource('rest/feed/get/:id').get; return res; }]); @@ -102,7 +103,8 @@ function($resource, $http) { } array.push({ id : category.id, - name : name + name : name, + origName: category.name }); if (category.children) { for ( var i = 0; i < category.children.length; i++) { @@ -142,10 +144,10 @@ function($resource, $http) { _method : 'delete' } }, - rename : { + modify : { method : 'POST', params : { - _method : 'rename' + _method : 'modify' } }, collapse : { diff --git a/src/main/webapp/templates/feeds.category_details.html b/src/main/webapp/templates/feeds.category_details.html new file mode 100644 index 00000000..554ecd61 --- /dev/null +++ b/src/main/webapp/templates/feeds.category_details.html @@ -0,0 +1,38 @@ +
    + +
    +
    + +
    + + Required +
    +
    + +
    + +
    + + Required +
    +
    + + + +
    + + + +
    +
    + +
    diff --git a/src/main/webapp/templates/feeds.feed_details.html b/src/main/webapp/templates/feeds.feed_details.html new file mode 100644 index 00000000..d6cfd977 --- /dev/null +++ b/src/main/webapp/templates/feeds.feed_details.html @@ -0,0 +1,51 @@ +
    + +
    +
    + + +
    +
    + +
    + + Required +
    +
    + +
    + +
    + + Required +
    +
    + +
    + +
    + {{sub.lastRefresh|date:'medium'}} +
    +
    + + + +
    + + + +
    +
    + +
    diff --git a/src/main/webapp/templates/feeds.view.html b/src/main/webapp/templates/feeds.view.html index 113725f8..f7d178bf 100644 --- a/src/main/webapp/templates/feeds.view.html +++ b/src/main/webapp/templates/feeds.view.html @@ -13,7 +13,7 @@ - + {{entry.feedName}}