Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
mythz committed Mar 22, 2024
1 parent f9b7e4a commit a511a2a
Show file tree
Hide file tree
Showing 15 changed files with 189 additions and 97 deletions.
44 changes: 42 additions & 2 deletions MyApp.ServiceInterface/Data/QuestionFiles.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using System.Collections.Concurrent;
using System.Data;
using MyApp.ServiceModel;
using ServiceStack;
using ServiceStack.IO;
using ServiceStack.OrmLite;
using ServiceStack.Text;

namespace MyApp.Data;

Expand Down Expand Up @@ -30,8 +33,10 @@ public class QuestionFiles(int id, string dir1, string dir2, string fileId, List
public string FileId { get; init; } = fileId;
public List<IVirtualFile> Files { get; init; } = files;
public bool LoadedRemotely { get; set; } = remote;
public bool ScoresUpdated { get; set; }
public ConcurrentDictionary<string, string> FileContents { get; } = [];
public QuestionAndAnswers? Question { get; set; }
public Meta? Meta { get; set; }

public async Task<QuestionAndAnswers?> GetQuestionAsync()
{
Expand All @@ -42,6 +47,26 @@ public class QuestionFiles(int id, string dir1, string dir2, string fileId, List
return Question;
}

public void UpdateScores(List<StatTotals> postStats)
{
if (Question == null)
throw new ArgumentNullException(nameof(Question));
if (ScoresUpdated)
return;

ScoresUpdated = true;

var map = postStats.ToDictionary(x => x.Id);
foreach (var answer in Question.Answers)
{
if (map.TryGetValue(answer.Id, out var stat))
{
answer.UpVotes = stat.UpVotes;
answer.DownVotes = stat.DownVotes;
}
}
}

public async Task LoadContentsAsync()
{
if (FileContents.Count > 0) return;
Expand All @@ -65,18 +90,27 @@ public async Task LoadQuestionAndAnswersAsync()
{
to.Post = entry.Value.FromJson<Post>();
}
else if (fileName == $"{FileId}.meta.json")
{
Meta = entry.Value.FromJson<Meta>();
Meta.StatTotals ??= new();
Meta.ModelVotes ??= new();
}
else if (fileName.StartsWith(FileId + ".a."))
{
to.Answers.Add(entry.Value.FromJson<Answer>());
var answer = entry.Value.FromJson<Answer>();
answer.Id = $"{Id}-{answer.Model.Replace(':','-')}";
to.Answers.Add(answer);
}
else if (fileName.StartsWith(FileId + ".h."))
{
var post = entry.Value.FromJson<Post>();
var userName = fileName.Substring((FileId + ".h.").Length).LeftPart('.');
var answer = new Answer
{
Id = $"{post.Id}",
Id = $"{Id}-{userName}",
Model = userName,
Created = (post.LastEditDate ?? post.CreationDate).ToUnixTime(),
UpVotes = userName == "most-voted" ? MostVotedScore : AcceptedScore,
Choices = [
new()
Expand All @@ -96,6 +130,12 @@ public async Task LoadQuestionAndAnswersAsync()

to.Answers.Each(x => x.UpVotes = x.UpVotes == 0 ? ModelScores.GetValueOrDefault(x.Model, 1) : x.UpVotes);
to.Answers.Sort((a, b) => b.Votes - a.Votes);

if (Meta?.StatTotals.Count > 0)
{
UpdateScores(Meta.StatTotals);
}

Question = to;
}
}
1 change: 1 addition & 0 deletions MyApp.ServiceModel/Meta.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@

public class Meta
{
public List<StatTotals> StatTotals { get; set; }
public Dictionary<string, int> ModelVotes { get; set; }
}
1 change: 1 addition & 0 deletions MyApp.ServiceModel/Posts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ public class PostFts
public string UserName { get; set; }
public string Body { get; set; }
public string? Tags { get; set; }
public DateTime ModifiedDate { get; set; }
}

public class Choice
Expand Down
20 changes: 16 additions & 4 deletions MyApp.ServiceModel/Stats.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ public static class Databases
public const string Search = nameof(Search);
}

public class StatTotals
{
public required string Id { get; set; } // PostId or PostId-UserName (Answer)
public int PostId { get; set; }
public int FavoriteCount { get; set; }
public int ViewCount { get; set; }
public int UpVotes { get; set; }
public int DownVotes { get; set; }
public int StartingUpVotes { get; set; }
public DateTime ModifiedDate { get; set; }
}

[NamedConnection(Databases.Analytics)]
public class StatBase
{
Expand All @@ -26,15 +38,15 @@ public class StatBase
}

[Icon(Svg = Icons.Stats)]
public class PostStat : StatBase
public class PostView : StatBase
{
[AutoIncrement]
public int Id { get; set; }
public int PostId { get; set; }
}

[Icon(Svg = Icons.Stats)]
public class SearchStat : StatBase
public class SearchView : StatBase
{
[AutoIncrement]
public int Id { get; set; }
Expand All @@ -46,7 +58,7 @@ public class SearchStat : StatBase
[Restrict(InternalOnly = true)]
public class AnalyticsTasks
{
public SearchStat? RecordSearchStat { get; set; }
public PostStat? RecordPostStat { get; set; }
public SearchView? RecordSearchStat { get; set; }
public PostView? RecordPostStat { get; set; }
}

37 changes: 25 additions & 12 deletions MyApp/Components/Pages/Questions/Index.razor
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
</h1>
</div>


@if (posts.Count > 0)
{
<div class="py-2 flex justify-end">
Expand Down Expand Up @@ -66,7 +65,9 @@
int? Skip => Page > 1 ? (Page - 1) * PageSize : 0;
string Title => "All Questions" + (!string.IsNullOrEmpty(Q) ? $" with '{Q}'" : "");

static string[] Tabs = ["most-votes", "most-views", "most-recent"];
static string[] DefaultTabs = ["interesting", "popular", "newest"];
static string[] SearchTabs = ["relevance", "newest", "oldest"];
string[] Tabs => !string.IsNullOrEmpty(Q) ? SearchTabs : DefaultTabs;

[SupplyParameterFromQuery] string? Q { get; set; }

Expand All @@ -84,14 +85,6 @@
{
try
{
if (Q != null)
{
MessageProducer.Publish(new AnalyticsTasks
{
RecordSearchStat = new SearchStat { Query = Q }.WithRequest(HttpContext)
});
}

if (Tab == null || !Tabs.Contains(Tab))
Tab = Tabs[0];
if (PageSize is null or <= 0)
Expand All @@ -102,6 +95,11 @@

if (!string.IsNullOrEmpty(Q))
{
MessageProducer.Publish(new AnalyticsTasks
{
RecordSearchStat = new SearchView { Query = Q }.WithRequest(HttpContext)
});

using var dbSearch = await DbFactory.OpenAsync(Databases.Search);
var q = dbSearch.From<PostFts>();

Expand All @@ -115,9 +113,21 @@
q.Where("Body match {0}", search);
}

if (Tab == "newest")
{
q.OrderByDescending("ModifiedDate");
}
else if (Tab == "oldest")
{
q.OrderBy("ModifiedDate");
}
else
{
q.OrderBy("Rank");
}

List<PostFts> postsFts = await dbSearch.SelectAsync(q
.Select("RefId, substring(Body,0,400) as Body")
.OrderBy("Rank")
.Select("RefId, substring(Body,0,400) as Body, ModifiedDate")
.Skip(skip)
.Take(take));

Expand All @@ -126,6 +136,8 @@
Id = x.RefId.LeftPart('-').ToInt(),
PostTypeId = x.RefId.Contains('-') ? 2 : 1,
Summary = x.Body.StripHtml().SubstringWithEllipsis(0,200),
CreationDate = x.ModifiedDate,
LastEditDate = x.ModifiedDate,
}).ToList();

var postIds = posts.Select(x => x.Id).ToSet();
Expand All @@ -146,6 +158,7 @@
post.Score = fullPost.Score;
post.ViewCount = fullPost.ViewCount;
post.CreationDate = fullPost.CreationDate;
post.LastEditDate = fullPost.LastEditDate;
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions MyApp/Components/Pages/Questions/Question.razor
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
async Task load()
{
MessageProducer.Publish(new AnalyticsTasks {
RecordPostStat = new PostStat { PostId = Id }.WithRequest(HttpContext)
RecordPostStat = new PostView { PostId = Id }.WithRequest(HttpContext)
});

title = Slug.Replace("-", " ").ToTitleCase();
Expand All @@ -62,7 +62,7 @@
{
var attrPrefix = "<template id=\"Post\">";
var json = Html.IndexOf(attrPrefix, StringComparison.Ordinal) >= 0
? Html.RightPart(attrPrefix).LeftPart("</template>")
? Html.RightPart(attrPrefix).LeftPart("</template>").HtmlDecode()
: null;

if (json != null)
Expand Down
2 changes: 1 addition & 1 deletion MyApp/Components/Pages/Questions/Tagged.razor
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
</div>

@code {
string Path => $"/questions/tagged/{selectedTag}";
string Path => $"/questions/tagged/{selectedTag.UrlEncode()}";
int? Skip => Page > 1 ? (Page - 1) * PageSize : 0;

[Parameter] public required string Slug { get; set; }
Expand Down
2 changes: 1 addition & 1 deletion MyApp/Components/Shared/Aside.razor
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@inject MarkdownBlog Blog

<div class="hidden lg:block w-72 ml-8 pt-12">
<div class="hidden lg:block w-72 ml-8 pt-44">
<div>
<div class="flex">
<svg class="w-8 h-8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M192 32c0 17.7 14.3 32 32 32c123.7 0 224 100.3 224 224c0 17.7 14.3 32 32 32s32-14.3 32-32C512 128.9 383.1 0 224 0c-17.7 0-32 14.3-32 32m0 96c0 17.7 14.3 32 32 32c70.7 0 128 57.3 128 128c0 17.7 14.3 32 32 32s32-14.3 32-32c0-106-86-192-192-192c-17.7 0-32 14.3-32 32m-96 16c0-26.5-21.5-48-48-48S0 117.5 0 144v224c0 79.5 64.5 144 144 144s144-64.5 144-144s-64.5-144-144-144h-16v96h16c26.5 0 48 21.5 48 48s-21.5 48-48 48s-48-21.5-48-48z"/></svg>
Expand Down
43 changes: 25 additions & 18 deletions MyApp/Components/Shared/QuestionPost.razor
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
@inject AppConfig AppConfig
@inject MarkdownQuestions Markdown

<template id="Post">@BlazorHtml.RawJson(Question.Post)</template>
<template id="Post">@Question.Post.ToJson()</template>

<div class="mt-8 mb-20 mx-auto sm:max-w-4xl sm:w-[56rem] xl:max-w-5xl xl:w-[64rem]">
<article>
Expand Down Expand Up @@ -31,10 +31,12 @@
<div class="flex">

<div class="w-20">
<div class="flex flex-col items-center">
<svg class="w-10 h-10" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M3 19h18a1.002 1.002 0 0 0 .823-1.569l-9-13c-.373-.539-1.271-.539-1.645 0l-9 13A.999.999 0 0 0 3 19m9-12.243L19.092 17H4.908z"/></svg>
<div id="@Question.Post.Id" class="flex flex-col items-center">
<!-- bx:up-arrow bxs:up-arrow <path fill="currentColor" d="M3 19h18a1.002 1.002 0 0 0 .823-1.569l-9-13c-.373-.539-1.271-.539-1.645 0l-9 13A.999.999 0 0 0 3 19"/> -->
<svg class="up w-10 h-10 cursor-pointer hover:text-green-600" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M3 19h18a1.002 1.002 0 0 0 .823-1.569l-9-13c-.373-.539-1.271-.539-1.645 0l-9 13A.999.999 0 0 0 3 19m9-12.243L19.092 17H4.908z"/></svg>
<b class="text-xl">@Question.Post.Score</b>
<svg class="w-10 h-10" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M21.886 5.536A1.002 1.002 0 0 0 21 5H3a1.002 1.002 0 0 0-.822 1.569l9 13a.998.998 0 0 0 1.644 0l9-13a.998.998 0 0 0 .064-1.033M12 17.243L4.908 7h14.184z"/></svg>
<!-- bx:down-arrow bxs:down-arrow <path fill="currentColor" d="M11.178 19.569a.998.998 0 0 0 1.644 0l9-13A.999.999 0 0 0 21 5H3a1.002 1.002 0 0 0-.822 1.569z"/> -->
<svg class="down w-10 h-10 cursor-pointer hover:text-green-600" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M21.886 5.536A1.002 1.002 0 0 0 21 5H3a1.002 1.002 0 0 0-.822 1.569l9 13a.998.998 0 0 0 1.644 0l9-13a.998.998 0 0 0 .064-1.033M12 17.243L4.908 7h14.184z"/></svg>
</div>
</div>
<div id="question" class="flex-grow prose">
Expand Down Expand Up @@ -82,23 +84,28 @@
<div>
@foreach (var answer in Question.Answers)
{
<div class="py-8 flex border-b border-gray-200 dark:border-gray-700">
<div class="w-32">
<div class="flex flex-col items-center">
<svg class="w-10 h-10" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M3 19h18a1.002 1.002 0 0 0 .823-1.569l-9-13c-.373-.539-1.271-.539-1.645 0l-9 13A.999.999 0 0 0 3 19m9-12.243L19.092 17H4.908z"/></svg>
<b class="text-xl">@answer.Votes</b>
<svg class="w-10 h-10" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M21.886 5.536A1.002 1.002 0 0 0 21 5H3a1.002 1.002 0 0 0-.822 1.569l9 13a.998.998 0 0 0 1.644 0l9-13a.998.998 0 0 0 .064-1.033M12 17.243L4.908 7h14.184z"/></svg>
<div class="py-8 border-b border-gray-200 dark:border-gray-700">
<div class="flex">
<div class="w-32">
<div id=@answer.Id class="flex flex-col items-center">
<svg class="up w-10 h-10 cursor-pointer hover:text-green-600" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M3 19h18a1.002 1.002 0 0 0 .823-1.569l-9-13c-.373-.539-1.271-.539-1.645 0l-9 13A.999.999 0 0 0 3 19m9-12.243L19.092 17H4.908z"/></svg>
<b class="text-xl">@answer.Votes</b>
<svg class="down w-10 h-10 cursor-pointer hover:text-green-600" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M21.886 5.536A1.002 1.002 0 0 0 21 5H3a1.002 1.002 0 0 0-.822 1.569l9 13a.998.998 0 0 0 1.644 0l9-13a.998.998 0 0 0 .064-1.033M12 17.243L4.908 7h14.184z"/></svg>
</div>
@{
var user = AppConfig.GetApplicationUser(answer.Model);
}
<div class="mt-8 flex flex-col items-center">
<img class="w-20 h-20 inline-block" src="@user.UserName.GetAvatarUrl()">
<div class="text-center whitespace-nowrap text-sm font-semibold">@user.UserName</div>
</div>
</div>
@{
var user = AppConfig.GetApplicationUser(answer.Model);
}
<div class="mt-8 flex flex-col items-center">
<img class="w-20 h-20 inline-block" src="@user.UserName.GetAvatarUrl()">
<div class="text-center whitespace-nowrap text-sm font-semibold">@user.UserName</div>
<div class="flex-grow prose">
@BlazorHtml.Raw(Markdown.GenerateHtml(answer.Choices.FirstOrDefault()?.Message.Content))
</div>
</div>
<div class="flex-grow prose">
@BlazorHtml.Raw(Markdown.GenerateHtml(answer.Choices.FirstOrDefault()?.Message.Content))
<div class="mt-4 flex justify-end text-gray-700 dark:text-gray-200 text-sm">
answered <time class="ml-2" datetime="@Markdown.GetDateTimestamp(DateTimeOffset.FromUnixTimeSeconds(answer.Created).DateTime)">@Markdown.GetDateLabel(DateTimeOffset.FromUnixTimeSeconds(answer.Created).DateTime)</time>
</div>
</div>
}
Expand Down
14 changes: 9 additions & 5 deletions MyApp/Components/Shared/QuestionPosts.razor
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
@foreach (var post in Posts)
{
<div class="flex sm:space-x-6 border-b border-gray-200 dark:border-gray-700 py-4">
<div class="hidden sm:flex flex-col text-center align-middle w-28">
<div class="hidden sm:flex flex-col text-center align-middle shrink-0 w-28">
@if (post.PostTypeId == 2)
{
<div class="pt-4 w-full flex justify-center">
<a href=@GetHref(post)>
<a href=@GetHref(post) title="Answer">
<svg class="w-10 h-10 text-green-700" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M3 5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2zm8 2a2 2 0 0 0-2 2v8h2v-4h2v4h2V9a2 2 0 0 0-2-2zm0 2h2v2h-2z"/></svg>
</a>
</div>
Expand All @@ -20,13 +20,17 @@
<div class="my-2 text-center items-center rounded-md bg-green-600 px-2 py-1 text-sm font-medium text-white whitespace-nowrap">
<a href=@GetHref(post)>
<svg class="w-5 h-5 inline-block align-middle" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M21 7L9 19l-5.5-5.5l1.41-1.41L9 16.17L19.59 5.59z"/></svg>
8 answers
@(post.AnswerCount != 1 ? $"{post.AnswerCount} answers" : "1 answer")
</a>
</div>
}
else
{
<div class="my-2 text-center items-center rounded-md bg-green-50 px-2 py-1 text-sm font-medium text-green-700 ring-1 ring-inset ring-green-600/20 whitespace-nowrap">8 answers</div>
<div class="my-2 text-center items-center rounded-md bg-green-50 px-2 py-1 text-sm font-medium text-green-700 ring-1 ring-inset ring-green-600/20 whitespace-nowrap">
<a href=@GetHref(post)>
@(post.AnswerCount != 1 ? $"{post.AnswerCount ?? 0} answers" : "1 answer")
</a>
</div>
}
<div class="text-gray-600 dark:text-gray-300 whitespace-nowrap">@post.ViewCount.ToHumanReadable() views</div>
}
Expand All @@ -49,7 +53,7 @@
<div class="flex flex-grow px-4 sm:px-6 text-xs justify-end">
<dt class="font-medium text-gray-600 dark:text-gray-300">Modified</dt>
<dd class="ml-2 text-gray-600 dark:text-gray-300 whitespace-nowrap">
@((post.LastEditDate ?? post.CreationDate).ToString("dd MMMM yyyy h:mm:ss tt"))
@(post.GetModifiedDate().ToString("dd MMMM yyyy h:mm:ss tt"))
</dd>
</div>
</dl>
Expand Down
2 changes: 1 addition & 1 deletion MyApp/Configure.Renderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public void Configure(IWebHostBuilder builder) => builder

public class RendererCache(AppConfig appConfig, R2VirtualFiles r2)
{
private static bool DisableCache = false;
private static bool DisableCache = true;

public string GetCachedQuestionPostPath(int id) => appConfig.CacheDir.CombineWith(GetQuestionPostVirtualPath(id));
public string GetQuestionPostVirtualPath(int id)
Expand Down
Loading

0 comments on commit a511a2a

Please sign in to comment.