implemented voting for quotes
This commit is contained in:
parent
7d51b80dcf
commit
cdd93afeff
@ -22,6 +22,30 @@ namespace VideoGameQuotes.Web.Controllers {
|
|||||||
this.currentUserProvider = currentUserProvider;
|
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() {
|
public ActionResult Recent() {
|
||||||
//get last 10 submitted quotes
|
//get last 10 submitted quotes
|
||||||
var quotes = quoteService.GetMostRecentQuotes(10);
|
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("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("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("individual-quote", "quote/{id}/{*text}", new { controller = "Quote", action = "Quote" }, new { id = @"\d+" });
|
||||||
routes.MapRoute("create-category", "category/create", new { controller = "Quote", action = "CreateCategory" });
|
routes.MapRoute("create-category", "category/create", new { controller = "Quote", action = "CreateCategory" });
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ using JetBrains.Annotations;
|
|||||||
using VideoGameQuotes.Api;
|
using VideoGameQuotes.Api;
|
||||||
|
|
||||||
namespace VideoGameQuotes.Web.Models {
|
namespace VideoGameQuotes.Web.Models {
|
||||||
|
|
||||||
public class QuoteCollectionModel {
|
public class QuoteCollectionModel {
|
||||||
public IEnumerable<Quote> Quotes { get; set; }
|
public IEnumerable<Quote> Quotes { get; set; }
|
||||||
public User User { 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]
|
[CanBeNull]
|
||||||
Quote GetRandomQuote();
|
Quote GetRandomQuote();
|
||||||
IEnumerable<Quote> GetBestQuotes(int start, int end);
|
IEnumerable<Quote> GetBestQuotes(int start, int end);
|
||||||
|
Vote SaveVote(Vote vote);
|
||||||
|
[NotNull]
|
||||||
|
Vote GetVoteOrCreateNew(Quote quote, User voter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class QuoteService : IQuoteService {
|
public class QuoteService : IQuoteService {
|
||||||
@ -30,15 +33,19 @@ namespace VideoGameQuotes.Web.Services {
|
|||||||
private readonly IRepository<GamingSystem> systemRepository;
|
private readonly IRepository<GamingSystem> systemRepository;
|
||||||
private readonly IRepository<Publisher> publisherRepository;
|
private readonly IRepository<Publisher> publisherRepository;
|
||||||
private readonly IRepository<Category> categoryRepository;
|
private readonly IRepository<Category> categoryRepository;
|
||||||
|
private readonly IRepository<Vote> voteRepository;
|
||||||
|
|
||||||
public QuoteService(
|
public QuoteService(
|
||||||
IRepository<Quote> quoteRepository,
|
IRepository<Quote> quoteRepository,
|
||||||
IRepository<Game> gameRepository,
|
IRepository<Game> gameRepository,
|
||||||
IRepository<GamingSystem> systemRepository,
|
IRepository<GamingSystem> systemRepository,
|
||||||
IRepository<Publisher> publisherRepository, IRepository<Category> categoryRepository
|
IRepository<Publisher> publisherRepository,
|
||||||
|
IRepository<Category> categoryRepository,
|
||||||
|
IRepository<Vote> voteRepository
|
||||||
) {
|
) {
|
||||||
this.quoteRepository = quoteRepository;
|
this.quoteRepository = quoteRepository;
|
||||||
this.categoryRepository = categoryRepository;
|
this.categoryRepository = categoryRepository;
|
||||||
|
this.voteRepository = voteRepository;
|
||||||
this.gameRepository = gameRepository;
|
this.gameRepository = gameRepository;
|
||||||
this.systemRepository = systemRepository;
|
this.systemRepository = systemRepository;
|
||||||
this.publisherRepository = publisherRepository;
|
this.publisherRepository = publisherRepository;
|
||||||
@ -122,9 +129,24 @@ namespace VideoGameQuotes.Web.Services {
|
|||||||
var records = quoteRepository.Records.ToArray();
|
var records = quoteRepository.Records.ToArray();
|
||||||
|
|
||||||
return records
|
return records
|
||||||
.OrderByDescending(quote => quote.UpVotes)
|
.OrderByDescending(quote => quote.NetVotes)
|
||||||
|
.ThenByDescending(quote => quote.UpVotes)
|
||||||
.Skip(start)
|
.Skip(start)
|
||||||
.Take(end - start + 1);
|
.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>
|
<ItemGroup>
|
||||||
<Compile Include="Controllers\HomeController.cs" />
|
<Compile Include="Controllers\HomeController.cs" />
|
||||||
<Compile Include="Models\QuoteModel.cs" />
|
<Compile Include="Models\QuoteModel.cs" />
|
||||||
|
<Compile Include="Models\VoteModel.cs" />
|
||||||
<Compile Include="Validation\NonEmptyText.cs" />
|
<Compile Include="Validation\NonEmptyText.cs" />
|
||||||
<Compile Include="Security\IsValidUserAttribute.cs" />
|
<Compile Include="Security\IsValidUserAttribute.cs" />
|
||||||
<Compile Include="Controllers\QuoteController.cs" />
|
<Compile Include="Controllers\QuoteController.cs" />
|
||||||
@ -113,9 +114,11 @@
|
|||||||
<Content Include="media\css\reset.css" />
|
<Content Include="media\css\reset.css" />
|
||||||
<Content Include="media\images\favicon.png" />
|
<Content Include="media\images\favicon.png" />
|
||||||
<Content Include="media\images\search.png" />
|
<Content Include="media\images\search.png" />
|
||||||
|
<Content Include="media\js\vgquotes.js" />
|
||||||
<Content Include="Views\Home\About.aspx" />
|
<Content Include="Views\Home\About.aspx" />
|
||||||
<Content Include="Views\Home\Contact.aspx" />
|
<Content Include="Views\Home\Contact.aspx" />
|
||||||
<Content Include="Views\Home\ContactSuccess.aspx" />
|
<Content Include="Views\Home\ContactSuccess.aspx" />
|
||||||
|
<Content Include="Views\Quote\BadPaging.aspx" />
|
||||||
<Content Include="Views\Quote\Best.aspx" />
|
<Content Include="Views\Quote\Best.aspx" />
|
||||||
<Content Include="Views\Quote\NoQuotes.aspx" />
|
<Content Include="Views\Quote\NoQuotes.aspx" />
|
||||||
<Content Include="Views\Quote\Quote.aspx" />
|
<Content Include="Views\Quote\Quote.aspx" />
|
||||||
@ -142,7 +145,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="App_Data\" />
|
<Folder Include="App_Data\" />
|
||||||
<Folder Include="media\js\" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\VideoGameQuotes.Api\VideoGameQuotes.Api.csproj">
|
<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" %>
|
<%@ Import Namespace="VideoGameQuotes.Api" %>
|
||||||
|
|
||||||
<div class="quote-container">
|
<div class="quote-container">
|
||||||
|
<input type="hidden" class="quote-id" value="<%= Model.Quote.Id %>" />
|
||||||
<div class="quote-data clearfix">
|
<div class="quote-data clearfix">
|
||||||
<div class="quote-score-container">
|
<div class="quote-score-container">
|
||||||
<div class="vote-container">
|
<div class="vote-container">
|
||||||
@ -29,7 +30,7 @@
|
|||||||
<div class="quote-details">
|
<div class="quote-details">
|
||||||
<dl>
|
<dl>
|
||||||
<dt>Game</dt>
|
<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>
|
<dt>Added</dt>
|
||||||
<dd><%: Model.Quote.GetHumanReadableTimeSinceCreated() %></dd>
|
<dd><%: Model.Quote.GetHumanReadableTimeSinceCreated() %></dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
@ -50,12 +50,13 @@
|
|||||||
<div id="footer">
|
<div id="footer">
|
||||||
<div class="content-container">
|
<div class="content-container">
|
||||||
© <%= DateTime.UtcNow.Year %> <a href="http://tommymontgomery.com/" title="Who is this man?">Tommy Montgomery</a><br />
|
© <%= 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>
|
</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="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" />
|
<asp:ContentPlaceHolder ID="DeferrableScripts" runat="server" />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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