* displaying a quote looks slightly prettier, although still hideous
* implemented recent quotes page
This commit is contained in:
parent
d917480c78
commit
a5f96076dc
@ -64,6 +64,15 @@ namespace VideoGameQuotes.Api {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual VoteDirection? VotedFor(User user) {
|
||||||
|
var vote = Votes.Where(v => v.Voter == user).SingleOrDefault();
|
||||||
|
if (vote == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vote.Direction;
|
||||||
|
}
|
||||||
|
|
||||||
public virtual int UpVotes { get { return Votes.Count(vote => vote.Direction == VoteDirection.Up); } }
|
public virtual int UpVotes { get { return Votes.Count(vote => vote.Direction == VoteDirection.Up); } }
|
||||||
public virtual int DownVotes { get { return Votes.Count(vote => vote.Direction == VoteDirection.Down); } }
|
public virtual int DownVotes { get { return Votes.Count(vote => vote.Direction == VoteDirection.Down); } }
|
||||||
public virtual int NetVotes { get { return Votes.Sum(vote => (int)vote); } }
|
public virtual int NetVotes { get { return Votes.Sum(vote => (int)vote); } }
|
||||||
|
@ -6,7 +6,6 @@ using System.Web.Mvc;
|
|||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Portoa.Persistence;
|
using Portoa.Persistence;
|
||||||
using Portoa.Web.Controllers;
|
using Portoa.Web.Controllers;
|
||||||
using Portoa.Web.ErrorHandling;
|
|
||||||
using Portoa.Web.Results;
|
using Portoa.Web.Results;
|
||||||
using VideoGameQuotes.Api;
|
using VideoGameQuotes.Api;
|
||||||
using VideoGameQuotes.Web.Models;
|
using VideoGameQuotes.Web.Models;
|
||||||
@ -23,6 +22,12 @@ namespace VideoGameQuotes.Web.Controllers {
|
|||||||
this.currentUserProvider = currentUserProvider;
|
this.currentUserProvider = currentUserProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ActionResult Recent() {
|
||||||
|
//get last 10 submitted quotes
|
||||||
|
var quotes = quoteService.GetMostRecentQuotes(10);
|
||||||
|
return View(new QuoteCollectionModel { Quotes = quotes, User = currentUserProvider.CurrentUser });
|
||||||
|
}
|
||||||
|
|
||||||
[IsValidUser]
|
[IsValidUser]
|
||||||
public ActionResult Submit() {
|
public ActionResult Submit() {
|
||||||
var model = new QuoteSubmitModel();
|
var model = new QuoteSubmitModel();
|
||||||
@ -140,7 +145,12 @@ namespace VideoGameQuotes.Web.Controllers {
|
|||||||
|
|
||||||
public ActionResult Quote(int id) {
|
public ActionResult Quote(int id) {
|
||||||
try {
|
try {
|
||||||
return View(quoteService.GetQuote(id));
|
var model = new QuoteModel {
|
||||||
|
Quote = quoteService.GetQuote(id),
|
||||||
|
User = currentUserProvider.CurrentUser
|
||||||
|
};
|
||||||
|
|
||||||
|
return View(model);
|
||||||
} catch (EntityNotFoundException) {
|
} catch (EntityNotFoundException) {
|
||||||
return new StatusOverrideResult(View("QuoteNotFound")) { StatusCode = HttpStatusCode.NotFound };
|
return new StatusOverrideResult(View("QuoteNotFound")) { StatusCode = HttpStatusCode.NotFound };
|
||||||
}
|
}
|
||||||
@ -156,6 +166,5 @@ namespace VideoGameQuotes.Web.Controllers {
|
|||||||
return Json(this.CreateJsonErrorResponse(e));
|
return Json(this.CreateJsonErrorResponse(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -41,6 +41,8 @@ namespace VideoGameQuotes.Web {
|
|||||||
routes.MapRoute("contact", "contact", new { controller = "Home", action = "Contact" });
|
routes.MapRoute("contact", "contact", new { controller = "Home", action = "Contact" });
|
||||||
routes.MapRoute("submit", "submit", new { controller = "Quote", action = "Submit" });
|
routes.MapRoute("submit", "submit", new { controller = "Quote", action = "Submit" });
|
||||||
routes.MapRoute("search", "search", new { controller = "Quote", action = "Search" });
|
routes.MapRoute("search", "search", new { controller = "Quote", action = "Search" });
|
||||||
|
routes.MapRoute("recent", "recent", new { controller = "Quote", action = "Recent" });
|
||||||
|
routes.MapRoute("random", "random", new { controller = "Quote", action = "Random" });
|
||||||
routes.MapRoute("create-category", "category/create", new { controller = "Quote", action = "CreateCategory" });
|
routes.MapRoute("create-category", "category/create", new { controller = "Quote", action = "CreateCategory" });
|
||||||
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("Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional });
|
routes.MapRoute("Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional });
|
||||||
|
23
Src/VideoGameQuotes.Web/Models/QuoteModel.cs
Normal file
23
Src/VideoGameQuotes.Web/Models/QuoteModel.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using VideoGameQuotes.Api;
|
||||||
|
|
||||||
|
namespace VideoGameQuotes.Web.Models {
|
||||||
|
|
||||||
|
public class QuoteCollectionModel {
|
||||||
|
public IEnumerable<Quote> Quotes { get; set; }
|
||||||
|
public User User { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class QuoteModel {
|
||||||
|
public Quote Quote { get; set; }
|
||||||
|
public User User { get; set; }
|
||||||
|
|
||||||
|
public bool VotedUp {
|
||||||
|
get { return Quote.VotedFor(User) == VoteDirection.Up; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool VotedDown {
|
||||||
|
get { return Quote.VotedFor(User) == VoteDirection.Down; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Portoa.Persistence;
|
using Portoa.Persistence;
|
||||||
using VideoGameQuotes.Api;
|
using VideoGameQuotes.Api;
|
||||||
|
|
||||||
@ -16,6 +17,7 @@ namespace VideoGameQuotes.Web.Services {
|
|||||||
GamingSystem GetSystem(int systemId);
|
GamingSystem GetSystem(int systemId);
|
||||||
Category GetCategory(int categoryId);
|
Category GetCategory(int categoryId);
|
||||||
Category SaveCategory(Category category);
|
Category SaveCategory(Category category);
|
||||||
|
IEnumerable<Quote> GetMostRecentQuotes(int limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class QuoteService : IQuoteService {
|
public class QuoteService : IQuoteService {
|
||||||
@ -92,5 +94,13 @@ namespace VideoGameQuotes.Web.Services {
|
|||||||
public Category SaveCategory(Category category) {
|
public Category SaveCategory(Category category) {
|
||||||
return categoryRepository.Save(category);
|
return categoryRepository.Save(category);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[UnitOfWork]
|
||||||
|
public IEnumerable<Quote> GetMostRecentQuotes(int limit) {
|
||||||
|
return quoteRepository
|
||||||
|
.Records
|
||||||
|
.OrderByDescending(quote => quote.Created)
|
||||||
|
.Take(limit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -85,6 +85,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Controllers\HomeController.cs" />
|
<Compile Include="Controllers\HomeController.cs" />
|
||||||
|
<Compile Include="Models\QuoteModel.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" />
|
||||||
@ -108,6 +109,7 @@
|
|||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="media\css\global.css" />
|
<Content Include="media\css\global.css" />
|
||||||
|
<Content Include="media\css\quote.css" />
|
||||||
<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" />
|
||||||
@ -116,12 +118,14 @@
|
|||||||
<Content Include="Views\Home\ContactSuccess.aspx" />
|
<Content Include="Views\Home\ContactSuccess.aspx" />
|
||||||
<Content Include="Views\Quote\Quote.aspx" />
|
<Content Include="Views\Quote\Quote.aspx" />
|
||||||
<Content Include="Views\Quote\QuoteNotFound.aspx" />
|
<Content Include="Views\Quote\QuoteNotFound.aspx" />
|
||||||
|
<Content Include="Views\Quote\Recent.aspx" />
|
||||||
<Content Include="Views\Quote\Submit.aspx" />
|
<Content Include="Views\Quote\Submit.aspx" />
|
||||||
<Content Include="Views\Shared\ExceptionView.ascx" />
|
<Content Include="Views\Shared\ExceptionView.ascx" />
|
||||||
<Content Include="Views\Shared\Forbidden.aspx" />
|
<Content Include="Views\Shared\Forbidden.aspx" />
|
||||||
<Content Include="Views\Shared\NotFound.aspx" />
|
<Content Include="Views\Shared\NotFound.aspx" />
|
||||||
<Content Include="Views\Shared\NotFoundContent.ascx" />
|
<Content Include="Views\Shared\NotFoundContent.ascx" />
|
||||||
<Content Include="Views\Shared\RecursiveExceptionView.ascx" />
|
<Content Include="Views\Shared\RecursiveExceptionView.ascx" />
|
||||||
|
<Content Include="Views\Shared\SingleQuote.ascx" />
|
||||||
<Content Include="Views\Shared\Unknown.aspx" />
|
<Content Include="Views\Shared\Unknown.aspx" />
|
||||||
<Content Include="Web.config" />
|
<Content Include="Web.config" />
|
||||||
<Content Include="Web.Debug.config">
|
<Content Include="Web.Debug.config">
|
||||||
|
@ -1,24 +1,5 @@
|
|||||||
<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<VideoGameQuotes.Api.Quote>" MasterPageFile="~/Views/Shared/Site.Master" %>
|
<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<VideoGameQuotes.Web.Models.QuoteModel>" MasterPageFile="~/Views/Shared/Site.Master" %>
|
||||||
<%@ Import Namespace="VideoGameQuotes.Api" %>
|
<asp:Content runat="server" ID="Title" ContentPlaceHolderID="TitleContent"><%: Model.Quote.Game.Name %></asp:Content>
|
||||||
<asp:Content runat="server" ID="Title" ContentPlaceHolderID="TitleContent"><%: Model.Game.Name %></asp:Content>
|
|
||||||
<asp:Content runat="server" ID="Main" ContentPlaceHolderID="MainContent">
|
<asp:Content runat="server" ID="Main" ContentPlaceHolderID="MainContent">
|
||||||
<div class="quote">
|
<% Html.RenderPartial("SingleQuote", Model); %>
|
||||||
<span class="vote-for" title="I like this quote"><a href="#" id="vote-for">↑</a></span>
|
</asp:Content>
|
||||||
<span class="vote-against" title="I dislike this quote"><a href="#" id="vote-against">↓</a></span>
|
|
||||||
<span class="quote-report-link" title="report this quote as inaccurate, fake, spam, duplicate, etc."><a href="#" id="quote-report-link">report</a></span>
|
|
||||||
|
|
||||||
<p class="quote-text">
|
|
||||||
<%: Model.Text %>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="quote-details">
|
|
||||||
<dl>
|
|
||||||
<dt>Game</dt>
|
|
||||||
<dd><%= Html.ActionLink(Model.Game.Name, "Index", "Quote", new { game = Model.Game.Id }, null) %></dd>
|
|
||||||
<dt>Added</dt>
|
|
||||||
<dd><%: Model.GetHumanReadableTimeSinceCreated() %></dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</asp:Content>
|
|
||||||
<asp:Content runat="server" ID="DeferrableScripts" ContentPlaceHolderID="DeferrableScripts"></asp:Content>
|
|
10
Src/VideoGameQuotes.Web/Views/Quote/Recent.aspx
Normal file
10
Src/VideoGameQuotes.Web/Views/Quote/Recent.aspx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<VideoGameQuotes.Web.Models.QuoteCollectionModel>" MasterPageFile="~/Views/Shared/Site.Master" %>
|
||||||
|
<%@ Import Namespace="VideoGameQuotes.Web.Models" %>
|
||||||
|
<asp:Content runat="server" ID="Title" ContentPlaceHolderID="TitleContent">Recently Submitted Quotes</asp:Content>
|
||||||
|
<asp:Content runat="server" ID="Main" ContentPlaceHolderID="MainContent">
|
||||||
|
<%
|
||||||
|
foreach (var quote in Model.Quotes) {
|
||||||
|
Html.RenderPartial("SingleQuote", new QuoteModel { Quote = quote, User = Model.User });
|
||||||
|
}
|
||||||
|
%>
|
||||||
|
</asp:Content>
|
37
Src/VideoGameQuotes.Web/Views/Shared/SingleQuote.ascx
Normal file
37
Src/VideoGameQuotes.Web/Views/Shared/SingleQuote.ascx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<VideoGameQuotes.Web.Models.QuoteModel>" %>
|
||||||
|
<%@ Import Namespace="VideoGameQuotes.Api" %>
|
||||||
|
|
||||||
|
<div class="quote-container">
|
||||||
|
<div class="quote-data clearfix">
|
||||||
|
<div class="quote-score-container">
|
||||||
|
<div class="vote-container">
|
||||||
|
<% if (!Model.VotedUp) { %>
|
||||||
|
<span class="vote-for" title="I like this quote">▲</span>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
<div class="quote-score" title="+<%= Model.Quote.UpVotes %>, -<%= Model.Quote.DownVotes %>"><%= Model.Quote.NetVotes %></div>
|
||||||
|
<div class="vote-container">
|
||||||
|
<% if (!Model.VotedDown) { %>
|
||||||
|
<span class="vote-against" title="I hate this quote">▼</span>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="quote-text">
|
||||||
|
<%: Model.Quote.Text %>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clearfix">
|
||||||
|
<a class="quote-report-link" href="#" title="report this quote as inaccurate, fake, spam, duplicate, etc.">report</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<dt>Added</dt>
|
||||||
|
<dd><%: Model.Quote.GetHumanReadableTimeSinceCreated() %></dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -8,6 +8,7 @@
|
|||||||
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
|
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
|
||||||
<link rel="stylesheet" type="text/css" href="/media/css/reset.css" />
|
<link rel="stylesheet" type="text/css" href="/media/css/reset.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="/media/css/global.css" />
|
<link rel="stylesheet" type="text/css" href="/media/css/global.css" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="/media/css/quote.css" />
|
||||||
<link rel="shortcut icon" type="image/png" href="/media/images/favicon.png" />
|
<link rel="shortcut icon" type="image/png" href="/media/images/favicon.png" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
@ -42,6 +42,9 @@ ul.menu {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
ul.menu li {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
.validation-summary-errors {
|
.validation-summary-errors {
|
||||||
color: #000000;
|
color: #000000;
|
||||||
@ -117,7 +120,6 @@ ul.menu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#main-menu li {
|
#main-menu li {
|
||||||
float: left;
|
|
||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
}
|
}
|
||||||
#main-menu li a {
|
#main-menu li a {
|
||||||
@ -140,6 +142,7 @@ ul.menu {
|
|||||||
color: #669966;
|
color: #669966;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
}
|
}
|
||||||
#main a:hover {
|
#main a:hover {
|
||||||
border-bottom: 2px solid #000000;
|
border-bottom: 2px solid #000000;
|
||||||
|
35
Src/VideoGameQuotes.Web/media/css/quote.css
Normal file
35
Src/VideoGameQuotes.Web/media/css/quote.css
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
.quote-container {
|
||||||
|
width: 500px;
|
||||||
|
margin: auto;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote-container .quote-data .quote-score-container {
|
||||||
|
float: left;
|
||||||
|
font-family: Georgia, serif;
|
||||||
|
padding: 5px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
background-color: #6699FF;
|
||||||
|
}
|
||||||
|
.quote-score {
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
.quote-score, .vote-container {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.vote-for, .vote-against {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.vote-for:hover, .vote-against:hover {
|
||||||
|
color: #FFFF99;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote-container .quote-data .quote-text {
|
||||||
|
float: left;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote-report-link {
|
||||||
|
float: right;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user