feat: add blog post editing
This commit is contained in:
parent
aae7f504e9
commit
4b2223634e
@ -40,6 +40,7 @@ EndProject
|
|||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "admin", "admin", "{183CDB1F-371D-4A24-8F96-1DF0967995E4}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "admin", "admin", "{183CDB1F-371D-4A24-8F96-1DF0967995E4}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
src\ts\admin\admin.ts = src\ts\admin\admin.ts
|
src\ts\admin\admin.ts = src\ts\admin\admin.ts
|
||||||
|
src\ts\admin\EditBlogPost.ts = src\ts\admin\EditBlogPost.ts
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "app", "app", "{A6590915-CB40-43EA-B0A3-EDEC63769780}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "app", "app", "{A6590915-CB40-43EA-B0A3-EDEC63769780}"
|
||||||
|
53
OliverBooth/Controllers/Api/v1/BlogPostController.cs
Normal file
53
OliverBooth/Controllers/Api/v1/BlogPostController.cs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
using System.Text;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Asp.Versioning;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using OliverBooth.Data.Blog;
|
||||||
|
using OliverBooth.Data.Web;
|
||||||
|
using OliverBooth.Services;
|
||||||
|
|
||||||
|
namespace OliverBooth.Controllers.Api.v1;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/v{version:apiVersion}/post")]
|
||||||
|
[ApiVersion(1)]
|
||||||
|
[Produces("application/json")]
|
||||||
|
public sealed class BlogPostController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ILogger<BlogPostController> _logger;
|
||||||
|
private readonly ISessionService _sessionService;
|
||||||
|
private readonly IBlogPostService _blogPostService;
|
||||||
|
|
||||||
|
public BlogPostController(ILogger<BlogPostController> logger,
|
||||||
|
ISessionService sessionService,
|
||||||
|
IBlogPostService blogPostService)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_sessionService = sessionService;
|
||||||
|
_blogPostService = blogPostService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPatch("{id:guid}")]
|
||||||
|
public async Task<IActionResult> OnPatch([FromRoute] Guid id)
|
||||||
|
{
|
||||||
|
if (!_sessionService.TryGetCurrentUser(Request, Response, out IUser? user))
|
||||||
|
{
|
||||||
|
Response.StatusCode = 401;
|
||||||
|
return new JsonResult(new { status = 401, message = "Unauthorized" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_blogPostService.TryGetPost(id, out IBlogPost? post))
|
||||||
|
{
|
||||||
|
Response.StatusCode = 404;
|
||||||
|
return new JsonResult(new { status = 404, message = "Not Found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
using var reader = new StreamReader(Request.Body, Encoding.UTF8);
|
||||||
|
string content = await reader.ReadToEndAsync();
|
||||||
|
|
||||||
|
post.Body = content;
|
||||||
|
_blogPostService.UpdatePost(post);
|
||||||
|
|
||||||
|
return new JsonResult(new { status = 200, message = "OK" });
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,7 @@ internal sealed class BlogPost : IBlogPost
|
|||||||
public IBlogAuthor Author { get; internal set; } = null!;
|
public IBlogAuthor Author { get; internal set; } = null!;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Body { get; internal set; } = string.Empty;
|
public string Body { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool EnableComments { get; internal set; }
|
public bool EnableComments { get; internal set; }
|
||||||
|
@ -12,10 +12,10 @@ public interface IBlogPost
|
|||||||
IBlogAuthor Author { get; }
|
IBlogAuthor Author { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the body of the post.
|
/// Gets or sets the body of the post.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The body of the post.</value>
|
/// <value>The body of the post.</value>
|
||||||
string Body { get; }
|
string Body { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether comments are enabled for the post.
|
/// Gets a value indicating whether comments are enabled for the post.
|
||||||
|
86
OliverBooth/Pages/Admin/BlogPosts.cshtml
Normal file
86
OliverBooth/Pages/Admin/BlogPosts.cshtml
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
@page "/admin/blog-posts"
|
||||||
|
@using System.Diagnostics
|
||||||
|
@using OliverBooth.Data
|
||||||
|
@using OliverBooth.Data.Blog
|
||||||
|
@using OliverBooth.Data.Web
|
||||||
|
@using OliverBooth.Services
|
||||||
|
@model OliverBooth.Pages.Admin.BlogPosts
|
||||||
|
@inject IBlogPostService BlogPostService
|
||||||
|
@inject IUserService UserService
|
||||||
|
@inject ISessionService SessionService
|
||||||
|
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Blog Posts";
|
||||||
|
Layout = "Shared/_AdminLayout";
|
||||||
|
|
||||||
|
HttpRequest request = HttpContext.Request;
|
||||||
|
SessionService.TryGetSession(request, out ISession? session);
|
||||||
|
IUser? user = null;
|
||||||
|
if (session is not null)
|
||||||
|
{
|
||||||
|
UserService.TryGetUser(session.UserId, out user);
|
||||||
|
}
|
||||||
|
Debug.Assert(user is not null);
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-3 col-md-6 mb-4">
|
||||||
|
<div class="card border-left-primary shadow h-100 py-2">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row no-gutters align-items-center">
|
||||||
|
<div class="col mr-2">
|
||||||
|
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">
|
||||||
|
<i class="fa-solid fa-globe fa-fw"></i>
|
||||||
|
Total Blog Posts
|
||||||
|
</div>
|
||||||
|
<div class="h5 mb-0 font-weight-bold text-gray-800">
|
||||||
|
@BlogPostService.GetBlogPostCount()
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Title</th>
|
||||||
|
<th>Posted</th>
|
||||||
|
<th>Author</th>
|
||||||
|
<th>Options</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
@foreach (IBlogPost post in BlogPostService.GetAllBlogPosts(visibility: (BlogPostVisibility)(-1)))
|
||||||
|
{
|
||||||
|
if (post.Visibility != BlogPostVisibility.Published && post.Author.Id != user.Id && !user.HasPermission(Permission.Administrator))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string icon = post.Visibility switch
|
||||||
|
{
|
||||||
|
BlogPostVisibility.Private => "key text-danger",
|
||||||
|
BlogPostVisibility.Unlisted => "unlock text-warning",
|
||||||
|
BlogPostVisibility.Published => "circle-check text-success"
|
||||||
|
};
|
||||||
|
|
||||||
|
<tr data-post-id="@post.Id.ToString("N")">
|
||||||
|
<td><i class="fa-solid fa-fw fa-@icon" title="@post.Visibility"></i> @post.Title</td>
|
||||||
|
<td>@post.Published</td>
|
||||||
|
<td><img src="@post.Author.AvatarUrl" class="rounded-circle me-2"> @post.Author.DisplayName</td>
|
||||||
|
<td>
|
||||||
|
<a asp-page="EditBlogPost" asp-route-id="@post.Id" class="btn btn-info">
|
||||||
|
<i class="fa-solid fa-pen-to-square"></i>
|
||||||
|
</a>
|
||||||
|
<a asp-controller="BlogAdmin" asp-action="DeletePost" asp-route-pid="@post.Id" class="btn btn-danger">
|
||||||
|
<i class="fa-solid fa-trash"></i>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
29
OliverBooth/Pages/Admin/BlogPosts.cshtml.cs
Normal file
29
OliverBooth/Pages/Admin/BlogPosts.cshtml.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
using OliverBooth.Data.Web;
|
||||||
|
using OliverBooth.Services;
|
||||||
|
|
||||||
|
namespace OliverBooth.Pages.Admin;
|
||||||
|
|
||||||
|
public class BlogPosts : PageModel
|
||||||
|
{
|
||||||
|
private readonly ISessionService _sessionService;
|
||||||
|
|
||||||
|
public BlogPosts(ISessionService sessionService)
|
||||||
|
{
|
||||||
|
_sessionService = sessionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IUser CurrentUser { get; private set; } = null!;
|
||||||
|
|
||||||
|
public IActionResult OnGet()
|
||||||
|
{
|
||||||
|
if (!_sessionService.TryGetCurrentUser(Request, Response, out IUser? user))
|
||||||
|
{
|
||||||
|
return RedirectToPage("/admin/login");
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentUser = user;
|
||||||
|
return Page();
|
||||||
|
}
|
||||||
|
}
|
27
OliverBooth/Pages/Admin/EditBlogPost.cshtml
Normal file
27
OliverBooth/Pages/Admin/EditBlogPost.cshtml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
@page "/admin/blog-posts/edit/{id}"
|
||||||
|
@using Markdig
|
||||||
|
@using OliverBooth.Data.Blog
|
||||||
|
@model OliverBooth.Pages.Admin.EditBlogPost
|
||||||
|
@inject MarkdownPipeline MarkdownPipeline
|
||||||
|
|
||||||
|
@{
|
||||||
|
Layout = "Shared/_AdminLayout";
|
||||||
|
ViewData["Title"] = "Edit Post";
|
||||||
|
IBlogPost post = Model.BlogPost;
|
||||||
|
}
|
||||||
|
|
||||||
|
<input type="hidden" data-blog-pid="@post.Id">
|
||||||
|
|
||||||
|
<button id="save-button" class="btn btn-primary"><i class="fa-solid fa-floppy-disk fa-fw"></i> Save <span class="text-muted">(Ctrl+S)</span></button>
|
||||||
|
<a href="/blog/@post.Published.ToString(@"yyyy\/MM\/dd")/@post.Slug" target="_blank" class="btn btn-info"><i class="fa-solid fa-magnifying-glass"></i> Preview</a>
|
||||||
|
|
||||||
|
<div class="row" style="margin-top: 20px;">
|
||||||
|
<div class="col-md-6 col-sm-12">
|
||||||
|
<textarea id="content" style="width: 100%; font-family: monospace; min-height: calc(100vh - 80px); max-height: 100%">@post.Body</textarea>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-sm-12" style="overflow-y: scroll; background: #1E1E1E">
|
||||||
|
<article id="article-preview" style="background: #333; max-width: 700px; margin: 20px auto; padding: 20px;">
|
||||||
|
@Html.Raw(Markdown.ToHtml(post.Body, MarkdownPipeline))
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</div>
|
39
OliverBooth/Pages/Admin/EditBlogPost.cshtml.cs
Normal file
39
OliverBooth/Pages/Admin/EditBlogPost.cshtml.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
using OliverBooth.Data.Blog;
|
||||||
|
using OliverBooth.Data.Web;
|
||||||
|
using OliverBooth.Services;
|
||||||
|
|
||||||
|
namespace OliverBooth.Pages.Admin;
|
||||||
|
|
||||||
|
public class EditBlogPost : PageModel
|
||||||
|
{
|
||||||
|
private readonly IBlogPostService _blogPostService;
|
||||||
|
private readonly ISessionService _sessionService;
|
||||||
|
|
||||||
|
public EditBlogPost(IBlogPostService blogPostService, ISessionService sessionService)
|
||||||
|
{
|
||||||
|
_blogPostService = blogPostService;
|
||||||
|
_sessionService = sessionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IUser CurrentUser { get; private set; } = null!;
|
||||||
|
|
||||||
|
public IBlogPost BlogPost { get; private set; } = null!;
|
||||||
|
|
||||||
|
public IActionResult OnGet([FromRoute(Name = "id")] Guid postId)
|
||||||
|
{
|
||||||
|
if (!_sessionService.TryGetCurrentUser(Request, Response, out IUser? user))
|
||||||
|
{
|
||||||
|
return RedirectToPage("/admin/login");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_blogPostService.TryGetPost(postId, out IBlogPost? post))
|
||||||
|
{
|
||||||
|
BlogPost = post;
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentUser = user;
|
||||||
|
return Page();
|
||||||
|
}
|
||||||
|
}
|
@ -148,6 +148,7 @@
|
|||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.8/handlebars.min.js" integrity="sha512-E1dSFxg+wsfJ4HKjutk/WaCzK7S2wv1POn1RRPGh8ZK+ag9l244Vqxji3r6wgz9YBf6+vhQEYJZpSjqWFPg9gg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.8/handlebars.min.js" integrity="sha512-E1dSFxg+wsfJ4HKjutk/WaCzK7S2wv1POn1RRPGh8ZK+ag9l244Vqxji3r6wgz9YBf6+vhQEYJZpSjqWFPg9gg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
<script src="~/js/prism.min.js" asp-append-version="true" data-manual></script>
|
<script src="~/js/prism.min.js" asp-append-version="true" data-manual></script>
|
||||||
<script src="~/js/app.min.js" asp-append-version="true"></script>
|
<script src="~/js/app.min.js" asp-append-version="true"></script>
|
||||||
|
<script src="~/js/admin.min.js" asp-append-version="true"></script>
|
||||||
|
|
||||||
<script id="loading-spinner-template" type="text/x-handlebars-template">
|
<script id="loading-spinner-template" type="text/x-handlebars-template">
|
||||||
@await Html.PartialAsync("_LoadingSpinner")
|
@await Html.PartialAsync("_LoadingSpinner")
|
||||||
|
@ -161,6 +161,19 @@ internal sealed class BlogPostService : IBlogPostService
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void UpdatePost(IBlogPost post)
|
||||||
|
{
|
||||||
|
if (post is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(post));
|
||||||
|
}
|
||||||
|
|
||||||
|
using BlogContext context = _dbContextFactory.CreateDbContext();
|
||||||
|
context.Update(post);
|
||||||
|
context.SaveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
private BlogPost CacheAuthor(BlogPost post)
|
private BlogPost CacheAuthor(BlogPost post)
|
||||||
{
|
{
|
||||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||||
|
@ -110,4 +110,11 @@ public interface IBlogPostService
|
|||||||
/// </returns>
|
/// </returns>
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="slug" /> is <see langword="null" />.</exception>
|
/// <exception cref="ArgumentNullException"><paramref name="slug" /> is <see langword="null" />.</exception>
|
||||||
bool TryGetPost(DateOnly publishDate, string slug, [NotNullWhen(true)] out IBlogPost? post);
|
bool TryGetPost(DateOnly publishDate, string slug, [NotNullWhen(true)] out IBlogPost? post);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the specified post.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="post">The post to edit.</param>
|
||||||
|
/// <exception cref="ArgumentNullException"><paramref name="post" /> is <see langword="null" />.</exception>
|
||||||
|
void UpdatePost(IBlogPost post);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,43 @@
|
|||||||
.form-signin {
|
body {
|
||||||
max-width: 330px;
|
min-height: 100vh;
|
||||||
padding: 1rem;
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
height: 100vh;
|
||||||
|
max-height: 100vh;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: #FFFFFF;
|
||||||
|
color: #1E1E1E;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn.btn-orange {
|
||||||
|
background: orange;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
td, th {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background: #1e1e1e;
|
||||||
|
|
||||||
|
code mark, code mark span {
|
||||||
|
background: #d8ba76 !important;
|
||||||
|
color: #000 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
code[class*="language-"] {
|
||||||
|
background: none !important;
|
||||||
}
|
}
|
58
src/ts/admin/EditBlogPost.ts
Normal file
58
src/ts/admin/EditBlogPost.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import BlogPost from "../app/BlogPost";
|
||||||
|
import API from "../app/API";
|
||||||
|
|
||||||
|
(() => {
|
||||||
|
getCurrentBlogPost().then(post => {
|
||||||
|
if (!post) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveButton = document.getElementById("save-button");
|
||||||
|
const preview = document.getElementById("article-preview");
|
||||||
|
const content = document.getElementById("content") as HTMLTextAreaElement;
|
||||||
|
|
||||||
|
saveButton.addEventListener("click", async (e: MouseEvent) => {
|
||||||
|
await savePost();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("keydown", async (e: KeyboardEvent) => {
|
||||||
|
if (e.ctrlKey && e.key === "s") {
|
||||||
|
e.preventDefault();
|
||||||
|
await savePost();
|
||||||
|
preview.innerHTML = post.content;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function savePost(): Promise<void> {
|
||||||
|
saveButton.classList.add("btn-primary");
|
||||||
|
saveButton.classList.remove("btn-success");
|
||||||
|
|
||||||
|
saveButton.setAttribute("disabled", "disabled");
|
||||||
|
saveButton.innerHTML = '<i class="fa-solid fa-spinner fa-spin fa-fw"></i> Saving ...';
|
||||||
|
|
||||||
|
post = await API.updatePost(post, content.value);
|
||||||
|
|
||||||
|
saveButton.classList.add("btn-success");
|
||||||
|
saveButton.classList.remove("btn-primary");
|
||||||
|
saveButton.removeAttribute("disabled");
|
||||||
|
saveButton.innerHTML = '<i class="fa-solid fa-circle-check fa-fw"></i> Saved';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
saveButton.classList.add("btn-primary");
|
||||||
|
saveButton.classList.remove("btn-success");
|
||||||
|
saveButton.innerHTML = '<i class="fa-solid fa-floppy-disk fa-fw"></i> Save <span class="text-muted">(Ctrl+S)</span>';
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function getCurrentBlogPost(): Promise<BlogPost> {
|
||||||
|
const blogPostRef: Element = document.querySelector('input[type="hidden"][data-blog-pid]');
|
||||||
|
|
||||||
|
if (blogPostRef) {
|
||||||
|
const pid: string = blogPostRef.getAttribute("data-blog-pid");
|
||||||
|
return await API.getBlogPost(pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})();
|
@ -0,0 +1 @@
|
|||||||
|
import "./EditBlogPost"
|
@ -6,32 +6,52 @@ class API {
|
|||||||
private static readonly BLOG_URL: string = "/blog";
|
private static readonly BLOG_URL: string = "/blog";
|
||||||
|
|
||||||
static async getBlogPostCount(): Promise<number> {
|
static async getBlogPostCount(): Promise<number> {
|
||||||
const response = await API.getResponse(`count`);
|
const response = await API.get(`${API.BLOG_URL}/count`);
|
||||||
return response.count;
|
return response.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getBlogPost(id: string): Promise<BlogPost> {
|
static async getBlogPost(id: string): Promise<BlogPost> {
|
||||||
const response = await API.getResponse(`post/${id}`);
|
const response = await API.get(`${API.BLOG_URL}/post/${id}`);
|
||||||
return new BlogPost(response);
|
return new BlogPost(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getBlogPosts(page: number): Promise<BlogPost[]> {
|
static async getBlogPosts(page: number): Promise<BlogPost[]> {
|
||||||
const response = await API.getResponse(`posts/${page}`);
|
const response = await API.get(`${API.BLOG_URL}/posts/${page}`);
|
||||||
return response.map(obj => new BlogPost(obj));
|
return response.map(obj => new BlogPost(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getBlogPostsByTag(tag: string, page: number): Promise<BlogPost[]> {
|
static async getBlogPostsByTag(tag: string, page: number): Promise<BlogPost[]> {
|
||||||
const response = await API.getResponse(`posts/tagged/${tag}/${page}`);
|
const response = await API.get(`${API.BLOG_URL}/posts/tagged/${tag}/${page}`);
|
||||||
return response.map(obj => new BlogPost(obj));
|
return response.map(obj => new BlogPost(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getAuthor(id: string): Promise<Author> {
|
static async getAuthor(id: string): Promise<Author> {
|
||||||
const response = await API.getResponse(`author/${id}`);
|
const response = await API.get(`${API.BLOG_URL}/author/${id}`);
|
||||||
return new Author(response);
|
return new Author(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async getResponse(url: string): Promise<any> {
|
static async updatePost(post: BlogPost, content: string): Promise<BlogPost> {
|
||||||
const response = await fetch(`${API.BASE_URL + API.BLOG_URL}/${url}`);
|
try {
|
||||||
|
await API.patch(`/post/${post.id}`, {body: content});
|
||||||
|
} catch {
|
||||||
|
return post;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await API.getBlogPost(post.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static get(url: string, options?: any): Promise<any> {
|
||||||
|
return API.perform(url, "GET", options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static patch(url: string, options?: any): Promise<any> {
|
||||||
|
return API.perform(url, "PATCH", options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async perform(url: string, method: string, options?: any): Promise<any> {
|
||||||
|
const opt = Object.assign({}, options, {method: method});
|
||||||
|
console.log("Sending options", opt);
|
||||||
|
const response = await fetch(`${API.BASE_URL}${url}`, opt);
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
throw new Error("Invalid response from server");
|
throw new Error("Invalid response from server");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user