Add create bookmark
This commit is contained in:
parent
64b893b778
commit
55625b1be4
|
@ -1,4 +1,4 @@
|
|||
<li>
|
||||
<li class="bookmark">
|
||||
@if (!String.IsNullOrEmpty(this.Model.Notes))
|
||||
{
|
||||
<details>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
@using Fluxor
|
||||
@using Start.Client.Store.State
|
||||
@using Start.Client.Store.Features.DeleteGroup
|
||||
@using Start.Client.Store.Features.CreateBookmark
|
||||
|
||||
@inherits Fluxor.Blazor.Web.Components.FluxorComponent
|
||||
|
||||
|
@ -33,7 +34,7 @@
|
|||
</div>
|
||||
<p class="empty-title h6">No Bookmarks</p>
|
||||
<div class="empty-action">
|
||||
<button type="button" class="btn btn-primary">
|
||||
<button type="button" class="btn btn-primary" @onclick="this.OnCreateBookmarkClicked">
|
||||
<i class="icon icon-plus"></i> Create Bookmark
|
||||
</button>
|
||||
</div>
|
||||
|
@ -89,6 +90,7 @@
|
|||
|
||||
protected void OnCreateBookmarkClicked()
|
||||
{
|
||||
// Placeholder
|
||||
dispatch.Dispatch(new ShowCreateBookmarkFormAction(this.Group.BookmarkGroupId,
|
||||
this.Group.Title));
|
||||
}
|
||||
}
|
||||
|
|
91
Start/Client/Components/CreateBookmark.razor
Normal file
91
Start/Client/Components/CreateBookmark.razor
Normal file
|
@ -0,0 +1,91 @@
|
|||
@using Start.Client.Store.Features.CreateBookmark
|
||||
@using Fluxor
|
||||
|
||||
@inherits Fluxor.Blazor.Web.Components.FluxorComponent
|
||||
|
||||
@inject IActionSubscriber actionSubscriber
|
||||
@inject IDispatcher dispatch
|
||||
@inject IState<CreateBookmarkState> state
|
||||
|
||||
<Dialog Title="Create Bookmark" Active="this.state.Value.ShowCreateBookmarkForm" OnClose="this.OnDialogClose">
|
||||
<EditForm Model="this.Model" OnValidSubmit="this.OnSubmit">
|
||||
<DataAnnotationsValidator />
|
||||
|
||||
@if (this.state.Value.CreateBookmarkErrorMessage != null)
|
||||
{
|
||||
<Alert Type="Alert.AlertType.Error">
|
||||
@this.state.Value.CreateBookmarkErrorMessage
|
||||
</Alert>
|
||||
}
|
||||
|
||||
<ValidationSummary />
|
||||
|
||||
<div class="form-group">
|
||||
<div class="container">
|
||||
<div class="columns">
|
||||
<div class="column col-12">
|
||||
<label for="createBookmarkTitle">Title</label>
|
||||
<InputText id="createBookmarkTitle" name="createBookmarkTitle"
|
||||
class="form-input" @bind-Value="this.Model.Title" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column col-12">
|
||||
<label for="createBookmarkUrl">URL</label>
|
||||
<input type="url" name="createBookmarkUrl" class="form-input"
|
||||
@bind-value="this.Model.Url" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column col-12">
|
||||
<label for="createBookmarkNotes">Notes</label>
|
||||
<InputTextArea name="createBookmarkNotes" class="form-input"
|
||||
@bind-Value="this.Model.Notes" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="columns">
|
||||
<div class="column col-12 text-right">
|
||||
@if (this.state.Value.IsLoadingCreateBookmark)
|
||||
{
|
||||
<button type="submit" disabled class="btn btn-primary loading">
|
||||
<i class="icon icon-plus"></i> Create
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="icon icon-plus"></i> Create
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</EditForm>
|
||||
</Dialog>
|
||||
|
||||
@code {
|
||||
protected BookmarkDto Model { get; set; } = new BookmarkDto("", "", null, 0);
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
|
||||
this.Model = new BookmarkDto("", "", null, this.state.Value.GroupId);
|
||||
|
||||
actionSubscriber.SubscribeToAction<ShowCreateBookmarkFormAction>(this,
|
||||
a => this.Model.BookmarkGroupId = a.GroupId);
|
||||
}
|
||||
|
||||
protected void OnSubmit()
|
||||
{
|
||||
dispatch.Dispatch(new SubmitCreateBookmarkAction(this.Model));
|
||||
}
|
||||
|
||||
protected void OnDialogClose()
|
||||
{
|
||||
dispatch.Dispatch(new HideCreateBookmarkFormAction());
|
||||
}
|
||||
}
|
|
@ -80,6 +80,8 @@ else
|
|||
|
||||
<CreateGroup />
|
||||
<DeleteGroup />
|
||||
|
||||
<CreateBookmark />
|
||||
</Sidebar>
|
||||
}
|
||||
|
||||
|
|
|
@ -191,6 +191,32 @@
|
|||
ul.bookmarks {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
margin-block: 0;
|
||||
|
||||
.bookmark {
|
||||
margin-top: 0;
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
padding: 0.5em 0.83em;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
background-color: $bg-color-dark;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
a {
|
||||
border-radius: 0 0 0.4em 0.4em;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: solid 1px $border-color;
|
||||
}
|
||||
}
|
||||
|
||||
li.noBookmarksItem {
|
||||
margin-top: 0;
|
||||
|
|
|
@ -74,6 +74,10 @@ $dark-border-color: darken($dark-color, 10%);
|
|||
// Cards
|
||||
.card {
|
||||
border-color: $dark-border-color;
|
||||
|
||||
.card-body {
|
||||
background-color: $dark-bg-color-dark;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -124,4 +128,20 @@ $dark-border-color: darken($dark-color, 10%);
|
|||
#sidebar .accountActions {
|
||||
border-top-color: $dark-border-color;
|
||||
}
|
||||
|
||||
ul.bookmarks {
|
||||
.bookmark {
|
||||
&:not(:last-child) {
|
||||
border-bottom: solid 1px $dark-border-color;
|
||||
}
|
||||
|
||||
a, a:visited {
|
||||
color: $dark-primary-color;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background-color: $dark-bg-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -130,6 +130,7 @@
|
|||
<None Remove="Sass\" />
|
||||
<None Remove="Sass\Spectre\" />
|
||||
<None Remove="Store\Features\DeleteGroup\" />
|
||||
<None Remove="Store\Features\CreateBookmark\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Remove="wwwroot\css\Spectre\" />
|
||||
|
@ -147,5 +148,6 @@
|
|||
<Folder Include="Sass\" />
|
||||
<Folder Include="Sass\Spectre\" />
|
||||
<Folder Include="Store\Features\DeleteGroup\" />
|
||||
<Folder Include="Store\Features\CreateBookmark\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
using Start.Shared;
|
||||
|
||||
namespace Start.Client.Store.Features.CreateBookmark {
|
||||
public class ShowCreateBookmarkFormAction {
|
||||
public int GroupId { get; init; }
|
||||
public string GroupTitle { get; init; }
|
||||
|
||||
public ShowCreateBookmarkFormAction(int groupId, string groupTitle) {
|
||||
this.GroupId = groupId;
|
||||
this.GroupTitle = groupTitle;
|
||||
}
|
||||
}
|
||||
|
||||
public class HideCreateBookmarkFormAction { }
|
||||
|
||||
public class FetchCreateBookmarkAction { }
|
||||
|
||||
public class ReceivedCreateBookmarkAction { }
|
||||
|
||||
public class ErrorFetchingCreateBookmarkAction {
|
||||
public string ErrorMessage { get; init; }
|
||||
|
||||
public ErrorFetchingCreateBookmarkAction(string errorMessage) {
|
||||
this.ErrorMessage = errorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
public class SubmitCreateBookmarkAction {
|
||||
public BookmarkDto NewBookmark { get; init; }
|
||||
|
||||
public SubmitCreateBookmarkAction(BookmarkDto newBookmark) {
|
||||
this.NewBookmark = newBookmark;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
using System.Threading.Tasks;
|
||||
using Fluxor;
|
||||
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
|
||||
using Start.Shared.Api;
|
||||
using Start.Client.Store.Features.CurrentContainer;
|
||||
|
||||
namespace Start.Client.Store.Features.CreateBookmark {
|
||||
public class CreateBookmarkEffects {
|
||||
public IBookmarksApi BookmarksApi { get; init; }
|
||||
|
||||
public CreateBookmarkEffects(IBookmarksApi bookmarksApi) {
|
||||
this.BookmarksApi = bookmarksApi;
|
||||
}
|
||||
|
||||
[EffectMethod]
|
||||
public async Task SubmitCreateBookmark(SubmitCreateBookmarkAction action,
|
||||
IDispatcher dispatch) {
|
||||
dispatch.Dispatch(new FetchCreateBookmarkAction());
|
||||
|
||||
try {
|
||||
Refit.ApiResponse<Start.Shared.BookmarkDto?>? apiResponse = await this.BookmarksApi
|
||||
.CreateBookmark(action.NewBookmark.Title, action.NewBookmark.Url,
|
||||
action.NewBookmark.Notes, action.NewBookmark.BookmarkGroupId);
|
||||
|
||||
if (!apiResponse.IsSuccessStatusCode) {
|
||||
dispatch.Dispatch(new ErrorFetchingCreateBookmarkAction(
|
||||
"Error creating bookmark group: Status code " + apiResponse.StatusCode.ToString()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (apiResponse.Content == null) {
|
||||
dispatch.Dispatch(new ErrorFetchingCreateBookmarkAction(
|
||||
"Error creating bookmark group"));
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch.Dispatch(new AddBookmarkAction(apiResponse.Content));
|
||||
dispatch.Dispatch(new ReceivedCreateBookmarkAction());
|
||||
dispatch.Dispatch(new HideCreateBookmarkFormAction());
|
||||
} catch (AccessTokenNotAvailableException e) {
|
||||
e.Redirect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
using Fluxor;
|
||||
|
||||
namespace Start.Client.Store.Features.CreateBookmark {
|
||||
public class CreateBookmarkFeature : Feature<CreateBookmarkState> {
|
||||
public override string GetName() => "Create Bookmark";
|
||||
|
||||
protected override CreateBookmarkState GetInitialState() {
|
||||
return new CreateBookmarkState();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
using Fluxor;
|
||||
|
||||
namespace Start.Client.Store.Features.CreateBookmark {
|
||||
public static class CreateBookmarkReducers {
|
||||
[ReducerMethod]
|
||||
public static CreateBookmarkState ShowCreateBookmarkForm(CreateBookmarkState state,
|
||||
ShowCreateBookmarkFormAction action) {
|
||||
return state with {
|
||||
ShowCreateBookmarkForm = true,
|
||||
GroupId = action.GroupId,
|
||||
GroupTitle = action.GroupTitle,
|
||||
IsLoadingCreateBookmark = false,
|
||||
CreateBookmarkErrorMessage = null
|
||||
};
|
||||
}
|
||||
|
||||
[ReducerMethod(typeof(HideCreateBookmarkFormAction))]
|
||||
public static CreateBookmarkState HideCreateBookmarkForm(CreateBookmarkState state) {
|
||||
return state with {
|
||||
ShowCreateBookmarkForm = false
|
||||
};
|
||||
}
|
||||
|
||||
[ReducerMethod(typeof(FetchCreateBookmarkAction))]
|
||||
public static CreateBookmarkState FetchCreateBookmark(CreateBookmarkState state) {
|
||||
return state with {
|
||||
IsLoadingCreateBookmark = true,
|
||||
CreateBookmarkErrorMessage = null
|
||||
};
|
||||
}
|
||||
|
||||
[ReducerMethod]
|
||||
public static CreateBookmarkState ErrorFetchingCreateBookmark(CreateBookmarkState state,
|
||||
ErrorFetchingCreateBookmarkAction action) {
|
||||
return state with {
|
||||
CreateBookmarkErrorMessage = action.ErrorMessage,
|
||||
IsLoadingCreateBookmark = false
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using Start.Client.Store.State;
|
||||
|
||||
namespace Start.Client.Store.Features.CreateBookmark {
|
||||
public record CreateBookmarkState : RootState {
|
||||
public bool ShowCreateBookmarkForm { get; init; }
|
||||
public int GroupId { get; init; }
|
||||
public string GroupTitle { get; init; }
|
||||
public bool IsLoadingCreateBookmark { get; init; }
|
||||
public string? CreateBookmarkErrorMessage { get; init; }
|
||||
|
||||
public CreateBookmarkState() {
|
||||
this.GroupTitle = "";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,7 +33,13 @@ namespace Start.Client.Store.Features.CreateGroup {
|
|||
return;
|
||||
}
|
||||
|
||||
dispatch.Dispatch(new AddBookmarkGroupAction(action.NewGroup));
|
||||
if (apiResponse.Content == null) {
|
||||
dispatch.Dispatch(new ErrorFetchingCreateGroupAction(
|
||||
"Error creating bookmark group"));
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch.Dispatch(new AddBookmarkGroupAction(apiResponse.Content));
|
||||
dispatch.Dispatch(new RecievedCreateGroupAction());
|
||||
dispatch.Dispatch(new HideCreateGroupFormAction());
|
||||
} catch (AccessTokenNotAvailableException e) {
|
||||
|
|
|
@ -44,4 +44,20 @@ namespace Start.Client.Store.Features.CurrentContainer {
|
|||
this.BookmarkGroupId = bookmarkGroupId;
|
||||
}
|
||||
}
|
||||
|
||||
public class AddBookmarkAction {
|
||||
public BookmarkDto Bookmark { get; init; }
|
||||
|
||||
public AddBookmarkAction(BookmarkDto bookmark) {
|
||||
this.Bookmark = bookmark;
|
||||
}
|
||||
}
|
||||
|
||||
public class RemoveBookmarkAction {
|
||||
public int BookmarkId { get; init; }
|
||||
|
||||
public RemoveBookmarkAction(int bookmarkId) {
|
||||
this.BookmarkId = bookmarkId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,5 +78,56 @@ namespace Start.Client.Store.Features.CurrentContainer {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
[ReducerMethod]
|
||||
public static RootState AddBookmark(RootState state, AddBookmarkAction action) {
|
||||
BookmarkContainerDto? container = state.CurrentContainerState.Container;
|
||||
|
||||
if (container == null)
|
||||
return state;
|
||||
|
||||
List<BookmarkGroupDto>? groups = container.BookmarkGroups
|
||||
?.Select(bg => {
|
||||
if (bg.BookmarkGroupId == action.Bookmark.BookmarkGroupId) {
|
||||
return new BookmarkGroupDto(bg.BookmarkGroupId, bg.Title, bg.Color,
|
||||
bg.BookmarkContainerId,
|
||||
bg.Bookmarks?
|
||||
.Concat(new List<BookmarkDto> { action.Bookmark })
|
||||
.ToList());
|
||||
}
|
||||
|
||||
return bg;
|
||||
})
|
||||
.ToList();
|
||||
|
||||
return state with {
|
||||
CurrentContainerState = state.CurrentContainerState with {
|
||||
Container = new BookmarkContainerDto(container.BookmarkContainerId,
|
||||
container.Title, groups)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[ReducerMethod]
|
||||
public static RootState RemoveBookmark(RootState state, RemoveBookmarkAction action) {
|
||||
BookmarkContainerDto? container = state.CurrentContainerState.Container;
|
||||
|
||||
if (container == null)
|
||||
return state;
|
||||
|
||||
List<BookmarkGroupDto>? groups = container.BookmarkGroups
|
||||
?.Select(bg => new BookmarkGroupDto(bg.BookmarkGroupId, bg.Title, bg.Color,
|
||||
bg.BookmarkContainerId, bg.Bookmarks
|
||||
?.Where(b => b.BookmarkId != action.BookmarkId)
|
||||
.ToList()))
|
||||
.ToList();
|
||||
|
||||
return state with {
|
||||
CurrentContainerState = state.CurrentContainerState with {
|
||||
Container = new BookmarkContainerDto(container.BookmarkContainerId,
|
||||
container.Title, groups)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using Start.Client.Store.State;
|
||||
|
||||
namespace Start.Client.Store.Features.DeleteGroup {
|
||||
public record DeleteGroupState {
|
||||
public record DeleteGroupState : RootState {
|
||||
public bool ShowDeleteGroupForm { get; init; }
|
||||
public int BookmarkGroupIdToDelete { get; init; }
|
||||
public string BookmarkGroupTitleToDelete { get; init; }
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace Start.Server.Extensions {
|
|||
public static class BookmarkMaps {
|
||||
public static BookmarkDto MapToDto(this Bookmark bookmark) {
|
||||
return new BookmarkDto(bookmark.BookmarkId, bookmark.Title, bookmark.Url,
|
||||
bookmark.Notes);
|
||||
bookmark.Notes, bookmark.BookmarkGroupId);
|
||||
}
|
||||
|
||||
public static BookmarkGroupDto MapToDto(this BookmarkGroup bookmarkGroup) {
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Net.Http;
|
|||
|
||||
namespace Start.Shared.Api {
|
||||
public interface IBookmarksApi {
|
||||
[Get("{bookmarkId}")]
|
||||
[Get("/{bookmarkId}")]
|
||||
Task<ApiResponse<BookmarkDto?>> GetBookmark(int bookmarkId);
|
||||
|
||||
[Post("/Create")]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Start.Shared {
|
||||
public class BookmarkDto {
|
||||
|
@ -11,14 +11,19 @@ namespace Start.Shared {
|
|||
[StringLength(5000)]
|
||||
public string? Notes { get; set; }
|
||||
|
||||
public BookmarkDto(string title, string url, string? notes) {
|
||||
public int BookmarkGroupId { get; set; }
|
||||
|
||||
public BookmarkDto(string title, string url, string? notes, int bookmarkGroupId) {
|
||||
this.Title = title;
|
||||
this.Url = url;
|
||||
this.Notes = notes;
|
||||
this.BookmarkGroupId = bookmarkGroupId;
|
||||
}
|
||||
|
||||
public BookmarkDto(int bookmarkId, string title, string url, string? notes)
|
||||
: this(title, url, notes) {
|
||||
[JsonConstructor]
|
||||
public BookmarkDto(int bookmarkId, string title, string url, string? notes,
|
||||
int bookmarkGroupId)
|
||||
: this(title, url, notes, bookmarkGroupId) {
|
||||
this.BookmarkId = bookmarkId;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue