浏览代码

news post create + edit + delete

Daniel Balk 2 年之前
父节点
当前提交
7c18a7a9a5

+ 1 - 0
Moonlight/Moonlight.csproj

@@ -24,6 +24,7 @@
     <PackageReference Include="JWT" Version="10.0.2" />
     <PackageReference Include="Logging.Net" Version="1.1.0" />
     <PackageReference Include="Mappy.Net" Version="1.0.2" />
+    <PackageReference Include="Markdig" Version="0.31.0" />
     <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.3">
       <PrivateAssets>all</PrivateAssets>
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

+ 98 - 0
Moonlight/Shared/Components/News/NewsEditor.razor

@@ -0,0 +1,98 @@
+@using Moonlight.App.Services
+@using Moonlight.App.Database.Entities
+@using BlazorMonaco
+
+@inject SmartTranslateService SmartTranslateService
+@inject IJSRuntime JsRuntime
+
+<div class="card mb-6">
+    <div class="card-header">
+        <h3 class="card-title w-75">
+            <input type="text" @bind="Model.Title" placeholder="@SmartTranslateService.Translate("Title...")" class="form-control form-control-flush"/>
+        </h3>
+        <div class="card-toolbar">
+            @{
+                string dateInt(int i) => i.ToString().Length < 2 ? "0" + i : i.ToString();
+                var date = Model.Date == default ? DateTime.Now : Model.Date;
+            }
+            <span class="text-gray-600 fw-semibold">@dateInt(date.Day).@dateInt(date.Month).@date.Year</span>
+        </div>
+    </div>
+    <div class="card-body">
+        <MonacoEditor CssClass="h-50" @ref="Editor" Id="vseditor" ConstructionOptions="(x) => EditorOptions"/>
+    </div>
+    <div class="card-footer">
+        <WButton CssClasses="btn btn-primary float-end" OnClick="DoSave" Text="@SmartTranslateService.Translate("Save")" WorkingText="@SmartTranslateService.Translate("Saving...")"></WButton>
+    </div>
+</div>
+
+@code {
+    // Monaco Editor
+    private MonacoEditor Editor;
+    private StandaloneEditorConstructionOptions EditorOptions;
+    
+    [Parameter]
+    public NewsEntry Model { get; set; }
+
+    [Parameter]
+    public Func<NewsEntry, Task> Save { get; set; }
+
+    protected override void OnInitialized()
+    {
+        EditorOptions = new()
+        {
+            AutomaticLayout = true,
+            Language = "plaintext",
+            Value = "Loading content",
+            Theme = "moonlight-theme",
+            Contextmenu = false,
+            Minimap = new()
+            {
+                Enabled = false
+            },
+            AutoIndent = true
+        };
+    }
+    
+    protected override async Task OnAfterRenderAsync(bool firstRender)
+    {
+        if (firstRender)
+        {
+            await JsRuntime.InvokeVoidAsync("initMonacoTheme");
+            
+            Editor.OnDidInit = new EventCallback<MonacoEditorBase>(this, async () =>
+            {
+                EditorOptions.Language = "markdown";
+        
+                var model = await Editor.GetModel();
+                await MonacoEditorBase.SetModelLanguage(model, EditorOptions.Language);
+                await Editor.SetPosition(new Position()
+                {
+                    Column = 0,
+                    LineNumber = 1
+                });
+        
+                await Editor.SetValue(string.IsNullOrWhiteSpace(Model.Markdown) ? "*enter your markdown here*" : Model.Markdown);
+        
+                await Editor.Layout(new Dimension()
+                {
+                    Height = 500,
+                    Width = 1000
+                }); 
+            });
+        }
+    }
+
+    private async Task DoSave()
+    {
+        Model.Date = Model.Date == default ? DateTime.Now : Model.Date;
+        Model.Markdown = await Editor.GetValue();
+        
+        Save?.Invoke(Model);
+    }
+
+    public async Task UpdateMonacoText()
+    {
+        await Editor.SetValue(Model.Markdown);
+    }
+}

+ 8 - 0
Moonlight/Shared/Components/Partials/SidebarMenu.razor

@@ -68,6 +68,14 @@ else
             <span class="menu-title"><TL>Changelog</TL></span>
         </a>
     </div>
+    <div class="menu-item">
+        <a class="menu-link" href="/news">
+            <span class="menu-icon">
+                <i class="bx bx-news"></i>
+            </span>
+            <span class="menu-title"><TL>News</TL></span>
+        </a>
+    </div>
 
     if (User.Admin)
     {

+ 32 - 0
Moonlight/Shared/News/Edit.razor

@@ -0,0 +1,32 @@
+@page "/news/edit/{Id:int}"
+
+@using Moonlight.App.Database.Entities
+@using Moonlight.App.Repositories
+@using Moonlight.Shared.Components.News
+
+@inject NewsEntryRepository NewsEntryRepository
+@inject NavigationManager NavigationManager
+
+<OnlyAdmin>
+    <LazyLoader Load="Load">
+        <NewsEditor Model="Entry" Save="DoSave"></NewsEditor>
+    </LazyLoader>
+</OnlyAdmin>
+
+@code {
+    [Parameter]
+    public int Id { get; set; }
+
+    private NewsEntry Entry;
+
+    private async Task Load(LazyLoader loader)
+    {
+        Entry = NewsEntryRepository.Get().First(x => x.Id == Id);
+    }
+
+    private async Task DoSave(NewsEntry entry)
+    {
+        NewsEntryRepository.Update(entry);
+        NavigationManager.NavigateTo("/news");
+    }
+}

+ 89 - 0
Moonlight/Shared/Views/News.razor

@@ -0,0 +1,89 @@
+@page "/news"
+@using Moonlight.App.Repositories
+@using Moonlight.App.Database.Entities
+@using Markdig
+@using Moonlight.App.Services
+@using Moonlight.App.Services.Interop
+@using Moonlight.Shared.Components.News
+
+@inject NewsEntryRepository NewsEntryRepository
+@inject SmartTranslateService SmartTranslateService
+@inject NavigationManager NavigationManager
+@inject AlertService AlertService
+
+<OnlyAdmin Silent="true">
+    <NewsEditor @ref="NewPostEditor" Model="NewPost" Save="DoSaveNewPost"></NewsEditor>
+</OnlyAdmin>
+
+<LazyLoader Load="Load">
+    @foreach (var entry in Entries)
+    {
+        <div class="card mb-6">
+            <div class="card-header">
+                <h3 class="card-title">@entry.Title</h3>
+                <div class="card-toolbar">
+                    <OnlyAdmin>
+                        <a href="/news/edit/@entry.Id">
+                            <button class="btn btn-sm btn-light me-4">
+                                <TL>Edit</TL>
+                            </button>
+                        </a>
+                        
+                        <WButton CssClasses="btn btn-sm btn-light me-4" 
+                                 Text="@SmartTranslateService.Translate("Delete")"
+                                 WorkingText="@SmartTranslateService.Translate("Deleting...")"
+                                 OnClick="() => Delete(entry)"></WButton>
+                    </OnlyAdmin>
+                    
+                    @{
+                        string dateInt(int i) => i.ToString().Length < 2 ? "0" + i : i.ToString();
+                    }
+                    <span class="text-gray-600 fw-semibold">@dateInt(entry.Date.Day).@dateInt(entry.Date.Month).@entry.Date.Year</span>
+                </div>
+            </div>
+            <div class="card-body">
+                @{
+                    var html = (MarkupString)Markdown.ToHtml(entry.Markdown);
+                }
+                
+                @html
+            </div>
+        </div>
+
+    }
+</LazyLoader>
+
+@code {
+    private NewsEntry NewPost = new();
+    private NewsEditor NewPostEditor;
+    
+    private NewsEntry[] Entries;
+
+    private async Task Load(LazyLoader loader)
+    {
+        Entries = NewsEntryRepository.Get().OrderByDescending(x => x.Date).ToArray();
+    }
+
+    private async Task DoSaveNewPost(NewsEntry post)
+    {
+        NewsEntryRepository.Add(post);
+        
+        NavigationManager.NavigateTo(NavigationManager.Uri, true);
+    }
+
+    private async Task Delete(NewsEntry entry)
+    {
+        var confirm = await AlertService.YesNo(
+            SmartTranslateService.Translate("Delete post"),  
+            SmartTranslateService.Translate("Do you really want to delete the post \"") + entry.Title + "\"?", 
+            SmartTranslateService.Translate("Yes"),
+            SmartTranslateService.Translate("No")
+        );
+        
+        if(!confirm) return;
+        
+        NewsEntryRepository.Delete(entry);
+        
+        NavigationManager.NavigateTo(NavigationManager.Uri, true);
+    }
+}

+ 8 - 1
Moonlight/resources/lang/de_de.lang

@@ -549,4 +549,11 @@ No node found to deploy to;No node found to deploy to
 Website details;Website details
 Configure your website;Configure your website
 The name cannot be longer that 32 characters;The name cannot be longer that 32 characters
-The name should only consist of lower case characters;The name should only consist of lower case characters
+The name should only consist of lower case characters;The name should only consist of lower case characters
+News;News
+Title...;Title...
+Enter text...;Enter text...
+Saving...;Saving...
+Deleting...;Deleting...
+Delete post;Delete post
+Do you really want to delete the post ";Do you really want to delete the post "