Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4bd7a9e
Initial scaffold
markashleybell Nov 12, 2023
05b02be
Start playing with basic Vue concepts
markashleybell Nov 12, 2023
8bc3466
Temporarily enable CORS so we can have a separate back end
markashleybell Nov 12, 2023
cc0d331
Dynamic data load
markashleybell Nov 12, 2023
030efdb
Update title
markashleybell Nov 12, 2023
63b73a1
Target .NET 8, upgrade NuGet dependencies
markashleybell Dec 9, 2023
c9ee978
Wire up track search
markashleybell Dec 9, 2023
acb89b5
Wire up basic track play
markashleybell Dec 9, 2023
cd15a2c
Cleanup and formatting
markashleybell Dec 9, 2023
99ba3b3
Upgrade all client-side dependencies
markashleybell Dec 9, 2023
cafb80d
Fix Vite warning
markashleybell Dec 9, 2023
c9a9a3e
Clean reinstall of all packages
markashleybell Dec 10, 2023
427c025
Install primeflex (CSS utils) and primeicons
markashleybell Dec 10, 2023
f6734ea
Component for track search inputs
markashleybell Dec 10, 2023
bfd9a72
Search icon
markashleybell Dec 10, 2023
7deccef
Implement multiple track selection
markashleybell Dec 16, 2023
24bf89d
Cleanup
markashleybell Dec 16, 2023
6e6145f
Wire up initial playlist UI, tweak styles so it's usable
markashleybell Dec 16, 2023
4edf54d
Fix inconsistent row heights when track list doesn't require scrolling
markashleybell Dec 27, 2023
d772a33
Variable button icon
markashleybell Dec 27, 2023
ac82a69
Wire up playlist remove
markashleybell Dec 27, 2023
c7c53bd
Wire up controls, sequencing and shuffle
markashleybell Dec 27, 2023
ccb8508
Cleanup
markashleybell Dec 27, 2023
1415a24
Why is height still an issue in 2023... this sort of works, so I give…
markashleybell Dec 27, 2023
6feb99d
Hide playlist when not in use
markashleybell Dec 27, 2023
473a2f3
Start wiring up playlist save
markashleybell Jan 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/app/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"editor.inlayHints.enabled": "offUnlessPressed"
}
4 changes: 4 additions & 0 deletions src/app/Domain.fs
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ type TrackSummary = {
TrackNumber: int option
Title: string
Url: string }

[<CLIMutable>]
type Playlist = {
Tracks: TrackSummary list }
50 changes: 45 additions & 5 deletions src/app/Main.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ open jook.Data
open Microsoft.Extensions.Configuration
open Microsoft.Extensions.Logging
open System
open System.Linq
open System.Data
open Microsoft.Data.SqlClient
open System.Text.Json
open Microsoft.AspNetCore.Cors.Infrastructure

let env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")

Expand All @@ -18,6 +20,22 @@ let config = configuration [||] {
optional_json $"appsettings.{env}.json"
}

let corsPolicyName = "local"

let corsPolicy (policyBuilder: CorsPolicyBuilder) =
// Note: This is a very lax setting, but a good fit for local development
policyBuilder
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
// Note: The URLs must not end with a /
.WithOrigins("https://localhost:5001",
"http://localhost:5173")
|> ignore

let corsOptions (options : CorsOptions) =
options.AddPolicy(corsPolicyName, corsPolicy)

let jsonOptions = JsonSerializerOptions()

jsonOptions.PropertyNamingPolicy <- JsonNamingPolicy.CamelCase
Expand All @@ -29,29 +47,51 @@ type ApplicationLogger = class end
let getDatabaseConnection () : IDbConnection =
new SqlConnection(connectionString) :> IDbConnection

let withServices getData handler : HttpHandler = fun ctx ->
let withServicesGet handler : HttpHandler = fun ctx ->
let logger = ctx.GetService<ILogger<ApplicationLogger>>()
let data = getData ctx
let data = Request.getQuery ctx
handler logger getDatabaseConnection data ctx

let withServicesPost handler : HttpHandler = fun ctx ->
let logger = ctx.GetService<ILogger<ApplicationLogger>>()
task {
let! body = Request.getJsonOptions jsonOptions ctx
return handler logger getDatabaseConnection body ctx
}

let get route handler =
get route (withServices Request.getQuery handler)
get route (withServicesGet handler)

let post route handler =
post route (withServicesPost handler)

[<EntryPoint>]
let main args =
webHost args {
use_default_files
use_static_files
use_cors corsPolicyName corsOptions
endpoints [
get "/tracks" (fun _ cf q ->
let title = q.TryGet ("title")
let artist = q.TryGet ("artist")
let album = q.TryGet ("album")
let genre = q.TryGet ("genre")

let data = {| tracks = trackList cf title artist album genre |}

let tracks = trackList cf title artist album genre

let meta = {|
count = tracks.Count()
|}

let data = {|
meta = meta
tracks = tracks |}

Response.ofJsonOptions jsonOptions data)
post "/playlist" (fun _ cf body ->
// TODO: Persist the playlist tracks and title etc
Response.ofJsonOptions jsonOptions body)
]
}
0
6 changes: 3 additions & 3 deletions src/app/app.fsproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup>
<ItemGroup>
Expand All @@ -16,8 +16,8 @@
<Content Update="tsconfig.json" CopyToPublishDirectory="Never" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="dapper" Version="2.0.123" />
<PackageReference Include="dapper" Version="2.1.24" />
<PackageReference Include="Falco" Version="4.*" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.1.1" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.1.2" />
</ItemGroup>
</Project>
30 changes: 15 additions & 15 deletions src/app/packages.lock.json
Original file line number Diff line number Diff line change
@@ -1,37 +1,37 @@
{
"version": 1,
"dependencies": {
"net6.0": {
"net8.0": {
"Dapper": {
"type": "Direct",
"requested": "[2.0.123, )",
"resolved": "2.0.123",
"contentHash": "RDFF4rBLLmbpi6pwkY7q/M6UXHRJEOerplDGE5jwEkP/JGJnBauAClYavNKJPW1yOTWRPIyfj4is3EaJxQXILQ=="
"requested": "[2.1.24, )",
"resolved": "2.1.24",
"contentHash": "/2t2vsdJyZRsk13AsWigZpsuFvEwK+o3v862cEULXoww905gyKhJFSuwmZI/4Ui9COX9ZCFCI09UHyH4wVYl3A=="
},
"Falco": {
"type": "Direct",
"requested": "[4.*, )",
"resolved": "4.0.4",
"contentHash": "YUjF1ZA6z5XxI6gwU2yZm3pPeRfeDWs/bog5+EJo31YHxd4niGntKlbdUECikee38tqez+SjJNrfhckqBUp+jw==",
"resolved": "4.0.5",
"contentHash": "M0x2oIgZULM6chkeKbpXNMVakcwxtqvWApPUoARdVvbDgbticjNOtlNAgESSATiHAy0ij3Cq5OGl6QL+gS1XXA==",
"dependencies": {
"FSharp.Core": "6.0.0",
"Falco.Markup": "1.0.2"
}
},
"FSharp.Core": {
"type": "Direct",
"requested": "[7.0.400, )",
"resolved": "7.0.400",
"contentHash": "SZR1jcjcrdhjRObN/U16nI+RyvKBgg0zq8Pt2KAorDkvW0cXSjX4qKDNX+LpZaKZRA5QR9StHZ2mhT5VI/QSxw=="
"requested": "[8.0.100, )",
"resolved": "8.0.100",
"contentHash": "ZOVZ/o+jI3ormTZOa28Wh0tSRoyle1f7lKFcUN61sPiXI7eDZu8eSveFybgTeyIEyW0ujjp31cp7GOglDgsNEg=="
},
"Microsoft.Data.SqlClient": {
"type": "Direct",
"requested": "[5.1.1, )",
"resolved": "5.1.1",
"contentHash": "MW5E9HFvCaV069o8b6YpuRDPBux8s96qDnOJ+4N9QNUCs7c5W3KxwQ+ftpAjbMUlImL+c9WR+l+f5hzjkqhu2g==",
"requested": "[5.1.2, )",
"resolved": "5.1.2",
"contentHash": "q/F1HTOn9QLwgRp4esJIA1b2X15faeV8WozkNhvU3Zk0DcYDWUsEf5WMkbypY4CaNkZntOduods5wLyv8I699w==",
"dependencies": {
"Azure.Identity": "1.7.0",
"Microsoft.Data.SqlClient.SNI.runtime": "5.1.0",
"Microsoft.Data.SqlClient.SNI.runtime": "5.1.1",
"Microsoft.Identity.Client": "4.47.2",
"Microsoft.IdentityModel.JsonWebTokens": "6.24.0",
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.24.0",
Expand Down Expand Up @@ -93,8 +93,8 @@
},
"Microsoft.Data.SqlClient.SNI.runtime": {
"type": "Transitive",
"resolved": "5.1.0",
"contentHash": "jVsElisM5sfBzaaV9kdq2NXZLwIbytetnsOIlJ0cQGgQP4zFNBmkfHBnpwtmKrtBJBEV9+9PVQPVrcCVhDgcIg=="
"resolved": "5.1.1",
"contentHash": "wNGM5ZTQCa2blc9ikXQouybGiyMd6IHPVJvAlBEPtr6JepZEOYeDxGyprYvFVeOxlCXs7avridZQ0nYkHzQWCQ=="
},
"Microsoft.Identity.Client": {
"type": "Transitive",
Expand Down
15 changes: 15 additions & 0 deletions src/client/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')

module.exports = {
root: true,
'extends': [
'plugin:vue/vue3-essential',
'eslint:recommended',
'@vue/eslint-config-typescript',
'@vue/eslint-config-prettier/skip-formatting'
],
parserOptions: {
ecmaVersion: 'latest'
}
}
31 changes: 31 additions & 0 deletions src/client/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
.DS_Store
dist
dist-ssr
coverage
*.local

/cypress/videos/
/cypress/screenshots/

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

test-results/
playwright-report/
8 changes: 8 additions & 0 deletions src/client/.prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"$schema": "https://json.schemastore.org/prettierrc",
"semi": false,
"tabWidth": 4,
"singleQuote": true,
"printWidth": 120,
"trailingComma": "none"
}
9 changes: 9 additions & 0 deletions src/client/.vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"recommendations": [
"Vue.volar",
"Vue.vscode-typescript-vue-plugin",
"ms-playwright.playwright",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
}
71 changes: 71 additions & 0 deletions src/client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# client

This template should help get you started developing with Vue 3 in Vite.

## Recommended IDE Setup

[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).

## Type Support for `.vue` Imports in TS

TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.

If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:

1. Disable the built-in TypeScript Extension
1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette
2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.

## Customize configuration

See [Vite Configuration Reference](https://vitejs.dev/config/).

## Project Setup

```sh
npm install
```

### Compile and Hot-Reload for Development

```sh
npm run dev
```

### Type-Check, Compile and Minify for Production

```sh
npm run build
```

### Run Unit Tests with [Vitest](https://vitest.dev/)

```sh
npm run test:unit
```

### Run End-to-End Tests with [Playwright](https://playwright.dev)

```sh
# Install browsers for the first run
npx playwright install

# When testing on CI, must build the project first
npm run build

# Runs the end-to-end tests
npm run test:e2e
# Runs the tests only on Chromium
npm run test:e2e -- --project=chromium
# Runs the tests of a specific file
npm run test:e2e -- tests/example.spec.ts
# Runs the tests in debug mode
npm run test:e2e -- --debug
```

### Lint with [ESLint](https://eslint.org/)

```sh
npm run lint
```
4 changes: 4 additions & 0 deletions src/client/e2e/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "@tsconfig/node18/tsconfig.json",
"include": ["./**/*"]
}
8 changes: 8 additions & 0 deletions src/client/e2e/vue.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { test, expect } from '@playwright/test';

// See here how to get started:
// https://playwright.dev/docs/intro
test('visits the app root url', async ({ page }) => {
await page.goto('/');
await expect(page.locator('div.greetings > h1')).toHaveText('You did it!');
})
1 change: 1 addition & 0 deletions src/client/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="vite/client" />
13 changes: 13 additions & 0 deletions src/client/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jook (Vue)</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
Loading