Add creating bookmark groups
This commit is contained in:
		
							parent
							
								
									d997655b59
								
							
						
					
					
						commit
						7841d1d1a8
					
				
							
								
								
									
										20
									
								
								Start/Client/Components/Bookmark.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								Start/Client/Components/Bookmark.razor
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,20 @@
 | 
				
			||||||
 | 
					<li>
 | 
				
			||||||
 | 
					    @if (!String.IsNullOrEmpty(this.Model.Notes))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        <details>
 | 
				
			||||||
 | 
					            <summary>
 | 
				
			||||||
 | 
					                <a href="@this.Model.Url" class="bookmarkLink">@this.Model.Title</a>
 | 
				
			||||||
 | 
					            </summary>
 | 
				
			||||||
 | 
					            @this.Model.Notes
 | 
				
			||||||
 | 
					        </details>
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        <a href="@this.Model.Url" class="bookmarkLink">@this.Model.Title</a>
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					</li>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@code {
 | 
				
			||||||
 | 
					    [Parameter]
 | 
				
			||||||
 | 
					    public BookmarkDto Model { get; set; } = null!;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,21 @@
 | 
				
			||||||
<div class="activeBookmarkContainer">
 | 
					@using Start.Client.Store.State
 | 
				
			||||||
    @if (this.Container == null)
 | 
					@using Start.Client.Store.Features.CreateGroup
 | 
				
			||||||
 | 
					@using Fluxor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inherits Fluxor.Blazor.Web.Components.FluxorComponent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject IDispatcher dispatch
 | 
				
			||||||
 | 
					@inject IState<RootState> state
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div class="activeBookmarkContainer">
 | 
				
			||||||
 | 
					    @if (this.state.Value.CurrentContainerState.ErrorMessage != null)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        <Alert Type="Alert.AlertType.Error">
 | 
				
			||||||
 | 
					            @this.state.Value.CurrentContainerState.ErrorMessage
 | 
				
			||||||
 | 
					        </Alert>
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @if (this.state.Value.CurrentContainerState.IsLoadingCurrentContainer)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        <div class="empty">
 | 
					        <div class="empty">
 | 
				
			||||||
            <div class="empty-icon">
 | 
					            <div class="empty-icon">
 | 
				
			||||||
| 
						 | 
					@ -7,27 +23,60 @@
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <p class="empty-title h5">Loading Bookmarks</p>
 | 
					            <p class="empty-title h5">Loading Bookmarks</p>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <p class="text-center">Loading Bookmarks</p>
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else if (!this.Container.BookmarkGroups?.Any() ?? true)
 | 
					    else if (this.state.Value.CurrentContainerState.Container == null)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        <div class="empty">
 | 
				
			||||||
 | 
					            <div class="empty-icon">
 | 
				
			||||||
 | 
					                <i class="icon icon-3x icon-bookmark"></i>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <p class="empty-title h5">Failed To Load Container</p>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (this.state.Value.CurrentContainerState.Container.BookmarkGroups == null
 | 
				
			||||||
 | 
					        || (!(this.state.Value.CurrentContainerState.Container.BookmarkGroups?.Any()) ?? true))
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        <div class="empty">
 | 
					        <div class="empty">
 | 
				
			||||||
            <div class="empty-icon">
 | 
					            <div class="empty-icon">
 | 
				
			||||||
                <i class="icon icon-3x icon-bookmark"></i>
 | 
					                <i class="icon icon-3x icon-bookmark"></i>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <p class="empty-title h5">No Bookmark Groups</p>
 | 
					            <p class="empty-title h5">No Bookmark Groups</p>
 | 
				
			||||||
 | 
					            <div class="empty-action">
 | 
				
			||||||
 | 
					                <button class="btn btn-primary" @onclick="this.ShowCreateGroupForm">
 | 
				
			||||||
 | 
					                    <i class="icon icon-plus"></i> Create Group
 | 
				
			||||||
 | 
					                </button>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        foreach (BookmarkGroupDto group in this.Container.BookmarkGroups!)
 | 
					        <div id="bookmarkGroups">
 | 
				
			||||||
        {
 | 
					            @* The compiler doesn't pick up that null has already been checked for,
 | 
				
			||||||
            <BookmarkGroup Group="group" />
 | 
					                so the ! is needed *@
 | 
				
			||||||
        }
 | 
					            @foreach (BookmarkGroupDto group in this.state.Value.CurrentContainerState.Container.BookmarkGroups!)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                <BookmarkGroup Group="group" />
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <div class="addBookmarkGroupButton text-center">
 | 
				
			||||||
 | 
					                <button type="button" class="btn tooltip tooltip-bottom"
 | 
				
			||||||
 | 
					                        @onclick="this.ShowCreateGroupForm"
 | 
				
			||||||
 | 
					                        aria-label="Create Group" data-tooltip="Create Group">
 | 
				
			||||||
 | 
					                    <i class="icon icon-plus"></i>
 | 
				
			||||||
 | 
					                </button>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@code {
 | 
					@code {
 | 
				
			||||||
    [Parameter]
 | 
					    public void ShowCreateGroupForm()
 | 
				
			||||||
    public BookmarkContainerDto? Container { get; set; }
 | 
					    {
 | 
				
			||||||
 | 
					        if (this.state.Value.CurrentContainerState.Container == null)
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dispatch.Dispatch(new ShowCreateGroupFormAction(
 | 
				
			||||||
 | 
					            this.state.Value.CurrentContainerState.Container.BookmarkContainerId,
 | 
				
			||||||
 | 
					            this.state.Value.CurrentContainerState.Container.Title));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,24 +1,47 @@
 | 
				
			||||||
<h2 class="bookmarkGroupTitle" style="background-color: #@this.Group.Color">
 | 
					<div class="card bookmarkGroup">
 | 
				
			||||||
    @this.Group.Title
 | 
					    <div class="card-header" style="background-color: @this.Group.Color">
 | 
				
			||||||
</h2>
 | 
					        <h2 class="card-title h5 d-inline-block">@this.Group.Title</h2>
 | 
				
			||||||
<ul class="bookmarkGroupList">
 | 
					        <button type="button" class="addBookmarkButton btn btn-sm tooltip tooltip-left"
 | 
				
			||||||
    @if (this.Group.Bookmarks == null || !this.Group.Bookmarks.Any())
 | 
					                aria-label="Create Bookmark" data-tooltip="Create Bookmark">
 | 
				
			||||||
    {
 | 
					            <i class="icon icon-plus"></i>
 | 
				
			||||||
        <li class="bookmarkListItem noBookmarksItem"><i>No Bookmarks</i></li>
 | 
					        </button>
 | 
				
			||||||
    }
 | 
					    </div>
 | 
				
			||||||
    else
 | 
					    <div class="card-body">
 | 
				
			||||||
    {
 | 
					        <ul class="bookmarks">
 | 
				
			||||||
        foreach (BookmarkDto bookmark in this.Group.Bookmarks!)
 | 
					            @if (this.Group.Bookmarks == null || !this.Group.Bookmarks.Any())
 | 
				
			||||||
        {
 | 
					            {
 | 
				
			||||||
            <li class="bookmarkListItem">
 | 
					                <li class="noBookmarksItem">
 | 
				
			||||||
                <a href="@bookmark.Url" class="bookmarkLink">@bookmark.Title</a>
 | 
					                    <div class="empty">
 | 
				
			||||||
            </li>
 | 
					                        <div class="empty-icon">
 | 
				
			||||||
        }
 | 
					                            <i class="icon icon-bookmark"></i>
 | 
				
			||||||
    }
 | 
					                        </div>
 | 
				
			||||||
</ul>
 | 
					                        <p class="empty-title h5">No Bookmarks</p>
 | 
				
			||||||
 | 
					                        <div class="empty-action">
 | 
				
			||||||
 | 
					                            <button type="button" class="btn btn-primary">
 | 
				
			||||||
 | 
					                                <i class="icon icon-plus"></i> Create Bookmark
 | 
				
			||||||
 | 
					                            </button>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </li>
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                foreach (BookmarkDto bookmark in this.Group.Bookmarks)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    <Bookmark Model="bookmark" />
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        </ul>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@code
 | 
					@code
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [Parameter]
 | 
					    [Parameter]
 | 
				
			||||||
    public BookmarkGroupDto Group { get; set; } = null!; // [Required] is a .net 6 feature
 | 
					    public BookmarkGroupDto Group { get; set; } = null!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected void OnCreateBookmarkClicked()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Placeholder
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,7 @@
 | 
				
			||||||
                @this.state.Value.CreateContainerErrorMessage
 | 
					                @this.state.Value.CreateContainerErrorMessage
 | 
				
			||||||
            </Alert>
 | 
					            </Alert>
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div class="form-group">
 | 
					        <div class="form-group">
 | 
				
			||||||
            <div class="container">
 | 
					            <div class="container">
 | 
				
			||||||
                <div class="columns">
 | 
					                <div class="columns">
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										82
									
								
								Start/Client/Components/CreateGroup.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								Start/Client/Components/CreateGroup.razor
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,82 @@
 | 
				
			||||||
 | 
					@using Start.Client.Store.Features.CreateGroup
 | 
				
			||||||
 | 
					@using Fluxor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inherits Fluxor.Blazor.Web.Components.FluxorComponent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject IActionSubscriber actionSubscriber
 | 
				
			||||||
 | 
					@inject IDispatcher dispatch
 | 
				
			||||||
 | 
					@inject IState<CreateGroupState> state
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Dialog Title="Create Bookmark Group" Active="this.state.Value.ShowCreateGroupForm" OnClose="this.OnDialogClose">
 | 
				
			||||||
 | 
					    <EditForm Model="this.Model" OnValidSubmit="this.OnSubmit">
 | 
				
			||||||
 | 
					        <DataAnnotationsValidator />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @if (this.state.Value.CreateGroupErrorMessage != null)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            <Alert Type="Alert.AlertType.Error">
 | 
				
			||||||
 | 
					                @this.state.Value.CreateGroupErrorMessage
 | 
				
			||||||
 | 
					            </Alert>
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <ValidationSummary />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div class="form-group">
 | 
				
			||||||
 | 
					            <div class="container">
 | 
				
			||||||
 | 
					                <div class="columns">
 | 
				
			||||||
 | 
					                    <div class="column col-10">
 | 
				
			||||||
 | 
					                        <label for="createBookmarkGroupTitle">Title</label>
 | 
				
			||||||
 | 
					                        <InputText id="createBookmarkGroupTitle" name="createBookmarkGroupTitle"
 | 
				
			||||||
 | 
					                                   class="form-input" @bind-Value="this.Model.Title" />
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="column col-2">
 | 
				
			||||||
 | 
					                        <label for="createBookmarkGroupColor">Color</label>
 | 
				
			||||||
 | 
					                        <input type="color" name="createBookmarkGroupColor" @bind="this.Model.Color" />
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="container">
 | 
				
			||||||
 | 
					                <div class="columns">
 | 
				
			||||||
 | 
					                    <div class="column col-12 text-right">
 | 
				
			||||||
 | 
					                        @if (this.state.Value.IsLoadingCreateGroup)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            <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 {
 | 
				
			||||||
 | 
					    private BookmarkGroupDto Model { get; set; } = new("", "", 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected override void OnInitialized()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        base.OnInitialized();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.Model = new BookmarkGroupDto("", "", state.Value.ContainerId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Keep the model's container ID up to date
 | 
				
			||||||
 | 
					        actionSubscriber.SubscribeToAction<ShowCreateGroupFormAction>(this,
 | 
				
			||||||
 | 
					            (a) => this.Model.BookmarkContainerId = a.ContainerId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected void OnSubmit()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        dispatch.Dispatch(new SubmitCreateGroupAction(this.Model));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected void OnDialogClose()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        dispatch.Dispatch(new HideCreateGroupFormAction());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,8 @@
 | 
				
			||||||
<div class="modal @(this.Active ? "active" : "")">
 | 
					@using Microsoft.Extensions.Logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@inject ILogger<Dialog> logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div class="modal @(this.Active ? "active" : "")">
 | 
				
			||||||
    <a class="modal-overlay" @onclick="this.OnDialogClose" aria-label="Close"></a>
 | 
					    <a class="modal-overlay" @onclick="this.OnDialogClose" aria-label="Close"></a>
 | 
				
			||||||
    <div class="modal-container">
 | 
					    <div class="modal-container">
 | 
				
			||||||
        <div class="modal-header">
 | 
					        <div class="modal-header">
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,6 @@
 | 
				
			||||||
@using System.Linq
 | 
					@using System.Linq
 | 
				
			||||||
@using Start.Client.Components
 | 
					@using Start.Client.Components
 | 
				
			||||||
@using Start.Client.Store.State
 | 
					@using Start.Client.Store.State
 | 
				
			||||||
@using Start.Client.Store.Features.ContainersList
 | 
					 | 
				
			||||||
@using Start.Client.Store.Features.CurrentContainer
 | 
					@using Start.Client.Store.Features.CurrentContainer
 | 
				
			||||||
@using Start.Client.Store.Features.CreateContainer
 | 
					@using Start.Client.Store.Features.CreateContainer
 | 
				
			||||||
@using Start.Client.Store.Features.DeleteContainer
 | 
					@using Start.Client.Store.Features.DeleteContainer
 | 
				
			||||||
| 
						 | 
					@ -52,27 +51,24 @@ else
 | 
				
			||||||
            </li>
 | 
					            </li>
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        <li class="tab-item tab-action">
 | 
					        <li class="tab-item tab-action">
 | 
				
			||||||
            <button @onclick="OnCreateContainerClicked" class="btn btn-link"
 | 
					            <button @onclick="OnCreateContainerClicked" class="btn btn-link tooltip tooltip-left"
 | 
				
			||||||
                    title="Create New Container" aria-label="Create New Container">
 | 
					                    title="Create New Container" aria-label="Create New Container"
 | 
				
			||||||
 | 
					                    data-tooltip="Create Container">
 | 
				
			||||||
                +
 | 
					                +
 | 
				
			||||||
            </button>
 | 
					            </button>
 | 
				
			||||||
        </li>
 | 
					        </li>
 | 
				
			||||||
    </ul>
 | 
					    </ul>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <BookmarkContainer Container="this.state.Value.CurrentContainerState.Container" />
 | 
					    <BookmarkContainer />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <CreateContainer />
 | 
					    <CreateContainer />
 | 
				
			||||||
    <DeleteContainer />
 | 
					    <DeleteContainer />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <CreateGroup />
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@code
 | 
					@code
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    protected override async Task OnInitializedAsync()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        this.dispatch.Dispatch(new LoadContainerListAction());
 | 
					 | 
				
			||||||
        this.dispatch.Dispatch(new LoadCurrentContainerAction(await this.GetSelectedContainerId()));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected void OnContainerSelected(int bookmarkContainerId)
 | 
					    protected void OnContainerSelected(int bookmarkContainerId)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        dispatch.Dispatch(new LoadCurrentContainerAction(bookmarkContainerId));
 | 
					        dispatch.Dispatch(new LoadCurrentContainerAction(bookmarkContainerId));
 | 
				
			||||||
| 
						 | 
					@ -95,26 +91,4 @@ else
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        dispatch.Dispatch(new ShowCreateContainerFormAction());
 | 
					        dispatch.Dispatch(new ShowCreateContainerFormAction());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Save the currently selected container in LocalStorage so that the same container remains
 | 
					 | 
				
			||||||
    // selected between new tabs
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected async Task<int> GetSelectedContainerId()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        bool hasValue = await localStorage.ContainKeyAsync("SelectedContainer");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (hasValue)
 | 
					 | 
				
			||||||
            return await localStorage.GetItemAsync<int>("SelectedContainer");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Default to the first container
 | 
					 | 
				
			||||||
        int firstContainer = this.state.Value.ContainerListState.Containers
 | 
					 | 
				
			||||||
            .First().BookmarkContainerId;
 | 
					 | 
				
			||||||
        await this.SetSelectedContainer(firstContainer);
 | 
					 | 
				
			||||||
        return firstContainer;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected async Task SetSelectedContainer(int selectedContainerId)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        await localStorage.SetItemAsync<int>("SelectedContainer", selectedContainerId);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,6 +34,10 @@ namespace Start.Client {
 | 
				
			||||||
				})
 | 
									})
 | 
				
			||||||
				.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
 | 
									.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								builder.Services.AddRefitClient<IBookmarkGroupsApi>()
 | 
				
			||||||
 | 
									.ConfigureHttpClient(c => { c.BaseAddress = new Uri(baseUri, "BookmarkGroups"); })
 | 
				
			||||||
 | 
									.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			builder.Services.AddRefitClient<IBookmarksApi>()
 | 
								builder.Services.AddRefitClient<IBookmarksApi>()
 | 
				
			||||||
				.ConfigureHttpClient(c => { c.BaseAddress = new Uri(baseUri, "Bookmarks"); })
 | 
									.ConfigureHttpClient(c => { c.BaseAddress = new Uri(baseUri, "Bookmarks"); })
 | 
				
			||||||
				.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
 | 
									.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
 | 
				
			||||||
| 
						 | 
					@ -44,6 +48,7 @@ namespace Start.Client {
 | 
				
			||||||
			builder.Services.AddFluxor(opt => {
 | 
								builder.Services.AddFluxor(opt => {
 | 
				
			||||||
				opt.ScanAssemblies(typeof(Program).Assembly);
 | 
									opt.ScanAssemblies(typeof(Program).Assembly);
 | 
				
			||||||
#if DEBUG
 | 
					#if DEBUG
 | 
				
			||||||
 | 
									Console.WriteLine("Enabling Redux dev tools");
 | 
				
			||||||
				opt.UseReduxDevTools();
 | 
									opt.UseReduxDevTools();
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,6 +46,7 @@
 | 
				
			||||||
    <None Remove="Store\Features\DeleteContainer\" />
 | 
					    <None Remove="Store\Features\DeleteContainer\" />
 | 
				
			||||||
    <None Remove="Store\Features\ContainersList\" />
 | 
					    <None Remove="Store\Features\ContainersList\" />
 | 
				
			||||||
    <None Remove="Store\Features\CurrentContainer\" />
 | 
					    <None Remove="Store\Features\CurrentContainer\" />
 | 
				
			||||||
 | 
					    <None Remove="Store\Features\CreateGroup\" />
 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
  <ItemGroup>
 | 
					  <ItemGroup>
 | 
				
			||||||
    <Content Remove="wwwroot\css\Spectre\" />
 | 
					    <Content Remove="wwwroot\css\Spectre\" />
 | 
				
			||||||
| 
						 | 
					@ -58,5 +59,6 @@
 | 
				
			||||||
    <Folder Include="Store\Features\DeleteContainer\" />
 | 
					    <Folder Include="Store\Features\DeleteContainer\" />
 | 
				
			||||||
    <Folder Include="Store\Features\ContainersList\" />
 | 
					    <Folder Include="Store\Features\ContainersList\" />
 | 
				
			||||||
    <Folder Include="Store\Features\CurrentContainer\" />
 | 
					    <Folder Include="Store\Features\CurrentContainer\" />
 | 
				
			||||||
 | 
					    <Folder Include="Store\Features\CreateGroup\" />
 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
</Project>
 | 
					</Project>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,12 +4,11 @@ using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
 | 
				
			||||||
using Refit;
 | 
					using Refit;
 | 
				
			||||||
using Start.Shared;
 | 
					using Start.Shared;
 | 
				
			||||||
using Start.Shared.Api;
 | 
					using Start.Shared.Api;
 | 
				
			||||||
using Start.Client.Store.Features.CurrentContainer;
 | 
					 | 
				
			||||||
using Start.Client.Store.Features.ContainersList;
 | 
					using Start.Client.Store.Features.ContainersList;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Start.Client.Store.Features.CreateContainer {
 | 
					namespace Start.Client.Store.Features.CreateContainer {
 | 
				
			||||||
	public class CreateContainerEffects {
 | 
						public class CreateContainerEffects {
 | 
				
			||||||
		public IBookmarkContainersApi BookmarkContainersApi { get; set; }
 | 
							public IBookmarkContainersApi BookmarkContainersApi { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public CreateContainerEffects(IBookmarkContainersApi bookmarkContainersApi) {
 | 
							public CreateContainerEffects(IBookmarkContainersApi bookmarkContainersApi) {
 | 
				
			||||||
			this.BookmarkContainersApi = bookmarkContainersApi;
 | 
								this.BookmarkContainersApi = bookmarkContainersApi;
 | 
				
			||||||
| 
						 | 
					@ -26,15 +25,14 @@ namespace Start.Client.Store.Features.CreateContainer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				BookmarkContainerDto? container = apiResponse.Content;
 | 
									BookmarkContainerDto? container = apiResponse.Content;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (container == null)
 | 
									if (container == null) {
 | 
				
			||||||
					dispatch.Dispatch(new ErrorFetchingCreateContainerAction(
 | 
										dispatch.Dispatch(new ErrorFetchingCreateContainerAction(
 | 
				
			||||||
						"Failed to create container"));
 | 
											"Failed to create container"));
 | 
				
			||||||
				else {
 | 
										return;
 | 
				
			||||||
					dispatch.Dispatch(new AddContainerToListAction(container));
 | 
					 | 
				
			||||||
					dispatch.Dispatch(new ReceivedCreateContainerAction());
 | 
					 | 
				
			||||||
					dispatch.Dispatch(new LoadCurrentContainerAction(
 | 
					 | 
				
			||||||
						container.BookmarkContainerId));
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									dispatch.Dispatch(new AddContainerToListAction(container));
 | 
				
			||||||
 | 
									dispatch.Dispatch(new ReceivedCreateContainerAction());
 | 
				
			||||||
			} catch (AccessTokenNotAvailableException e) {
 | 
								} catch (AccessTokenNotAvailableException e) {
 | 
				
			||||||
				e.Redirect();
 | 
									e.Redirect();
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,5 @@
 | 
				
			||||||
using Fluxor;
 | 
					using System;
 | 
				
			||||||
 | 
					using Fluxor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Start.Client.Store.Features.CreateContainer {
 | 
					namespace Start.Client.Store.Features.CreateContainer {
 | 
				
			||||||
	public static class CreateContainerReducers {
 | 
						public static class CreateContainerReducers {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,35 @@
 | 
				
			||||||
 | 
					using Start.Shared;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Start.Client.Store.Features.CreateGroup {
 | 
				
			||||||
 | 
						public class ShowCreateGroupFormAction {
 | 
				
			||||||
 | 
							public int ContainerId { get; init; }
 | 
				
			||||||
 | 
							public string ContainerTitle { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public ShowCreateGroupFormAction(int containerId, string containerTitle) {
 | 
				
			||||||
 | 
								this.ContainerId = containerId;
 | 
				
			||||||
 | 
								this.ContainerTitle = containerTitle;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public class HideCreateGroupFormAction { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public class FetchCreateGroupAction { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public class RecievedCreateGroupAction { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public class ErrorFetchingCreateGroupAction {
 | 
				
			||||||
 | 
							public string ErrorMessage { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public ErrorFetchingCreateGroupAction(string errorMessage) {
 | 
				
			||||||
 | 
								this.ErrorMessage = errorMessage;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public class SubmitCreateGroupAction {
 | 
				
			||||||
 | 
							public BookmarkGroupDto NewGroup { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public SubmitCreateGroupAction(BookmarkGroupDto newGroup) {
 | 
				
			||||||
 | 
								this.NewGroup = newGroup;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,44 @@
 | 
				
			||||||
 | 
					using Start.Shared.Api;
 | 
				
			||||||
 | 
					using Fluxor;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
 | 
				
			||||||
 | 
					using Refit;
 | 
				
			||||||
 | 
					using Start.Shared;
 | 
				
			||||||
 | 
					using Start.Client.Store.Features.CurrentContainer;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Start.Client.Store.Features.CreateGroup {
 | 
				
			||||||
 | 
						public class CreateGroupEffects {
 | 
				
			||||||
 | 
							public IBookmarkGroupsApi BookmarkGroupsApi { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public CreateGroupEffects(IBookmarkGroupsApi bookmarksApi) {
 | 
				
			||||||
 | 
								this.BookmarkGroupsApi = bookmarksApi;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							[EffectMethod]
 | 
				
			||||||
 | 
							public async Task SubmitCreateBookmarkGroup(SubmitCreateGroupAction action,
 | 
				
			||||||
 | 
								IDispatcher dispatch) {
 | 
				
			||||||
 | 
								dispatch.Dispatch(new FetchCreateGroupAction());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								try {
 | 
				
			||||||
 | 
									ApiResponse<BookmarkGroupDto?> apiResponse = await this.BookmarkGroupsApi
 | 
				
			||||||
 | 
										.CreateBookmarkGroup(action.NewGroup.Title, action.NewGroup.Color,
 | 
				
			||||||
 | 
										action.NewGroup.BookmarkContainerId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									Console.WriteLine("Status code: " + apiResponse.StatusCode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (!apiResponse.IsSuccessStatusCode) {
 | 
				
			||||||
 | 
										dispatch.Dispatch(new ErrorFetchingCreateGroupAction(
 | 
				
			||||||
 | 
											"Error creating bookmark group"));
 | 
				
			||||||
 | 
										return;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									dispatch.Dispatch(new AddBookmarkGroupAction(action.NewGroup));
 | 
				
			||||||
 | 
									dispatch.Dispatch(new RecievedCreateGroupAction());
 | 
				
			||||||
 | 
									dispatch.Dispatch(new HideCreateGroupFormAction());
 | 
				
			||||||
 | 
								} catch (AccessTokenNotAvailableException e) {
 | 
				
			||||||
 | 
									e.Redirect();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					using Fluxor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Start.Client.Store.Features.CreateGroup {
 | 
				
			||||||
 | 
						public class CreateGroupFeature : Feature<CreateGroupState> {
 | 
				
			||||||
 | 
							public override string GetName() => "Create Group";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							protected override CreateGroupState GetInitialState() {
 | 
				
			||||||
 | 
								return new CreateGroupState();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,54 @@
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using Fluxor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Start.Client.Store.Features.CreateGroup {
 | 
				
			||||||
 | 
						public static class CreateGroupReducers {
 | 
				
			||||||
 | 
							[ReducerMethod]
 | 
				
			||||||
 | 
							public static CreateGroupState ShowCreateGroupForm(CreateGroupState state,
 | 
				
			||||||
 | 
								ShowCreateGroupFormAction action) {
 | 
				
			||||||
 | 
								return state with {
 | 
				
			||||||
 | 
									ShowCreateGroupForm = true,
 | 
				
			||||||
 | 
									ContainerId = action.ContainerId,
 | 
				
			||||||
 | 
									ContainerTitle = action.ContainerTitle,
 | 
				
			||||||
 | 
									IsLoadingCreateGroup = false,
 | 
				
			||||||
 | 
									CreateGroupErrorMessage = null
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							[ReducerMethod(typeof(HideCreateGroupFormAction))]
 | 
				
			||||||
 | 
							public static CreateGroupState HideCreateContainerForm(CreateGroupState state) {
 | 
				
			||||||
 | 
								return state with {
 | 
				
			||||||
 | 
									ShowCreateGroupForm = false,
 | 
				
			||||||
 | 
									IsLoadingCreateGroup = false,
 | 
				
			||||||
 | 
									CreateGroupErrorMessage = null,
 | 
				
			||||||
 | 
									ContainerId = 0,
 | 
				
			||||||
 | 
									ContainerTitle = ""
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							[ReducerMethod(typeof(FetchCreateGroupAction))]
 | 
				
			||||||
 | 
							public static CreateGroupState FetchCreateGroup(CreateGroupState state) {
 | 
				
			||||||
 | 
								return state with {
 | 
				
			||||||
 | 
									IsLoadingCreateGroup = true,
 | 
				
			||||||
 | 
									CreateGroupErrorMessage = null
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							[ReducerMethod(typeof(RecievedCreateGroupAction))]
 | 
				
			||||||
 | 
							public static CreateGroupState RecievedCreateGroup(CreateGroupState state) {
 | 
				
			||||||
 | 
								return state with {
 | 
				
			||||||
 | 
									IsLoadingCreateGroup = false,
 | 
				
			||||||
 | 
									CreateGroupErrorMessage = null
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							[ReducerMethod]
 | 
				
			||||||
 | 
							public static CreateGroupState ErrorFetchingCreateGroup(CreateGroupState state,
 | 
				
			||||||
 | 
								ErrorFetchingCreateGroupAction action) {
 | 
				
			||||||
 | 
								return state with {
 | 
				
			||||||
 | 
									CreateGroupErrorMessage = action.ErrorMessage,
 | 
				
			||||||
 | 
									IsLoadingCreateGroup = false
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										25
									
								
								Start/Client/Store/Features/CreateGroup/CreateGroupState.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								Start/Client/Store/Features/CreateGroup/CreateGroupState.cs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,25 @@
 | 
				
			||||||
 | 
					using Start.Client.Store.State;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Start.Client.Store.Features.CreateGroup {
 | 
				
			||||||
 | 
						public record CreateGroupState : RootState {
 | 
				
			||||||
 | 
							public bool ShowCreateGroupForm { get; init; }
 | 
				
			||||||
 | 
							public int ContainerId { get; init; }
 | 
				
			||||||
 | 
							public string ContainerTitle { get; init; }
 | 
				
			||||||
 | 
							public bool IsLoadingCreateGroup { get; init; }
 | 
				
			||||||
 | 
							public string? CreateGroupErrorMessage { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public CreateGroupState() {
 | 
				
			||||||
 | 
								this.ContainerTitle = "";
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public CreateGroupState(ContainerListState containerList,
 | 
				
			||||||
 | 
								CurrentContainerState currentContainer, bool showCreateGroupForm, string containerTitle,
 | 
				
			||||||
 | 
								bool isLoadingCreateGroup, string? createGroupErrorMessage)
 | 
				
			||||||
 | 
								: base(containerList, currentContainer) {
 | 
				
			||||||
 | 
								this.ShowCreateGroupForm = showCreateGroupForm;
 | 
				
			||||||
 | 
								this.ContainerTitle = containerTitle;
 | 
				
			||||||
 | 
								this.IsLoadingCreateGroup = isLoadingCreateGroup;
 | 
				
			||||||
 | 
								this.CreateGroupErrorMessage = createGroupErrorMessage;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -28,4 +28,20 @@ namespace Start.Client.Store.Features.CurrentContainer {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public class FixCurrentContainerAction { }
 | 
						public class FixCurrentContainerAction { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public class AddBookmarkGroupAction {
 | 
				
			||||||
 | 
							public BookmarkGroupDto BookmarkGroup { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public AddBookmarkGroupAction(BookmarkGroupDto bookmarkGroup) {
 | 
				
			||||||
 | 
								this.BookmarkGroup = bookmarkGroup;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public class RemoveBookmarkGroupAction {
 | 
				
			||||||
 | 
							public int BookmarkGroupId { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public RemoveBookmarkGroupAction(int bookmarkGroupId) {
 | 
				
			||||||
 | 
								this.BookmarkGroupId = bookmarkGroupId;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,7 @@ using Start.Client.Store.State;
 | 
				
			||||||
using Start.Client.Store.Features.CreateContainer;
 | 
					using Start.Client.Store.Features.CreateContainer;
 | 
				
			||||||
using Start.Shared;
 | 
					using Start.Shared;
 | 
				
			||||||
using Start.Shared.Api;
 | 
					using Start.Shared.Api;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Start.Client.Store.Features.CurrentContainer {
 | 
					namespace Start.Client.Store.Features.CurrentContainer {
 | 
				
			||||||
	public class CurrentContainerEffects {
 | 
						public class CurrentContainerEffects {
 | 
				
			||||||
| 
						 | 
					@ -35,11 +36,17 @@ namespace Start.Client.Store.Features.CurrentContainer {
 | 
				
			||||||
				BookmarkContainerDto? container = response.Content;
 | 
									BookmarkContainerDto? container = response.Content;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (container == null) {
 | 
									if (container == null) {
 | 
				
			||||||
 | 
										Console.WriteLine("Error fetching container " + action.BookmarkContainerId);
 | 
				
			||||||
 | 
										Console.WriteLine(response);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					dispatch.Dispatch(new ErrorFetchingCurrentContainerAction(
 | 
										dispatch.Dispatch(new ErrorFetchingCurrentContainerAction(
 | 
				
			||||||
						"Failed to get current bookmark container"));
 | 
											"Failed to get current bookmark container"));
 | 
				
			||||||
					return;
 | 
										return;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									Console.WriteLine("Recieved container " + action.BookmarkContainerId);
 | 
				
			||||||
 | 
									Console.WriteLine(response);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				dispatch.Dispatch(new ReceivedCurrentContainerAction(container));
 | 
									dispatch.Dispatch(new ReceivedCurrentContainerAction(container));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				await this.LocalStorage
 | 
									await this.LocalStorage
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,8 @@
 | 
				
			||||||
using Fluxor;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using Fluxor;
 | 
				
			||||||
using Start.Client.Store.State;
 | 
					using Start.Client.Store.State;
 | 
				
			||||||
 | 
					using Start.Shared;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Start.Client.Store.Features.CurrentContainer {
 | 
					namespace Start.Client.Store.Features.CurrentContainer {
 | 
				
			||||||
	public static class CurrentContainerReducers {
 | 
						public static class CurrentContainerReducers {
 | 
				
			||||||
| 
						 | 
					@ -37,5 +40,43 @@ namespace Start.Client.Store.Features.CurrentContainer {
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							[ReducerMethod]
 | 
				
			||||||
 | 
							public static RootState AddBookmarkGroup(RootState state, AddBookmarkGroupAction action) {
 | 
				
			||||||
 | 
								BookmarkContainerDto? container = state.CurrentContainerState.Container;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (container == null)
 | 
				
			||||||
 | 
									return state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (action.BookmarkGroup.BookmarkContainerId != container.BookmarkContainerId)
 | 
				
			||||||
 | 
									return state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return state with {
 | 
				
			||||||
 | 
									CurrentContainerState = state.CurrentContainerState with {
 | 
				
			||||||
 | 
										Container = new BookmarkContainerDto(container.BookmarkContainerId,
 | 
				
			||||||
 | 
											container.Title, container.BookmarkGroups?
 | 
				
			||||||
 | 
												.Concat(new List<BookmarkGroupDto> { action.BookmarkGroup })
 | 
				
			||||||
 | 
												.ToList())
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							[ReducerMethod]
 | 
				
			||||||
 | 
							public static RootState RemoveBookmarkGroup(RootState state,
 | 
				
			||||||
 | 
								RemoveBookmarkGroupAction action) {
 | 
				
			||||||
 | 
								BookmarkContainerDto? container = state.CurrentContainerState.Container;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (container == null)
 | 
				
			||||||
 | 
									return state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return state with {
 | 
				
			||||||
 | 
									CurrentContainerState = state.CurrentContainerState with {
 | 
				
			||||||
 | 
										Container = new BookmarkContainerDto(container.BookmarkContainerId,
 | 
				
			||||||
 | 
											container.Title, container.BookmarkGroups?
 | 
				
			||||||
 | 
												.Where(g => g.BookmarkGroupId != action.BookmarkGroupId)
 | 
				
			||||||
 | 
												.ToList())
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										41
									
								
								Start/Client/Store/State/StoreInitializedEffects.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								Start/Client/Store/State/StoreInitializedEffects.cs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,41 @@
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Fluxor;
 | 
				
			||||||
 | 
					using Blazored.LocalStorage;
 | 
				
			||||||
 | 
					using Start.Client.Store.Features.ContainersList;
 | 
				
			||||||
 | 
					using Start.Client.Store.Features.CurrentContainer;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Start.Client.Store.State {
 | 
				
			||||||
 | 
						public class StoreInitializedEffects {
 | 
				
			||||||
 | 
							public IState<RootState> State { get; init; }
 | 
				
			||||||
 | 
							public ILocalStorageService LocalStorage { get; init; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public StoreInitializedEffects(IState<RootState> state, ILocalStorageService localStorage) {
 | 
				
			||||||
 | 
								this.State = state;
 | 
				
			||||||
 | 
								this.LocalStorage = localStorage;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							[EffectMethod(typeof(StoreInitializedAction))]
 | 
				
			||||||
 | 
							public async Task InitialLoad(IDispatcher dispatch) {
 | 
				
			||||||
 | 
								dispatch.Dispatch(new LoadContainerListAction());
 | 
				
			||||||
 | 
								dispatch.Dispatch(new LoadCurrentContainerAction(await GetSelectedContainerId()));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private async Task<int> GetSelectedContainerId() {
 | 
				
			||||||
 | 
								bool hasValue = await this.LocalStorage.ContainKeyAsync("SelectedContainer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (hasValue)
 | 
				
			||||||
 | 
									return await this.LocalStorage.GetItemAsync<int>("SelectedContainer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Default to the first container
 | 
				
			||||||
 | 
								int firstContainer = this.State.Value.ContainerListState.Containers
 | 
				
			||||||
 | 
									.First().BookmarkContainerId;
 | 
				
			||||||
 | 
								await this.SetSelectedContainer(firstContainer);
 | 
				
			||||||
 | 
								return firstContainer;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							protected async Task SetSelectedContainer(int selectedContainerId) {
 | 
				
			||||||
 | 
								await this.LocalStorage.SetItemAsync("SelectedContainer", selectedContainerId);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,23 +1,4 @@
 | 
				
			||||||
@import url('open-iconic/font/css/open-iconic-bootstrap.min.css');
 | 
					/* Validation */
 | 
				
			||||||
 | 
					 | 
				
			||||||
html, body {
 | 
					 | 
				
			||||||
	font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
a, .btn-link {
 | 
					 | 
				
			||||||
	color: #0366d6;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.btn-primary {
 | 
					 | 
				
			||||||
	color: #fff;
 | 
					 | 
				
			||||||
	background-color: #1b6ec2;
 | 
					 | 
				
			||||||
	border-color: #1861ac;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.content {
 | 
					 | 
				
			||||||
	padding-top: 1.1rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.valid.modified:not([type=checkbox]) {
 | 
					.valid.modified:not([type=checkbox]) {
 | 
				
			||||||
	outline: 1px solid #26b050;
 | 
						outline: 1px solid #26b050;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -30,6 +11,8 @@ a, .btn-link {
 | 
				
			||||||
	color: red;
 | 
						color: red;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Blazor */
 | 
				
			||||||
#blazor-error-ui {
 | 
					#blazor-error-ui {
 | 
				
			||||||
	background: lightyellow;
 | 
						background: lightyellow;
 | 
				
			||||||
	bottom: 0;
 | 
						bottom: 0;
 | 
				
			||||||
| 
						 | 
					@ -49,10 +32,13 @@ a, .btn-link {
 | 
				
			||||||
		top: 0.5rem;
 | 
							top: 0.5rem;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Spectre's default is no padding */
 | 
				
			||||||
.container {
 | 
					.container {
 | 
				
			||||||
	padding: 0.4rem;
 | 
						padding: 0.4rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* The splash screen */
 | 
				
			||||||
.appLoadingContainer {
 | 
					.appLoadingContainer {
 | 
				
			||||||
	height: 100vh;
 | 
						height: 100vh;
 | 
				
			||||||
	display: flex;
 | 
						display: flex;
 | 
				
			||||||
| 
						 | 
					@ -60,3 +46,73 @@ a, .btn-link {
 | 
				
			||||||
	justify-content: center;
 | 
						justify-content: center;
 | 
				
			||||||
	align-items: center;
 | 
						align-items: center;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#bookmarkGroups {
 | 
				
			||||||
 | 
						margin-top: 1em;
 | 
				
			||||||
 | 
						margin-left: auto;
 | 
				
			||||||
 | 
						margin-right: auto;
 | 
				
			||||||
 | 
						max-width: 1280px;
 | 
				
			||||||
 | 
						display: grid;
 | 
				
			||||||
 | 
						grid-column-gap: 1.25rem;
 | 
				
			||||||
 | 
						grid-row-gap: 1.25rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@media screen and (max-width: 960px) {
 | 
				
			||||||
 | 
						#bookmarkGroups {
 | 
				
			||||||
 | 
							grid-template-columns: repeat(1, 1fr);
 | 
				
			||||||
 | 
							padding: 1em;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@media screen and (min-width: 960px) {
 | 
				
			||||||
 | 
						#bookmarkGroups {
 | 
				
			||||||
 | 
							grid-template-columns: repeat(2, 1fr);
 | 
				
			||||||
 | 
							width: 70%;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@media screen and (min-width: 1280px) {
 | 
				
			||||||
 | 
						#bookmarkGroups {
 | 
				
			||||||
 | 
							grid-template-columns: repeat(3, 1fr);
 | 
				
			||||||
 | 
							width: 70%;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.bookmarkGroup {
 | 
				
			||||||
 | 
						width: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.bookmarkGroup .card-header {
 | 
				
			||||||
 | 
						padding-top: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.bookmarkGroup .card-title {
 | 
				
			||||||
 | 
						margin-top: 0.5em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.bookmarkGroup .card-body {
 | 
				
			||||||
 | 
						padding: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.bookmarkGroup .card-body:last-child {
 | 
				
			||||||
 | 
						padding: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ul.bookmarks {
 | 
				
			||||||
 | 
						list-style: none;
 | 
				
			||||||
 | 
						margin: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					li.noBookmarksItem {
 | 
				
			||||||
 | 
						margin-top: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.addBookmarkGroupButton button {
 | 
				
			||||||
 | 
						width: 100%;
 | 
				
			||||||
 | 
						margin-top: 0.5em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					button.addBookmarkButton {
 | 
				
			||||||
 | 
						float: right;
 | 
				
			||||||
 | 
						margin-top: 0.5em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										67
									
								
								Start/Server/Controllers/BookmarkGroupsController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								Start/Server/Controllers/BookmarkGroupsController.cs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,67 @@
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Authorization;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
 | 
					using Start.Server.Data.Services.Interfaces;
 | 
				
			||||||
 | 
					using Start.Server.Models;
 | 
				
			||||||
 | 
					using Start.Server.Extensions;
 | 
				
			||||||
 | 
					using Start.Shared;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Start.Server.Controllers {
 | 
				
			||||||
 | 
						[Authorize]
 | 
				
			||||||
 | 
						[ApiController]
 | 
				
			||||||
 | 
						[Route("[controller]")]
 | 
				
			||||||
 | 
						public class BookmarkGroupsController : ControllerBase {
 | 
				
			||||||
 | 
							private readonly IBookmarkGroupService bookmarkGroupService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public BookmarkGroupsController(IBookmarkGroupService bookmarkGroupService) {
 | 
				
			||||||
 | 
								this.bookmarkGroupService = bookmarkGroupService;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							[HttpGet]
 | 
				
			||||||
 | 
							[Route("{bookmarkGroupId}")]
 | 
				
			||||||
 | 
							[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BookmarkGroupDto))]
 | 
				
			||||||
 | 
							[ProducesResponseType(StatusCodes.Status404NotFound)]
 | 
				
			||||||
 | 
							public async Task<IActionResult> GetBookmarkGroup(int bookmarkGroupId) {
 | 
				
			||||||
 | 
								BookmarkGroup? group = await this.bookmarkGroupService
 | 
				
			||||||
 | 
									.GetBookmarkGroup(this.GetAuthorizedUserId(), bookmarkGroupId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (group == null)
 | 
				
			||||||
 | 
									return NotFound();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return Ok(group.MapToDto());
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							[HttpPost]
 | 
				
			||||||
 | 
							[Route("Create")]
 | 
				
			||||||
 | 
							[ProducesResponseType(StatusCodes.Status201Created, Type = typeof(BookmarkGroupDto))]
 | 
				
			||||||
 | 
							[ProducesResponseType(StatusCodes.Status400BadRequest)]
 | 
				
			||||||
 | 
							public async Task<IActionResult> CreateBookmarkGroup(string title, string color,
 | 
				
			||||||
 | 
								int bookmarkContainerId) {
 | 
				
			||||||
 | 
								BookmarkGroup? newGroup = await this.bookmarkGroupService
 | 
				
			||||||
 | 
									.CreateBookmarkGroup(this.GetAuthorizedUserId(), title, color, bookmarkContainerId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (newGroup == null)
 | 
				
			||||||
 | 
									return BadRequest();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return Created(
 | 
				
			||||||
 | 
									Url.Action(nameof(GetBookmarkGroup),
 | 
				
			||||||
 | 
										new { bookmarkGroupId = newGroup.BookmarkGroupId }),
 | 
				
			||||||
 | 
									newGroup.MapToDto());
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							[HttpDelete]
 | 
				
			||||||
 | 
							[Route("Delete/{bookmarkGroupId}")]
 | 
				
			||||||
 | 
							[ProducesResponseType(StatusCodes.Status200OK)]
 | 
				
			||||||
 | 
							[ProducesResponseType(StatusCodes.Status404NotFound)]
 | 
				
			||||||
 | 
							public async Task<IActionResult> DeleteBookmark(int bookmarkGroupId) {
 | 
				
			||||||
 | 
								bool res = await this.bookmarkGroupService
 | 
				
			||||||
 | 
									.DeleteBookmarkGroup(this.GetAuthorizedUserId(), bookmarkGroupId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (!res)
 | 
				
			||||||
 | 
									return NotFound();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return Ok();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -11,12 +11,9 @@ namespace Start.Server.Controllers {
 | 
				
			||||||
	[ApiController]
 | 
						[ApiController]
 | 
				
			||||||
	[Route("[controller]")]
 | 
						[Route("[controller]")]
 | 
				
			||||||
	public class BookmarksController : ControllerBase {
 | 
						public class BookmarksController : ControllerBase {
 | 
				
			||||||
		private readonly IBookmarkGroupService bookmarkGroupService;
 | 
					 | 
				
			||||||
		private readonly IBookmarkService bookmarkService;
 | 
							private readonly IBookmarkService bookmarkService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public BookmarksController(IBookmarkGroupService bookmarkGroupService,
 | 
							public BookmarksController(IBookmarkService bookmarkService) {
 | 
				
			||||||
			IBookmarkService bookmarkService) {
 | 
					 | 
				
			||||||
			this.bookmarkGroupService = bookmarkGroupService;
 | 
					 | 
				
			||||||
			this.bookmarkService = bookmarkService;
 | 
								this.bookmarkService = bookmarkService;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,4 @@
 | 
				
			||||||
using System;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Linq;
 | 
					 | 
				
			||||||
using Start.Server.Models;
 | 
					using Start.Server.Models;
 | 
				
			||||||
using Start.Shared;
 | 
					using Start.Shared;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,7 +11,8 @@ namespace Start.Server.Extensions {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public static BookmarkGroupDto MapToDto(this BookmarkGroup bookmarkGroup) {
 | 
							public static BookmarkGroupDto MapToDto(this BookmarkGroup bookmarkGroup) {
 | 
				
			||||||
			return new BookmarkGroupDto(bookmarkGroup.BookmarkGroupId, bookmarkGroup.Title,
 | 
								return new BookmarkGroupDto(bookmarkGroup.BookmarkGroupId, bookmarkGroup.Title,
 | 
				
			||||||
				bookmarkGroup.Color, bookmarkGroup.Bookmarks?.Select(b => b.MapToDto()).ToList());
 | 
									bookmarkGroup.Color, bookmarkGroup.BookmarkContainerId,
 | 
				
			||||||
 | 
									bookmarkGroup.Bookmarks?.Select(b => b.MapToDto()).ToList());
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public static BookmarkContainerDto MapToDto(this BookmarkContainer bookmarkContainer) {
 | 
							public static BookmarkContainerDto MapToDto(this BookmarkContainer bookmarkContainer) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										17
									
								
								Start/Shared/Api/IBookmarkGroupsApi.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								Start/Shared/Api/IBookmarkGroupsApi.cs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,17 @@
 | 
				
			||||||
 | 
					using System.Net.Http;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Refit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Start.Shared.Api {
 | 
				
			||||||
 | 
						public interface IBookmarkGroupsApi {
 | 
				
			||||||
 | 
							[Get("/{bookmarkGroupId}")]
 | 
				
			||||||
 | 
							Task<ApiResponse<BookmarkGroupDto?>> GetBookmarkGroup(int bookmarkGroupId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							[Post("/Create")]
 | 
				
			||||||
 | 
							Task<ApiResponse<BookmarkGroupDto?>> CreateBookmarkGroup(string title, string color,
 | 
				
			||||||
 | 
								int bookmarkContainerId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							[Delete("/Delete/{bookmarkGroupId}")]
 | 
				
			||||||
 | 
							Task<HttpResponseMessage> DeleteBookmarkGroup(int bookmarkGroupId);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,15 +1,17 @@
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Refit;
 | 
					using Refit;
 | 
				
			||||||
 | 
					using System.Net.Http;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Start.Shared.Api {
 | 
					namespace Start.Shared.Api {
 | 
				
			||||||
	public interface IBookmarksApi {
 | 
						public interface IBookmarksApi {
 | 
				
			||||||
		[Get("{bookmarkId}")]
 | 
							[Get("{bookmarkId}")]
 | 
				
			||||||
		Task<BookmarkDto?> GetBookmark(int bookmarkId);
 | 
							Task<ApiResponse<BookmarkDto?>> GetBookmark(int bookmarkId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[Post("/Create")]
 | 
							[Post("/Create")]
 | 
				
			||||||
		Task CreateBookmark(string title, string url, string? notes, int bookmarkGroupId);
 | 
							Task<ApiResponse<BookmarkDto?>> CreateBookmark(string title, string url, string? notes,
 | 
				
			||||||
 | 
								int bookmarkGroupId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[Delete("/Delete/{bookmarkId}")]
 | 
							[Delete("/Delete/{bookmarkId}")]
 | 
				
			||||||
		Task DeleteBookmark(int bookmarkId);
 | 
							Task<HttpResponseMessage> DeleteBookmark(int bookmarkId);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,4 @@
 | 
				
			||||||
using System;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					 | 
				
			||||||
using System.ComponentModel.DataAnnotations;
 | 
					using System.ComponentModel.DataAnnotations;
 | 
				
			||||||
using System.Text.Json.Serialization;
 | 
					using System.Text.Json.Serialization;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,29 +1,36 @@
 | 
				
			||||||
using System;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					 | 
				
			||||||
using System.ComponentModel.DataAnnotations;
 | 
					using System.ComponentModel.DataAnnotations;
 | 
				
			||||||
 | 
					using System.Text.Json.Serialization;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Start.Shared {
 | 
					namespace Start.Shared {
 | 
				
			||||||
	public class BookmarkGroupDto {
 | 
						public class BookmarkGroupDto {
 | 
				
			||||||
		public int BookmarkGroupId { get; set; }
 | 
							public int BookmarkGroupId { get; set; }
 | 
				
			||||||
 | 
							[Required(AllowEmptyStrings = false, ErrorMessage = "Title is required")]
 | 
				
			||||||
		[StringLength(300)]
 | 
							[StringLength(300)]
 | 
				
			||||||
		public string Title { get; set; }
 | 
							public string Title { get; set; }
 | 
				
			||||||
		[StringLength(6)]
 | 
							[Required(AllowEmptyStrings = false, ErrorMessage = "Color is required")]
 | 
				
			||||||
 | 
							[StringLength(7)]
 | 
				
			||||||
		public string Color { get; set; }
 | 
							public string Color { get; set; }
 | 
				
			||||||
 | 
							public int BookmarkContainerId { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public IList<BookmarkDto>? Bookmarks { get; set; }
 | 
							public IList<BookmarkDto>? Bookmarks { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public BookmarkGroupDto(string title, string color) {
 | 
							public BookmarkGroupDto(string title, string color, int bookmarkContainerId) {
 | 
				
			||||||
			this.Title = title;
 | 
								this.Title = title;
 | 
				
			||||||
			this.Color = color;
 | 
								this.Color = color;
 | 
				
			||||||
		}
 | 
								this.BookmarkContainerId = bookmarkContainerId;
 | 
				
			||||||
 | 
					 | 
				
			||||||
		public BookmarkGroupDto(int bookmarkGroupId, string title, string color)
 | 
					 | 
				
			||||||
			: this(title, color) {
 | 
					 | 
				
			||||||
			this.BookmarkGroupId = bookmarkGroupId;
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public BookmarkGroupDto(int bookmarkGroupId, string title, string color,
 | 
							public BookmarkGroupDto(int bookmarkGroupId, string title, string color,
 | 
				
			||||||
			IList<BookmarkDto>? bookmarks) : this(bookmarkGroupId, title, color) {
 | 
								int bookmarkContainerId)
 | 
				
			||||||
 | 
								: this(title, color, bookmarkContainerId) {
 | 
				
			||||||
 | 
								this.BookmarkGroupId = bookmarkGroupId;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							[JsonConstructor]
 | 
				
			||||||
 | 
							public BookmarkGroupDto(int bookmarkGroupId, string title, string color,
 | 
				
			||||||
 | 
								int bookmarkContainerId, IList<BookmarkDto>? bookmarks)
 | 
				
			||||||
 | 
								: this(bookmarkGroupId, title, color, bookmarkContainerId) {
 | 
				
			||||||
			this.Bookmarks = bookmarks;
 | 
								this.Bookmarks = bookmarks;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue