implemented voting for quotes
This commit is contained in:
parent
7d51b80dcf
commit
cdd93afeff
@ -22,6 +22,30 @@ namespace VideoGameQuotes.Web.Controllers {
|
||||
this.currentUserProvider = currentUserProvider;
|
||||
}
|
||||
|
||||
[HttpPost, IsValidUser]
|
||||
public JsonResult Vote(VoteModel model) {
|
||||
if (!ModelState.IsValid) {
|
||||
return Json(this.CreateJsonErrorResponse("Invalid request"));
|
||||
}
|
||||
|
||||
try {
|
||||
var vote = quoteService.GetVoteOrCreateNew(quoteService.GetQuote(model.QuoteId), currentUserProvider.CurrentUser);
|
||||
vote.Direction = model.Direction;
|
||||
|
||||
vote = quoteService.SaveVote(vote);
|
||||
|
||||
var data = new Dictionary<string, string> {
|
||||
{ "netVotes", vote.Quote.NetVotes.ToString() },
|
||||
{ "upVotes", vote.Quote.UpVotes.ToString() },
|
||||
{ "downVotes", vote.Quote.DownVotes.ToString() }
|
||||
};
|
||||
|
||||
return Json(this.CreateJsonResponse(null, data));
|
||||
} catch {
|
||||
return Json(this.CreateJsonErrorResponse("An error occurred while trying to process your vote"));
|
||||
}
|
||||
}
|
||||
|
||||
public ActionResult Recent() {
|
||||
//get last 10 submitted quotes
|
||||
var quotes = quoteService.GetMostRecentQuotes(10);
|
||||
|
@ -39,7 +39,7 @@ namespace VideoGameQuotes.Web {
|
||||
|
||||
routes.MapRoute("home", "{action}", new { controller = "Home", action = "Index" }, new { action = "about|contact" });
|
||||
routes.MapRoute("best", "best/{start}-{end}/", new { controller = "Quote", action = "Best" }, new { start = @"\d+", end = @"\d+" });
|
||||
routes.MapRoute("quote", "{action}", new { controller = "Quote" }, new { action = "submit|search|recent|random|best|browse" });
|
||||
routes.MapRoute("quote", "{action}", new { controller = "Quote" }, new { action = "submit|search|recent|random|best|browse|vote" });
|
||||
routes.MapRoute("individual-quote", "quote/{id}/{*text}", new { controller = "Quote", action = "Quote" }, new { id = @"\d+" });
|
||||
routes.MapRoute("create-category", "category/create", new { controller = "Quote", action = "CreateCategory" });
|
||||
|
||||
|
@ -4,7 +4,6 @@ using JetBrains.Annotations;
|
||||
using VideoGameQuotes.Api;
|
||||
|
||||
namespace VideoGameQuotes.Web.Models {
|
||||
|
||||
public class QuoteCollectionModel {
|
||||
public IEnumerable<Quote> Quotes { get; set; }
|
||||
public User User { get; set; }
|
||||
|
11
Src/VideoGameQuotes.Web/Models/VoteModel.cs
Normal file
11
Src/VideoGameQuotes.Web/Models/VoteModel.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using Portoa.Validation.DataAnnotations;
|
||||
using VideoGameQuotes.Api;
|
||||
|
||||
namespace VideoGameQuotes.Web.Models {
|
||||
public class VoteModel {
|
||||
[GreaterThanZero]
|
||||
public int QuoteId { get; set; }
|
||||
|
||||
public VoteDirection Direction { get; set; }
|
||||
}
|
||||
}
|
@ -22,6 +22,9 @@ namespace VideoGameQuotes.Web.Services {
|
||||
[CanBeNull]
|
||||
Quote GetRandomQuote();
|
||||
IEnumerable<Quote> GetBestQuotes(int start, int end);
|
||||
Vote SaveVote(Vote vote);
|
||||
[NotNull]
|
||||
Vote GetVoteOrCreateNew(Quote quote, User voter);
|
||||
}
|
||||
|
||||
public class QuoteService : IQuoteService {
|
||||
@ -30,15 +33,19 @@ namespace VideoGameQuotes.Web.Services {
|
||||
private readonly IRepository<GamingSystem> systemRepository;
|
||||
private readonly IRepository<Publisher> publisherRepository;
|
||||
private readonly IRepository<Category> categoryRepository;
|
||||
private readonly IRepository<Vote> voteRepository;
|
||||
|
||||
public QuoteService(
|
||||
IRepository<Quote> quoteRepository,
|
||||
IRepository<Game> gameRepository,
|
||||
IRepository<GamingSystem> systemRepository,
|
||||
IRepository<Publisher> publisherRepository, IRepository<Category> categoryRepository
|
||||
IRepository<Publisher> publisherRepository,
|
||||
IRepository<Category> categoryRepository,
|
||||
IRepository<Vote> voteRepository
|
||||
) {
|
||||
this.quoteRepository = quoteRepository;
|
||||
this.categoryRepository = categoryRepository;
|
||||
this.voteRepository = voteRepository;
|
||||
this.gameRepository = gameRepository;
|
||||
this.systemRepository = systemRepository;
|
||||
this.publisherRepository = publisherRepository;
|
||||
@ -122,9 +129,24 @@ namespace VideoGameQuotes.Web.Services {
|
||||
var records = quoteRepository.Records.ToArray();
|
||||
|
||||
return records
|
||||
.OrderByDescending(quote => quote.UpVotes)
|
||||
.OrderByDescending(quote => quote.NetVotes)
|
||||
.ThenByDescending(quote => quote.UpVotes)
|
||||
.Skip(start)
|
||||
.Take(end - start + 1);
|
||||
}
|
||||
|
||||
[UnitOfWork]
|
||||
public Vote SaveVote(Vote vote) {
|
||||
return voteRepository.Save(vote);
|
||||
}
|
||||
|
||||
[UnitOfWork]
|
||||
public Vote GetVoteOrCreateNew(Quote quote, User voter) {
|
||||
var vote = voteRepository.Records.SingleOrDefault(v => v.Quote == quote && v.Voter == voter);
|
||||
return vote ?? new Vote {
|
||||
Quote = quote,
|
||||
Voter = voter
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -86,6 +86,7 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="Controllers\HomeController.cs" />
|
||||
<Compile Include="Models\QuoteModel.cs" />
|
||||
<Compile Include="Models\VoteModel.cs" />
|
||||
<Compile Include="Validation\NonEmptyText.cs" />
|
||||
<Compile Include="Security\IsValidUserAttribute.cs" />
|
||||
<Compile Include="Controllers\QuoteController.cs" />
|
||||
@ -113,9 +114,11 @@
|
||||
<Content Include="media\css\reset.css" />
|
||||
<Content Include="media\images\favicon.png" />
|
||||
<Content Include="media\images\search.png" />
|
||||
<Content Include="media\js\vgquotes.js" />
|
||||
<Content Include="Views\Home\About.aspx" />
|
||||
<Content Include="Views\Home\Contact.aspx" />
|
||||
<Content Include="Views\Home\ContactSuccess.aspx" />
|
||||
<Content Include="Views\Quote\BadPaging.aspx" />
|
||||
<Content Include="Views\Quote\Best.aspx" />
|
||||
<Content Include="Views\Quote\NoQuotes.aspx" />
|
||||
<Content Include="Views\Quote\Quote.aspx" />
|
||||
@ -142,7 +145,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="App_Data\" />
|
||||
<Folder Include="media\js\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\VideoGameQuotes.Api\VideoGameQuotes.Api.csproj">
|
||||
|
10
Src/VideoGameQuotes.Web/Views/Quote/BadPaging.aspx
Normal file
10
Src/VideoGameQuotes.Web/Views/Quote/BadPaging.aspx
Normal file
@ -0,0 +1,10 @@
|
||||
<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Views/Shared/Site.Master" %>
|
||||
<asp:Content runat="server" ID="Title" ContentPlaceHolderID="TitleContent">Invalid Paging Request</asp:Content>
|
||||
<asp:Content runat="server" ID="Main" ContentPlaceHolderID="MainContent">
|
||||
<h2>Invalid Paging Request</h2>
|
||||
|
||||
<p>
|
||||
Since you’re here, you were probably mucking around with the query string.
|
||||
Please stop.
|
||||
</p>
|
||||
</asp:Content>
|
@ -2,6 +2,7 @@
|
||||
<%@ Import Namespace="VideoGameQuotes.Api" %>
|
||||
|
||||
<div class="quote-container">
|
||||
<input type="hidden" class="quote-id" value="<%= Model.Quote.Id %>" />
|
||||
<div class="quote-data clearfix">
|
||||
<div class="quote-score-container">
|
||||
<div class="vote-container">
|
||||
@ -29,7 +30,7 @@
|
||||
<div class="quote-details">
|
||||
<dl>
|
||||
<dt>Game</dt>
|
||||
<dd><%= Html.ActionLink(Model.Quote.Game.Name, "Index", "Quote", new { game = Model.Quote.Game.Id }, null) %></dd>
|
||||
<dd><%= Html.ActionLink(Model.Quote.Game.Name, "browse", "Quote", new { game = Model.Quote.Game.Id }, null) %></dd>
|
||||
<dt>Added</dt>
|
||||
<dd><%: Model.Quote.GetHumanReadableTimeSinceCreated() %></dd>
|
||||
</dl>
|
||||
|
@ -50,12 +50,13 @@
|
||||
<div id="footer">
|
||||
<div class="content-container">
|
||||
© <%= DateTime.UtcNow.Year %> <a href="http://tommymontgomery.com/" title="Who is this man?">Tommy Montgomery</a><br />
|
||||
If you steal something, I'll murder your family.
|
||||
If you steal something, I’ll murder your family.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/media/js/vgquotes.js"></script>
|
||||
<asp:ContentPlaceHolder ID="DeferrableScripts" runat="server" />
|
||||
</body>
|
||||
</html>
|
||||
|
60
Src/VideoGameQuotes.Web/media/js/vgquotes.js
Normal file
60
Src/VideoGameQuotes.Web/media/js/vgquotes.js
Normal file
@ -0,0 +1,60 @@
|
||||
(function($, window, undefined){
|
||||
$(document).ready(function() {
|
||||
var voting = false;
|
||||
$(".vote-for, .vote-against").live("click", function() {
|
||||
if (voting) {
|
||||
alert("Please wait for the current vote to process before voting again");
|
||||
return false;
|
||||
}
|
||||
|
||||
voting = true;
|
||||
var $votingLink = $(this);
|
||||
var $container = $votingLink.parents(".quote-container");
|
||||
var direction = $votingLink.hasClass("vote-for") ? 1 : 0;
|
||||
var quoteId = $container.find("input.quote-id").val();
|
||||
$.ajax("/vote", {
|
||||
type: "POST",
|
||||
data: {
|
||||
QuoteId: quoteId,
|
||||
Direction: direction
|
||||
},
|
||||
|
||||
success: function(data, status, $xhr) {
|
||||
if (data.Error !== null) {
|
||||
alert(data.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
$container
|
||||
.find(".quote-score")
|
||||
.attr("title", "+" + data.Data.upVotes + ", -" + data.Data.downVotes)
|
||||
.text(data.Data.netVotes);
|
||||
|
||||
//remove the voting arrow, and add the other one if needed
|
||||
$votingLink.remove();
|
||||
if (direction === 1) {
|
||||
if ($container.find(".vote-against").length === 0) {
|
||||
$("<span/>")
|
||||
.addClass("vote-against")
|
||||
.attr("title", "I hate this quote")
|
||||
.text(String.fromCharCode(0x25BC))
|
||||
.appendTo($container.find(".vote-container:last"));
|
||||
}
|
||||
} else {
|
||||
if ($container.find(".vote-for").length === 0) {
|
||||
$("<span/>")
|
||||
.addClass("vote-for")
|
||||
.attr("title", "I like this quote")
|
||||
.text(String.fromCharCode(0x25B2))
|
||||
.appendTo($container.find(".vote-container:first"));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
complete: function() { voting = false; }
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}(jQuery, window));
|
Loading…
Reference in New Issue
Block a user